I am currently working on android application which is based on Client-server architecture. For data security, I am using Public-Private key pair for data encryption and signing. I am using AndroidKeyStore for storing key pair. Below is the code to generate key pair:
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(
mContext)
.setAlias(mPrivateKeyAlias)
.setSubject(new X500Principal("CN=" + mPrivateKeyAlias))
.setSerialNumber(
BigInteger.valueOf(System.currentTimeMillis()))
.setStartDate(start.getTime())
.setEndDate(end.getTime()).setKeySize(2048).build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(
"RSA",
"AndroidKeyStore");
kpGenerator.initialize(spec);
// Key Pair will be saved in AndroidKeyStore
KeyPair pair = kpGenerator.generateKeyPair();
After executing this code, Keystore releated files (CERT and PKEY files) will be generated at '/data/misc/keystore/user_0/' directory.
I am encrypting application sensitive data like auth-token and saving it to Shared Pref for security reasons.
But now when user changes device password or pin, keystore files are getting deleted as Masterkey used for keystore encryption is generated using device credentials.
Now to fix this issue, I tried to keep Public-Private key pair in RAM and when password gets changed. From onPasswordChanged(Context context, Intent intent) method of DeviceAdminReceiver, I am executing below code :
KeyStore keyStore = KeyStore
.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setKeyEntry(mPrivateKeyAlias, mPrivateKey.getPrivateKey(),
null, new Certificate[] { mPrivateKey.getCertificate() });
But, after this code only CERT file gets created at '/data/misc/keystore/user_0/' directory and while decryption using private key, giving some invalid signature error.
Also, I have shared my public key with server, encrypted data with private key, so creating new key pair would not be better solution.
So, how I can retain my public private key pair after device password change ? If there is no work around, what is the exact use of AndroidKeyStore? Where can I use it ?
This issue has been fixed by Google in Android 5.0 (Lollipop) release. But, for previous versions of Android, you will have to live with this issue. :(
Related
I want to encrypt and decrypt with symmetrically with android Keystore KMS. I'm aware with Google cloud KMS, and AWS KMS, but I don't want to handle with that platform.
How to manage this generated Android Keystore private key for both (client, server) sides?
I have created a private key for encrypting and decrypting, but hard to manage for the store and share this key.
I had stored that private key in Private SharedPreferences for reuse but There is one problem is that, this private SharedPreferences is not secured because all can observe this private SharedPreferences file in the rooted device.
Refer this link to get information about generating a private key for Android Keystore.
I'm new with tink, so please help me to out this. if there is a wrong thing in my idea then feel free to give your opinion.
Android Keystore is a client side KMS, you cannot use it on server side.
If you want to use Tink with Android Keystore on Android, please take a look at AndroidKeysetManager. Here's an example:
String masterKeyUri = "android-keystore://my_master_key_id";
AndroidKeysetManager manager = AndroidKeysetManager.Builder()
.withSharedPref(getApplicationContext(), "my_keyset_name", "my_pref_file_name")
.withKeyTemplate(SignatureKeyTemplates.ECDSA_P256)
.withMasterKeyUri(masterKeyUri)
.build();
PublicKeySign signer = PublicKeySignFactory.getPrimitive(manager.getKeysetHandle());
This will read a keyset stored in the my_keyset_name preference of the my_pref_file_name preferences file. If the preference file name is null, it uses the default preferences file.
If the keyset is not found or invalid, and a valid KeyTemplate is set with AndroidKeysetManager.Builder.withKeyTemplate(com.google.crypto.tink.proto.KeyTemplate), a fresh keyset is generated and is written to the my_keyset_name preference of the my_pref_file_name shared preferences file.
On Android M or newer and if a master key URI is set with AndroidKeysetManager.Builder.withMasterKeyUri(java.lang.String), the keyset is encrypted with a master key generated and stored in Android Keystore. When Tink cannot decrypt the keyset it would assume that it is not encrypted.
The master key URI must start with android-keystore://. If the master key doesn't exist, a fresh one is generated. Usage of Android Keystore can be disabled with AndroidKeysetManager.Builder.doNotUseKeystore().
On Android L or older, or when the master key URI is not set, the keyset will be stored in cleartext in private preferences which, thanks to the security of the Android framework, no other apps can read or write.
I am generating an Asymmetric key pair in the Android key store as below:
I have used the public key for symmetric key wrapping and storing the wrapped key to a file. When I try to unwrap symmetric key using the private key, I am able to do so within that instance. Once my application is re-installed, I am unable to get the key store entry with the alias.
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
);
kpg.initialize(new KeyGenParameterSpec.Builder(
Constants.KEY_STORE_ALIAS_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
)
.setKeySize(Constants.ASYMMETRIC_KEY_LENGTH)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build()
);
keyPair = kpg.generateKeyPair();
// Code for accessing the key store entry to un wrap the symmetric key
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(Constants.KEY_STORE_ALIAS_NAME, null);
PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
Keys stored in Android Keystore are non-extractable. It is a security measure
Security Features
Android Keystore system protects key material from unauthorized use. Firstly, Android Keystore mitigates unauthorized use of key material outside of the Android device by preventing extraction of the key material from application processes and from the Android device as a whole. Secondly, Android KeyStore mitigates unauthorized use of key material on the Android device by making apps specify authorized uses of their keys and then enforcing these restrictions outside of the apps' processes
This means that the keys can not be part of the Android backup service in any way. It allows to store application data on the cloud once the application is uninstalled. See HowBackupWorks.
It would be a serious security risk that private keys could be extracted and stored in cloud or even that they remain stored in the device when the application has been uninstalled
If you need to use an encryption key that does not depend on the reinstallation, you could generate a symmetric key from a user passphrase using a key derivation algorithm
I have access token from the server after authentication lets say "uyhjjfjfgg567f8fhjkkf" now I want to save it in the device securely. I looked in Keystore and Keychain in android developer sites. I dont clearly understand how it works and how we should retrieve the token from the keystore.
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.build());
KeyPair kp = kpg.generateKeyPair();
/*
* Load the Android KeyStore instance using the the
* "AndroidKeyStore" provider to list out what entries are
* currently stored.
*/
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
You don't need to save the access token, since it has short life anyway. Keeping it in memory is good enough.
You do need to keep the refresh token, and you have a few options for that:
In a file
Either directly in a file in the internal storage
or using SharedPreferences
or in a Database
Using the AccountManager
Consider using the StoredCredential. For the flow itself, I recommend you to use Google AppAuth library.
Of course, you can also encrypt the key using a cipher:
private static byte[] encrypt(byte[] key, byte[] text) throws GeneralSecurityException {
final SecretKeySpec skeySpec = new SecretKeySpec(key, KEY_ALGORITHM);
final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, sInitVectorSpec);
return cipher.doFinal(text);
}
And the key can be stored in the KeyStore.
We use a custom SharedPreference instance that encrypts the keys and values when adding, and decrypts when requesting.
SecurePreferences preferences = ...
preferences.edit().putString( "key", "value" ).apply(); // key and value are encrypted automatically
String value = preferences.getString( "key", null ); // key and value are decrypted automatically
I would only recommend using SharedPreferences if the values are encrypted, because even though the xml file is only available to the app, it can be accessed on rooted devices.
If you already using a SqlLiteDB, I would probably use that. If not, it's bit heavy for just saving a token.
EDIT:
An oauth token is completely unrelated to the key and keystore used to sign the app.
The oauth token is a token provided by the server after validating the user's credentials, within the app.
The keystore contains 1 or more certificates that is used to digitally sign the app. This is to prevent someone else from uploading an app that has the same package name as yours and replacing it.
I am trying to setup an encrypted default realm instance in my app.
The idea is to generate a key using a KeyPairGenerator with a given alias, store it in the AndroidKeyStore and use said key every time it is needed.
WHAT I DO
This is how i generate the key:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
if (!ks.containsAlias(KEY_ALIAS)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 99);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=Example, O=ExampleOrg"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
I am using the KeyPairGenerator as i need to support api versions 18 and up.
Here is how i setup my default realm instance in my Application:
RealmConfiguration config = null;
try {
config = new RealmConfiguration
.Builder(this)
.encryptionKey(ks.getKey(KEY_ALIAS, null).getEncoded())
.name("dealmatrix.realm")
.schemaVersion(1)
.build();
where ks is a Keystore instance acquired like so:
Keystore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
WHAT GOES WRONG
My problem is that this expression:
ks.getKey(KEY_ALIAS, null).getEncoded()
returns null, which understandably leads to an exception.
I have read online that this is the intended behaviour of the KeyStore system.
If indeed i am unable to get the stored encryption key's byte array, how am I supposed to encrypt my realm using said key?
Are there any other methods to securely store an encryption key so that i may use it in my realm configuration?
There is a WIP example project in feature/example/store_password branch in Realm repository which uses Android keystore.
https://github.com/realm/realm-java/tree/feature/example/store_password/examples/StoreEncryptionPassword
Core logic is written in Store.java
We need some more works(cleanup, adding comments, supporting old devices) before releasing this example project. But I think this project helps you.
Android Keystore keys returning null from getEncoded is working as intended. getEncoded is supposed to return the private key's key material (usually in PKCS#8 DER-encoded format) or null if key material export is not supported. Android Keystore by design does not reveal/export key material of private or secret keys and thus getEncoded returns null. See https://developer.android.com/training/articles/keystore.html#SecurityFeatures.
You can still use these keys just fine with Signature and Cipher abstractions.
The Android Keystore prohibits extraction of private keys from it. So the design would be to generate a Realm key outside of the Android Keystore, so that you can use it for the encryption/decryption of the Realm database.
But to safely store that Realm key, you would use the power of the Android Keystore, by encrypting the Realm key with the Android Keystore and then store it locally (e.g. Shared Preferences). Later you could read that encrypted Realm key, decrypt it with the Android Keystore and use it again to unlock your Realm database.
I am trying to write an application that enables users to exchange their public (e.g. RSA) keys via Bluetooth/NFC. To store and later retrieve the keys, I would like to use Android's KeyChain API.
I have read the online API documentation and some tutorials/examples so far. They all involve importing a keychain from a PKCS12 file which contains a private key and the corresponding public key and certificate. Once this file is imported, an alias-String is returned for subsequent reference. So far, so good.
However, what I want to do is receive someone's public key, store it, get back an alias, store that alias somewhere (e.g. contacts data) and use it to retrieve back the public key when the user wants to encrypt a message to that person. Is that possible? I have very little experience with Public-key cryptography and have the feeling that I have misunderstood the whole purpose of the KeyChain API.
Any help would be much apprechiated! Thanks.
The Android KeyChain API is designed to store SSL certificates and keys: your own keys when installing a PKCS#12 or a trusted root certification authorities certificates. This keys and certificate are then available to all applications.
It depends on the purpose of the application you are developing but you maybe should consider using a KeyStore dedicated to your application to store the keys you received instead of the KeyChain API.
Another limitation of both API (KeyStore and KeyChain) is taht it is not possible to directly store public keys. You need to have a certificate. I suggest you to embedded a self-signed certificate in your application and use this certificate to sign "dummy" certificates containing the public keys the application will receive.
A simplified code snippet with the bouncycastle library to store a public RSA key:
public void storeRSAPublicKey(String alias, BigInteger modulus, BigInteger exponent)
{
/** Load the key to generate the certificate */
KeyStore ks = getApplicationKeyStore();
KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)ks.getEntry(MY_PRIVATE_KEY, null);
X509Certificate issuerCert = (X509Certificate)entry.getCertificate();
PrivateKey skey = entry.getPrivateKey();
/** Prepare the certificate template */
RSAKeyParameters params = new RSAKeyParameters(false, modulus, exponent);
SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfoFactory.SubjectPublicKeyInfo(params);
X500Name issuer = new X500Name(issuerCert.getIssuerX500Principal().getName());
X500Name subject = new X500Name("CN=alias");
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuer, randomSeriaNumber(), new Date(), dateIn20years(), subject, pkInfo);
/** Generate the certificate */
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(skey);
X509CertificateHolder holder = builder.build(signer);
/** Store the certificate in the KeyStore */
JcaX509CertificateConverter conv = new JcaX509CertificateConverter();
X509Certificate cert = conv.getCertificate(holder);
ks.setCertificateEntry(alias, cert);
pushKeyStoreToPersistentStorage(ks);
Now you can get the certificate with ks.getCertificateEntry(alias);
Note that I omitted some non fundamental code like serial number generation and not-after date computation.
You can create the initial KeyStore with keytool and add it to you app as a resource.