I have Generated a RSA key pair in AndroidKeystore. I need to protect my Alias with a password so that no other can access the same alias without the password.
I have used the following code to generate the keypair.
if (!keyStore.containsAlias(alias)) {
Calendar notBefore = Calendar.getInstance();
Calendar notAfter = Calendar.getInstance();
notAfter.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
.setAlias(alias)
.setKeyType("RSA")
.setKeySize(2048)
.setSubject(new X500Principal("CN=test"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(notBefore.getTime())
.setEndDate(notAfter.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
Related
I've stumpled upon following Problem:
I need to create a KeyPair where I need to access the PrivateKey's getEncoded() once, before inserting it into the AndroidKeyStore.
When generating a Key via AndroidKeyStore, the resulting Keys getEncoded() method returns null (as intended as an extra key protection mechanism).
This is how I generate a Key using a KeyGenParameterSpec (targeting only devices above Android.M):
public AlgorithmParameterSpec createSpec(){
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 1);
String alias = "myKeyAlias";
AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY |
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setCertificateSubject(new X500Principal("CN=" + alias))
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setCertificateSerialNumber(BigInteger.valueOf(1337))
.setCertificateNotBefore(start.getTime())
.setCertificateNotAfter(end.getTime())
.build();
return spec;
}
public KeyPair generateKeyPairInKeystore(AlgorithmParameterSpec spec){
KeyPairGenerator kpGenerator = KeyPairGenerator
.getInstance("RSA", "AndroidKeyStore");
kpGenerator.initialize(spec);
KeyPair kp = kpGenerator.generateKeyPair();
//here kp.getPrivate().getEncrypted() doesn't give me the key
return kp;
}
public KeyPair generateKeyPair(AlgorithmParameterSpec spec){
KeyPairGenerator kpGenerator = KeyPairGenerator
.getInstance(SecurityConstants.TYPE_RSA);
kpGenerator.initialize(spec);
KeyPair kp = kpGenerator.generateKeyPair();
//I cannot receive the encrypted Key here either
//kp.getPrivate().getEncrypted();
return kp;
}
These Keys are generated inside AndroidKeyStore so they are automatically stored there. But I cannot access getEncrypted() to send it to a trusted Server.
So what I'm missing is:
How to create a PrivateKey where getEncoded() is accessible?
How do I store it inside Androids secure KeyStore (as I need to provide Certificates along)?
Android API 28 running on a Pixel 3 gives the option to require User Presence for keys generated inside the Android KeyStore. But how do I actually test for user presence when creating a signature with that key? Am I missing something in the docs?
KeyGenParameterSpec.Builder keyGenSpec = new KeyGenParameterSpec.Builder("alias", KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA256)
.setKeySize(256)
.setIsStrongBoxBacked(true)
.setUserPresenceRequired(true);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
keyPairGenerator.initialize(keyGenSpec.build());
keyPairGenerator.generateKeyPair();
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
PrivateKey privateKey = (PrivateKey) keyStore.getKey("alias", null);
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update("Hello".getBytes(Charset.defaultCharset()));
byte[] sign = signature.sign();
That code throws android.security.KeyStoreException: -69 which translates to PROOF_OF_PRESENCE_REQUIRED. I've also tried to wrap the signature process in an BiometricPrompt, but to no avail.
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.
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,