In my android app, I receive an API token that I get from an http request
I did some research, and, If it seems the best way to store this key, is with the Android Keystore Provider.
I had a look at the documentation https://developer.android.com/training/articles/keystore.html#UsingAndroidKeyStore
But since i'm new with Android and programing in general, I need some help to put that in place.
I'm not sure in which variable I should save the key :
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias(alias)
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=test1"))
.build());
KeyPair kp = kpg.generateKeyPair();
Then how should i retrieve the key, using (I think) the following code :
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
Thank you for your help.
I would assume that your API token is only valid for a certain period of time. Just store it in a SharedPreferences for your application. If it isn't there, or it has expired, just get another one.
Related
Which method stores Private and Public keys in AndroidKeyStore?
I have implemented below code to initialise keystore and generate private and public keys.
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyPair keyPair;
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 50);
KeyPairGenerator generator= KeyPairGenerator.getInstance("RSA","AndroidKeyStore");
KeyPairGeneratorSpec keyPairGeneratorSpec = new KeyPairGeneratorSpec.Builder(context).
setAlias("alias").
setSubject(new X500Principal("O=Authority")).
setSerialNumber(BigInteger.ONE).
setStartDate(start.getTime()).
setEndDate(end.getTime()).build();
if (generator != null) {
generator.initialize(keyPairGeneratorSpec);
}
keyPair = generator.generateKeyPair();
To your question, this line
generator.generateKeyPair(); implement the keypair generating and storing processs.
You might be confused when seeing codes in java.security.KeyPairGenerator like this
public KeyPair generateKeyPair() {
// ...
return null;
}
But actually since KeyPairGenerator is an abstract class, the 'true' class here using is
java.security.KeyPairGenerator$Delegate, which delegates the generateKeyPair like this
You can also check the difference of alias list in "AndroidKeyStore" before and after this process.
I would like to share the gist about how to view alias list of any keystore. Hope it might help you testing: https://gist.github.com/davidkhala/4aa1d6b44f287699aeac028786633c7a
When you generate a key pair with AndroidKeyStore, it is already automatically stored. You can retrieve it when you need it using the alias you specified ("alias" in your example).
Note, however, that when you get the PrivateKey, you do not actually get the private key secrets. Those stay in secure hardware and never leave it, so they can't leak. But you get a PrivateKey object which you can use just as though you had the secrets; your requests to encrypt or sign are sent to the secure hardware, which uses the secrets to perform the operation and hands the result back to you.
In my app we are using RSA key, that the app generate (using android key store) on the first launch. From unknown reason, the app failed to retrieved the key from the key store on some of the devices. I've checked the logs, and I could not find a correlation between this bug to a specific OS version or to a specific device model. Also, I know for sure the app tried to read it only after the key created. So - my question is that: As far as I know, android key store should be persistent. What can cause such a bug?
Bellow are relevant code samples.
Key generation:
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", keyStore.getProvider());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
KeyGenParameterSpec spec;
spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT| KeyProperties.PURPOSE_SIGN| KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setKeySize(2048)
.build();
generator.initialize(spec);
} else {
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 500);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal("CN="+ subject))
.setSerialNumber(BigInteger.valueOf(new Random().nextInt(Integer.MAX_VALUE)))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setKeySize(2048)
.build();
generator.initialize(spec);
}
return generator.generateKeyPair();
} catch (Exception e) {
logger.warn("Failed to create private key in store", e);
return null;
}
The keystore itself intialized using the following code:
KeyStore androidKeyStore = KeyStore.getInstance("AndroidKeyStore");
androidKeyStore.load(null);
return androidKeyStore;
And we use the following code to retrieve the key, the bug is that on some devices the keystore returns null:
try {
Key key = keyStore.getKey(alias, null);
if (key == null){
logger.warn("Key not found in key store");
return null;
}
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keyStore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
} else {
logger.warn("Key found, but not from current type. type found: " + key.getClass().getSimpleName());
}
return null;
}catch (Exception e){
logger.warn("Failed to get private key in store", e);
return null;
}
Thanks,
Omer
Just in case someone will run into the same problem:
I've found out that Azure Active Directory library for android suffer from similar issue, and from reading the code I've saw they linked to two issues that are similar to this problem and to another issue we have. Because of that I am planing to use keystore based on p12 file, stored in the app private storage.
Is it that you appear to lose your keys immediately after generation or some time later they are lost? Take a look at this question AndroidKeyStore getEntry is consistently failing after certain point which links to this great article: http://doridori.github.io/android-security-the-forgetful-keystore/
The moral of this story is if you use the AndroidKeyStore be prepared to lose your keys under certain circumstances!
Would appreciate any hint what I am missing in below code?
void addKeyToKeyStore() {
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
.setAlias("MY_ALIAS")
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=MY_ALIAS"))
.build());
}
void ListTheKeys() {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
final Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
Log.d(TAG, "Key Alias = "+aliases.nextElement());
}
String tempstr= "Entry for MY_ALIAS is " + (ks.isKeyEntry(key)? "exist" : "not exist");
Log.d(TAG, tempstr)
}
If same app calls addKeyToKeyStore() and then ListTheKeys() - the entry is exist. It also works if app restarted or phone rebooted.
However if app1 calls addKeyToKeyStore() but app2 then calls ListTheKeys() - no entry that was added.
Have tried set same sharedUserID in AndroidManifest(s) - no effect.
I need to be able to access KeyStore from different apps.
Thanks in advance!
Never mind. I have figured out - a bit messed up with sharedUserID. If the sharedUserID is declared properly - everything works.
Issue can be closed.
I have followed this below code that I need to generate a keypair stored in KeyStore
// generate a key pair
Context ctx = getContext();
Calendar notBefore = Calendar.getInstance()
Calendar notAfter = Calendar.getInstance();
notAfter.add(1, Calendar.YEAR);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(ctx)
.setAlias("key1")
.setSubject(
new X500Principal(String.format("CN=%s, OU=%s", alais,
ctx.getPackageName())))
.setSerialNumber(BigInteger.ONE).setStartDate(notBefore.getTime())
.setEndDate(notAfter.getTime()).build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpGenerator.initialize(spec);
KeyPair kp = kpGenerator.generateKeyPair();
By this way, my keypair is generated and stored into Keystore but when calling this function: getPrivateKey.encoded() , it will return a null bytes array.
My will is I want generate normally, I can get both PrivateKey and PublicKey Encode (This is my own purpose). Then I want to store them into KeyStore to keep secret.
I have tried this: keyStore.setEntry(alias, privateKey, cert) but I got exception due to wrong params maybe. I don't understand what certificate I should get from?
I really appreciate your comments
Hi I have a program that need store a key in the keystore, I generate a pair keys and I sign a value and this works perfectly all time. The problem comes when the user goes to preferences and changes the password or change the password mode to pin mode. After that, when I try to access to the private key the keystore return to me a null value.
I know that the keysotore values are signed with the unlock password value, but I believed that if the user changed the password the keystore would be to resign with the new key, but this is not the case.
I'm doing something wrong? If it is not the case, exist any way to take the password change and do manually?
this is the code that I'm using.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(new KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=test1"))
.build());
KeyPair kp = kpg.generateKeyPair();
an this is the code of obtain keystore
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(ALIAS, null);
if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
Log.w("borrar", "Not an instance of a PrivateKeyEntry");
return null;
}
Thank you,