I'm trying to store a short piece of text inside Android's Secure element using the following code:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
PasswordProtection keyStorePP = new KeyStore.PasswordProtection(null);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithHmacSHA1");
SecretKey generatedSecret = factory.generateSecret(newPBEKeySpec(mySecureText.toCharArray()));
ks.setEntry("alias", new KeyStore.SecretKeyEntry(generatedSecret), keyStorePP);
The code above gives the following exception:
08-17 14:39:32.832: W/System.err(11713): java.security.KeyStoreException: Unsupported protection parameter class: java.security.KeyStore$PasswordProtection. Supported: android.security.keystore.KeyProtection
Is there something I'm doing wrong or just AndroidKeyStore does not support storing strings?
Last thing, if I'll switch to BKS the code will execute.
Thanks.
The error tells you to change this:
PasswordProtection keyStorePP = new KeyStore.PasswordProtection(null);
To this
PasswordProtection keyStorePP = new KeyStore.KeyProtection();
KeyProtection cant set null password.
char[] password = {'p','a','s','s'};
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(password);
ks.setEntry("alias", new KeyStore.SecretKeyEntry(generatedSecret), keyStorePP);
I got this to work below. This is how you specify new KeyProtection:
import android.security.keystore.KeyProtection;
import android.security.keystore.KeyProperties;
...............
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
SecureRandom secureRandom = new SecureRandom(mySecureText.getBytes());
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(256, secureRandom);
SecretKeySpec keySpec = new SecretKeySpec(keyGenerator.generateKey().getEncoded(), KeyProperties.KEY_ALGORITHM_AES);
keyStore.setEntry(alias, new SecretKeyEntry(keySpec),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()
);
Related
I trying to insert 3DES key into AndroidKeyStore with setIsStrongBoxBacked(true) and then encrypt and decrypt some text. I understand that normal AndroidKeyStore dont support 3DES, but Hardware security module support Triple DES according this document https://developer.android.com/training/articles/keystore#HardwareSecurityModule
this is my test code:
KeyGenerator kg = KeyGenerator.getInstance("DESede");
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setEntry(
"TestAlias",
new KeyStore.SecretKeyEntry(kg.generateKey()),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setIsStrongBoxBacked(true)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
SecretKey key = (SecretKey) keyStore.getKey("TestAlias", null);
Cipher c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
c.init(Cipher.ENCRYPT_MODE, key);
IvParameterSpec paramSpec = new IvParameterSpec(c.getIV());
byte[] encrypted = c.doFinal("hello, world".getBytes());
c = Cipher.getInstance("DESede/CBC/PKCS7Padding");
c.init(Cipher.DECRYPT_MODE, key, paramSpec);
String decrypted = new String(c.doFinal(encrypted));
But it always crash on line c.init(Cipher.ENCRYPT_MODE, key); with
java.lang.NullPointerException: Attempt to get length of null array
at com.android.org.bouncycastle.crypto.params.KeyParameter.(KeyParameter.java:17)
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:787)
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:1153)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2985)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2892)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2797)
at javax.crypto.Cipher.chooseProvider(Cipher.java:774)
at javax.crypto.Cipher.init(Cipher.java:1144)
at javax.crypto.Cipher.init(Cipher.java:1085)
....
I already tested AES instead 3DES and it complet without problems.
Testing on Pixel 6 with strongbox support.
Was create key successful? Also, it seems that the code you’re using does not have BouncyCastle but the logs have bouncycastle. Am I missing something?
It is possible that you’re using previously created key with wrong provider later on.
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.
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);