-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Storing Secret Keys in Android
Often your app will have secret credentials or API keys that you need to have in your app to function but you'd rather not have easily extracted from your app. If you are using dynamically generated secrets, the most effective way to store this information is to use the Android Keystore API.
You should not store them in shared preferences without encrypting this data first because they can be extracted when performing a backup of your data. The alternative is to disable backups by setting android:allowBackup
in your AndroidManifest.xml
file. You can also specify which files to avoid backups too by reviewing this doc.
<application ...
android:allowBackup="true">
</application>
To understand the Android Keystore API, you must first understand that encrypting secrets requires both public key and symmetric cryptography. In public key cryptography, data can be encrypted with one key and decrypted with the other key. In symmetric cryptography, the same key is used to encrypt and decrypt the data. The Keystore API uses both types of cryptography in order to safeguard secrets.
A public/private RSA key pair is generated, which is stored in the Android device's keystore. An AES symmetric key is also generated, which is used to encrypt and decrypt the secrets. The app needs to store this AES symmetric key to later decode, so it is encrypted by the RSA public key first before persisted. When the app runs, it gives this encrypted AES key to the Keystore API, which will decode the data using its private RSA key.
Read this Medium blog for more information about how to use the Keystore API. Do not use the Qlassified Android library because it introduces an additional 20K methods to your Android program. You can use the Android Vault library instead of needing to write the boilerplate code yourself.
For storing fixed API keys, the following common strategies exist for storing secrets in your source code:
- Embedded in resource file
- Hidden as constants in source code
- Hidden in BuildConfigs
- Obfuscating with Proguard
- Disguised or Encrypted Strings
- Hidden in Native Libraries with NDK
However, none of these strategies will ensure the protection of your keys and your secrets aren't safe. The best way to protect secrets is to never reveal them in the code in the first place. Compartmentalizing sensitive information and operations on your own backend server/service should always be your first choice. If you do have to consider a hiding scheme, you should do so with the realization that you can only make the reverse engineering process harder and may add significant complication to the development, testing, and maintenance of your app in doing so.
The simplest and most straightforward approach is outlined below which is to store your secrets within a resource file. Keep in mind that most of the other more complex approaches above are at best only marginally more secure.
The simplest approach for storing secrets in to keep them as resource files that are simply not checked into source control. Start by creating a resource file for your secrets called res/values/secrets.xml
with:
<!-- Inside of `res/values/secrets.xml` -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="parse_application_id">xxxxxx</string>
<string name="parse_client_secret">yyyyyy</string>
<string name="google_maps_api_key">zzzzzz</string>
</resources>
Once these keys are in the file, Android will automatically merge it into your resources, where you can access them exactly as you would your normal strings. You can access the secret values with:
Parse.initialize(
this,
getString(R.string.parse_application_id),
getString(R.string.parse_client_secret)
);
If you need your keys in another XML file such as in AndroidManifest.xml
, you can just use the XML notation for accessing string resources:
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="@string/google_maps_api_key"/>
Since your secrets are now in an individual file, they're simple to ignore in your source control system (for example, in Git, you would add this to the '.gitignore' file in your repository):
echo "**/*/res/values/secrets.xml" > .gitignore
This process is not bulletproof. As resources, they are somewhat more vulnerable to decompilation of your application package, and so they are discoverable if somebody really wants to know them. This solution does, however, prevent your secrets just sitting in plaintext in source control waiting for someone to use, and also has the advantage of being simple to use, leveraging Android's resource management system, and requiring no extra libraries.
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.