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.
Related
I am attempting to do the following,
Generate a secret key for sqlcipher.
Store the secret in android keystore.
Retrieve secret from keystore.
I've found nearly everything I need, but I'm having trouble getting the below pieces of code working together.
Pre: Setup keystore
private void InitialiseKeystore() throws ... {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
boolean containsAlias = keyStore.containsAlias("com.example.myapp");
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
if (!containsAlias) {
kpg.initialize(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.build());
KeyPair kp = kpg.generateKeyPair();
}
Generating symmetric key
I found these good examples but they're creating public/private keys, as the same with many others, so a bit confused there, but I have the following.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128); //<------------------------------------- [ERRORS HERE]
SecretKey secretKey = keyGen.generateKey();
Storing in keystore
Reviewing the documentation, it seems I should use KeyStore.SecretKeyEntry to store secrets, so this is what I currently have,
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
keyStore.setKeyEntry("key1", secretKeyEntry.getSecretKey().getEncoded(), null);
Retrieving from keystore
private static byte[] getRawSecret() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
byte[] raw = keyStore.getKey("key1", null).getEncoded();
return raw;
}
Here's where it goes wrong, in another function I'm trying to call getRawSecret().
SQLiteDatabase.openOrCreateDatabase(databaseFile, getRawSecret().toString(), null);
ERROR: Cannot initialize without a android.security.keystore.KeyGenParameterSpec parameter
So question, how do I create a symmetric key and store in keystore? As the error is telling me to use KeyGenParameterSpec but as in the examples linked above, this wants to create public/private keys. What am I doing wrong?
I used this to solve my issue,
Guide: https://medium.com/#josiassena/using-the-android-keystore-system-to-store-sensitive-information-3a56175a454b
Code: https://gist.github.com/JosiasSena/3bf4ca59777f7dedcaf41a495d96d984
I've created a key pair, stored the private key in the AndroidKeystore.
Generated a CSR using the Public key from the key pair
Exported the CSR to the CA, and got it to generate a cert
Installed the cert on the phone
Now, I'm trying to associate the private key from #1 with the cert that was installed at step #4.
Have looked all day, cannot find a mechanism to do it. Android documentation suggests using the KeyStore.setKeyEntry method. I intend to use it but am unable to read the cert that was installed.
I can display all certs in the AndroidCAStore, and I do see this newly installed cert in the "User certificates" section on the phone but am unable to read it programatically. Any ideas ?
Trying to do pretty much the same thing. The key association with the signed cert also works using keystore.setEntry. What I can't get to work is this user cert then to appear in the "IPSec User certificate" listbox when trying to add a VPN. Running on Marshmallow 6.0.1
My logic looks like this
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyGen = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
keyGen.initialize(
new KeyGenParameterSpec.Builder(
"myAlias",
KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp384r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setUserAuthenticationRequired(false)
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(nowPlusThreeYears)
.build());
keyPair = keyGen.generateKeyPair();
privateKey = (PrivateKey) keyStore.getKey("myAlias", null);
publicKey = keyStore.getCertificate("myAlias").getPublicKey();
Certificate[] chain = keyStore.getCertificateChain("myAlias");
keyStore.setKeyEntry("myAlias", privateKey, null, chain);
StringBuilder x500PrincipalBuilder = new StringBuilder("CN=");
x500PrincipalBuilder.append("My Company");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("L=");
x500PrincipalBuilder.append("My Location");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("ST=");
x500PrincipalBuilder.append("CA");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("O=");
x500PrincipalBuilder.append("My Org");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("OU=");
x500PrincipalBuilder.append("my ou");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("C=");
x500PrincipalBuilder.append("US");
Signature ecdsaSignature = Signature.getInstance("SHA384withECDSA");
ecdsaSignature.initSign(keyPair.getPrivate());
byte[] strByte = new byte[0];
strByte = x500PrincipalBuilder.toString().getBytes(UTF8_CHARSET);
ecdsaSignature.update(strByte);
X500Principal x500Principal = new X500Principal(x500PrincipalBuilder.toString());
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
x500Principal, keyPair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(signatureGenerator.getSigningAlgorithm());
ContentSigner signer = csBuilder.build(keyPair.getPrivate());
org.bouncycastle.pkcs.PKCS10CertificationRequest csr = p10Builder.build(signer);
PemObject pemObject = new PemObject("CERTIFICATE REQUEST", csr.getEncoded());
StringWriter str = new StringWriter();
PEMWriter pemWriter = new PEMWriter(str);
pemWriter.writeObject(pemObject);
pemWriter.close();
str.close();
// write the csr to a file
// get it signed by the CA (Microsoft CA)
// Push the cert to the phone
// Install the cert
Certificate clientCertSignedByCA = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream("/path/to/cert.cer));
KeyStore.Entry entry = keyStore.getEntry("myAlias", null);
PrivateKey privateKey = (PrivateKey) keyStore.getKey("myAlias", null);
Certificate[] chain = new Certificate[1];
chain[0] = clientCertSignedByCA;
keyStore.setKeyEntry("myAlias", privateKey, null, chain);
I have been using the keystore to generate RSA key/pair and the code has been working for API levels 18-22.
Today when I ran it on API-23, I am not able to retrieve the Private key from the keystore.
Below is the code that I have been using:
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(utility.getConfigValue(configuration, OuterKeys.KEYSTORE_NAME))
.setSubject(
new X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE).setStartDate(start.getTime())
.setEndDate(end.getTime()).build();
KeyPairGenerator generator = null;
generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] publicKeyBytes = publicKey.getEncoded();
String pubKeyStr = Base64.encodeToString(publicKeyBytes, Base64.NO_PADDING);
byte[] privKeyBytes = privateKey.getEncoded();
String privKeyStr = Base64.encodeToString(privKeyBytes, Base64.NO_PADDING);
Log.d("^^^^^^^^^1",pubKeyStr);
Log.d("^^^^^^^^^2",privKeyStr);
I debugged this and found that the private key generated is null.
Whereas I can print the public key string as it is being retrieved.
Can anyone help out please.
It looks like you are retrieving the PrivateKey instance from Android Keystore just fine. You should be able to use this PrivateKey instance with Cipher and Signature primitives just fine too.
What's "not working" is that that PrivateKey instance's getEncoded returns null. As James K Polk mentioned, this 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 keys and thus getEncoded returns null. On older Android platform versions it may have been returning an empty byte array instead.
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,