Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems loading private Key from AndroidKeystore

Tags:

android

Android provides a API to generate keys for crypto operations and to store/load them in/from Androids Systemkeystore.

I followed the Example in the JavaDocs of KeyGenParameterSpec class. Generation, store and load of the Secret Key works. But if I try to use the key, the init() call of the Cipher Object fails. I debugged a little bit, and I can see, that the loaded Key is of type "android.security.keystore.AndroidKeyStoreSecretKey". This implementation prevents, that the byte[] of the key is exposed. I understand this for security reasons, but if I want to use the key, I have to get the key content. So, I must do something wrong. Maybe, there exists another way for using crypto operations in Android? Or is the loading code of the Key wrong?

Here is the code:

KeyGenerator keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");

KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("demo-alias", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
builder.setKeySize(256);
builder.setBlockModes(KeyProperties.BLOCK_MODE_CBC);
builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
keyGenerator.init(builder.build());

// this key will work with a CipherObject ...
SecretKey key = keyGenerator.generateKey();

// Load the key from the Keystore
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

// This key will not work with the Cipher Object
SecretKey notWorkingKey = (SecretKey) keyStore.getKey("demo-alias", null);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// That call fails
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[]{87, 99, -94, 23, -17, 26, 84, -117, 59, -59, 25, -88, -66, 86, -42, 78}));

byte[] crypted = cipher.doFinal("testdata".getBytes());

the init(...) of the cipher fails with the following exception:

java.lang.NullPointerException: Attempt to get length of null array
    at com.android.org.bouncycastle.crypto.params.KeyParameter.<init>(KeyParameter.java:13)
    at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(BaseBlockCipher.java:557)
    at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:608)
    at javax.crypto.Cipher.tryCombinations(Cipher.java:532)
    at javax.crypto.Cipher.getSpi(Cipher.java:437)
    at javax.crypto.Cipher.init(Cipher.java:909)
    at javax.crypto.Cipher.init(Cipher.java:859)
    at de.demo.crypt.LoginActivity.executeLogin(LoginActivity.java:95)
    at de.demo.crypt.LoginActivity.access$000(LoginActivity.java:37)
    at de.demo.crypt.LoginActivity$1.onClick(LoginActivity.java:58)
    at de.demo.crypt.ActionButton.buttonClicked(ActionButton.java:104)
    at de.demo.crypt.ActionButton.access$000(ActionButton.java:17)
    at de.demo.crypt.ActionButton$1.onClick(ActionButton.java:60)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
like image 775
creaity Avatar asked Dec 28 '25 16:12

creaity


1 Answers

Use "AES/CBC/PKCS7Padding" or "AES/CBC/" + KeyProperties.ENCRYPTION_PADDING_PKCS7 for Cipher.getInstance.

Android Keystore only supports PKCS#7 padding for AES (see https://developer.android.com/training/articles/keystore.html#SupportedCiphers). PKCS#5 padding technically is not defined for block sizes larger than 64 bit (AES uses 128 bit blocks). Typically, when people say PKCS#5 padding they mean PKCS#7 padding these days.

The particular error you're seeing is because Bouncy Castle erroneously claims to support Android Keystore keys for AES/CBC/PKCS5Padding (see Bouncy Castle issue tracker issue BJA-543).

P. S. It is best practice to let the encryption Cipher implementation generate a random IV for your instead of providing the Cipher with an IV yourself -- you can later query the generated IV using Cipher.getIV(). If, for some reason, you must provide your own IV when encrypting, when generating the key you'll need to disable the default requirement of randomized encryption on the key (https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder.html#setRandomizedEncryptionRequired(boolean)).

like image 133
Alex Klyubin Avatar answered Dec 30 '25 04:12

Alex Klyubin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!