- Storing Secret Keys in Android
- Securing API Keys in Android App using NDK (Native Development Kit)
- Requirements
- The Android Native Development Kit (NDK):
- CMake:
- Two ways for using NDK: ndk-build, CMake
- — — CMake:
- — — ndk-build:
- CMake or ndk-build .
- ***Securing Keys using ndk-build…(Item-01)
- ***Securing Keys using CMake…(item-02)
- ***** The easiest and best way*****
- Android Keystore System
- In this document
- Blog articles
- Unifying Key Store Access in ICS
- Security Features
- Extraction Prevention
- Key Use Authorizations
- Choosing Between a Keychain or the Android Keystore Provider
- Using Android Keystore Provider
- Generating a New Private Key
- Generating a New Secret Key
- Working with Keystore Entries
- Listing Entries
- Signing and Verifying Data
- Requiring User Authentication For Key Use
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.
For storing fixed API keys, the following common strategies exist for storing secrets in your source code:
The simplest approach for storing secrets in to keep them as resource files that are simply not checked into source control. The approaches below two ways of accomplishing the same goal.
First, create a file apikey.properties in your root directory with the values for different secret keys:
Double quotes are required.
To avoid these keys showing up in your repository, make sure to exclude the file from being checked in by adding to your .gitignore file:
Next, add this section to read from this file in your app/build.gradle file. You’ll also create compile-time options that will be generated from this file by using the buildConfigField definition:
You can now access these two fields anywhere within your source code with the BuildConfig object provided by Gradle:
Now you have access to as many secret values as you need within your app, but will avoid checking in the actual values into your git repository. To read more about this approach, check out this article or this other article.
Start by creating a resource file for your secrets called res/values/secrets.xml with a string pair per secret value:
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 in your Java code with:
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:
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) by entering this on the command line within your project git repository:
Verification: To make sure this worked, check the .gitignore file within your git repository, and make sure that this line referencing secrets.xml exists. Now, go to commit files to Git and make sure that you do not see the secrets.xml file in the staging area. You do not want to commit this file to Git.
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.
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. Check out this excellent StackOverflow post for a detailed breakdown of the obfuscation options available.
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.
Another way to make your keys hard to reverse engineer is to save them in the NDK. A recommanded implementation as done in hidden-secrets-gradle-plugin :
- secret is obfuscated using the reversible XOR operator so it never appears in plain sight,
- obfuscated secret is stored in a NDK binary as an hexadecimal array, so it is really hard to spot / put together from a disassembly,
- the obfuscating string is not persisted in the binary to force runtime evaluation (ie : prevent the compiler from disclosing the secret by optimizing the de-obfuscation logic),
- optionnaly, anyone can provide it’s own encoding / decoding algorithm when using the plugin to add an additional security layer.
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 key RSA pair is generated, which is stored in the Android device’s keystore and protected usually by the device PIN. An AES-based 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. In this way, data cannot be decoded without the use of the device keystore.
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, which will also help facilitate the rotation of RSA keys, which usually have an expiration date of 1-5 years.
See also the official Google sample for using the Android Keystore.
Источник
Securing API Keys in Android App using NDK (Native Development Kit)
Securing API keys in Android application has become a challenging by traditional methods (storing them in strings.xml or in Gradle etc.), since they can be easily exposed by reverse engineering the app. It may lead to privacy issues and also may affect the billing for third party paid API access.
Though its not possible to fully protect this API keys from reverse engineering but we can make extra laye r for security by using NDK in our app.
The NDK (Native Development Kit) allows to write code in C/C++ in our app. And the good news is that ndk libraries can not be decompiled easily which make the APIs harder to find.
Today, We are going to store API keys in the native C/C++ class and accessing them in our Java classes. Now Let’s started:
Requirements
To compile and debug native code for our app, we need the following components:
The Android Native Development Kit (NDK):
a set of tools that allows to use C and C++ code with Android.
CMake:
an external build tool that works alongside Gradle to build your native library. You do not need this component if you only plan to use ndk-build.
the debugger Android Studio uses to debug native code.
You can install these components using the SDK Manager:
- From an open project, select Tools > Android > SDK Manager from the menu bar.
- Click the SDK Tools tab.
- Check the boxes next to LLDB, CMake, and NDK, as shown in below figure:
Two ways for using NDK: ndk-build, CMake
- NDK is a collection of compilers and libraries that are needed to build C/C++ code for android.
- ndk-build and CMake both use the NDK and solve the same problem.
— — CMake:
- CMake is a new way and the default build tool for using ndk. If we are creating new native library , then we should use CMake.
— — ndk-build:
- Android studio also support this ndk-build(this is a build system included in NDK) due to the large number of existing projects that use the build toolkit to compile their native code.
- It uses android.mk files. in other way we can say android.mk file contains ndk-build.
- If you use CMake then you don’t need Android.mk instead you will need CMakeList.txt
CMake or ndk-build .
- If you’re not familiar with either or your project is cross platform, CMake is probably the better choice. Because CMake’s main advantage is that you can use one set of build files for all your targets (Android, Linux, Windows, iOS, etc).
- ndk-build should be preferred if you’re building a project that already uses Android.mk files for its build system (legacy projects).
***Securing Keys using ndk-build…(Item-01)
step 01: Create a folder “jni” under src/main.
step 02: Create a file ‘Android.mk’ under “jni” folder with following content:
step 03: Create another file ‘Application.mk’ file under “jni” folder with the following content:
Here, This is for setting different Application Binary Interface or ABI. More details can be found at Application.mk.
step 04: Create the C/C++ file “keys.c” under “jni” folder. Add the following content to it:
- “Java_com_shishirstudio_ndktest_MainActivity_getFacebookApiKey”: represents the Java code with package name “com.shishirstudio.ndktest” [ dot(.) must be replaced with underscore(_)]followed by Activity name “MainActivity” where we want to call the native function and the static method “getFacebookApiKey” to fetch the API key from the native function. So the combination is:< Package Name>_
_ - In the above code, I have encoded the actual API key using Base64 encoding (You can store actual API key) for making another security layer.
step 05: Add a static block and load the library ‘keys’ in the activity where you will access the keys. (In our case in MainActivity).
step 06: We also have to add member functions of type “native” to access the keys from the C/C++ file. Since we stored 2 keys, we will declare 2 functions:
step 07: Now access the keys in the code like:
step 08: Oh ! one more things, to compile or make the native build using NDK, don’t forget to add entry into the gradle file:
step 09: Now, sync and build the project. And you will find everything is ok….
***Securing Keys using CMake…(item-02)
step 01: Create a new project or use existing project.
step 02: Create a folder named “cpp” under src/main.
step 03: Create the C/C++ file “native-lib.cpp” under “cpp” folder. And add the following content to it:
- “Java_com_shishirstudio_ndktest_MainActivity_stringFromJNI”: represents the Java code with package name “com.shishirstudio.ndktest” [ dot(.) must be replaced with underscore(_)]followed by Activity name “MainActivity” where we want to call the native function and the static method “stringFromJNI” to fetch the API key from the native function. So the combination is:< Package Name>_
_
Now the directory looks like below…
step 04: Create a CMake build script CMakeLists.txt under app folder ( This is a plain text file that you must name CMakeLists.txt ) and add the following content to it.
Step 05: Now, our C/C++ native files and Java code are ready. But to compile or make the native build using NDK, we need to add entry into the gradle file:
Step 06: To use this API follow the step 05,06 and 07 of item-01.
***** The easiest and best way*****
In the newer updates of Android Studio its very easy because everything will be ready for you automatically. Just follow the below step:-
step 01: Enable “include C++ support” while taking a new project in android studio.
Источник
Android Keystore System
In this document
Blog articles
Unifying Key Store Access in ICS
The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the key material remaining non-exportable. Moreover, it offers facilities to restrict when and how keys can be used, such as requiring user authentication for key use or restricting keys to be used only in certain cryptographic modes. See Security Features section for more information.
The Keystore system is used by the KeyChain API as well as the Android Keystore provider feature that was introduced in Android 4.3 (API level 18). This document goes over when and how to use the Android Keystore provider.
Security Features
Extraction Prevention
Key Use Authorizations
Supported key use authorizations fall into the following categories:
- cryptography: authorized key algorithm, operations or purposes (encrypt, decrypt, sign, verify), padding schemes, block modes, digests with which the key can be used;
- temporal validity interval: interval of time during which the key is authorized for use;
- user authentication: the key can only be used if the user has been authenticated recently enough. See Requiring User Authentication For Key Use.
As an additional security measure, for keys whose key material is inside secure hardware (see KeyInfo.isInsideSecurityHardware() ) some key use authorizations may be enforced by secure hardware, depending on the Android device. Cryptographic and user authentication authorizations are likely to be enforced by secure hardware. Temporal validity interval authorizations are unlikely to be enforced by the secure hardware because it normally does not have an independent secure real-time clock.
Whether a key’s user authentication authorization is enforced by the secure hardware can be queried using KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware() .
Choosing Between a Keychain or the Android Keystore Provider
Use the KeyChain API when you want system-wide credentials. When an app requests the use of any credential through the KeyChain API, users get to choose, through a system-provided UI, which of the installed credentials an app can access. This allows several apps to use the same set of credentials with user consent.
Use the Android Keystore provider to let an individual app store its own credentials that only the app itself can access. This provides a way for apps to manage credentials that are usable only by itself while providing the same security benefits that the KeyChain API provides for system-wide credentials. This method requires no user interaction to select the credentials.
Using Android Keystore Provider
To use this feature, you use the standard KeyStore and KeyPairGenerator or KeyGenerator classes along with the AndroidKeyStore provider introduced in Android 4.3 (API level 18).
AndroidKeyStore is registered as a KeyStore type for use with the KeyStore.getInstance(type) method and as a provider for use with the KeyPairGenerator.getInstance(algorithm, provider) and KeyGenerator.getInstance(algorithm, provider) methods.
Generating a New Private Key
Generating a new PrivateKey requires that you also specify the initial X.509 attributes that the self-signed certificate will have. You can replace the certificate at a later time with a certificate signed by a Certificate Authority.
Generating a New Secret Key
Working with Keystore Entries
Using the AndroidKeyStore provider takes place through all the standard KeyStore APIs.
Listing Entries
List entries in the keystore by calling the aliases() method:
Signing and Verifying Data
Sign data by fetching the KeyStore.Entry from the keystore and using the Signature APIs, such as sign() :
Similarly, verify data with the verify(byte[]) method:
Requiring User Authentication For Key Use
When generating or importing a key into the AndroidKeyStore you can specify that the key is only authorized to be used if the user has been authenticated. The user is authenticated using a subset of their secure lock screen credentials (pattern/PIN/password, fingerprint).
This is an advanced security feature which is generally useful only if your requirements are that a compromise of your application process after key generation/import (but not before or during) cannot bypass the requirement for the user to be authenticated to use the key.
Источник