I get an error trying to generate a key for certain devices. I'm able to reproduce the error on a Samsung Galaxy Note running 4.4.2.
java.lang.IllegalStateException: could not generate key in keystore
at android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:100)
at java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:275)
at com.eric.demo.MainActivity.generateKeyPair(MainActivity.java:65)
at com.eric.demo.MainActivity.onClickButton(MainActivity.java:43)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at android.view.View$1.onClick(View.java:3964)
at android.view.View.performClick(View.java:4640)
at android.view.View$PerformClick.run(View.java:19421)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5476)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
at dalvik.system.NativeStart.main(Native Method)
I created a small app to only generate a key by copying the code line by line from the Android developer page https://developer.android.com/training/articles/keystore.html under "Generating a New Private Key."
public void onClickButton (View view) {
try {
generateKeyPair(this, "test3");
} catch (Exception e){
Log.wtf("exception", e);
}
}
private void generateKeyPair(Context context, String alias)
throws Exception {
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=test3"))
.build());
KeyPair kp = kpg.generateKeyPair();
}
The error seems to occur in kpg.generateKeyPair(), inside AndroidKeyPairGenerator.java:
if (!mKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF, keyType,
mSpec.getKeySize(), mSpec.getFlags(), args)) {
throw new IllegalStateException("could not generate key in keystore");
}
and in KeyStore.java:
public boolean generate(String key, int uid, int keyType, int keySize, int flags,
byte[][] args) {
try {
return mBinder.generate(key, uid, keyType, keySize, flags, args) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
}
}
The mBinder.generate() call seems to return 2 which means the keystore is locked?
// ResponseCodes
public static final int NO_ERROR = 1;
public static final int LOCKED = 2;
public static final int UNINITIALIZED = 3;
public static final int SYSTEM_ERROR = 4;
public static final int PROTOCOL_ERROR = 5;
public static final int PERMISSION_DENIED = 6;
public static final int KEY_NOT_FOUND = 7;
public static final int VALUE_CORRUPTED = 8;
public static final int UNDEFINED_ACTION = 9;
public static final int WRONG_PASSWORD = 10;
The error might be somewhat related to this issue with the keystore https://code.google.com/p/android/issues/detail?id=177459&q=could%20not%20generate%20key%20in%20keystore&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
A few things I've tried individually and combinations of the following:
1. Setting encryption required. The result is another error: "Android keystore must be in initialized and unlocked state if encryption is required"
2. Setting a lock screen (pattern, PIN, NONE, password, swipe). Same behavior
3. Programmatically attempting to unlock or reset the credential storage with startActivity(new Intent("com.android.credentials.UNLOCK"));
or startActivity(new Intent("com.android.credentials.RESET"));
Attempting to unlock shows a "enter password for credential storage" message, in which no reasonable password works, and even clearing the credentials doesn't help.
public class EncryptionApi18AndAbove{
private Context context;
private KeyStore keyStore;
private static String alias = "alias";
public EncryptionApi18AndAbove(Context context) {
this.context = context;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (Exception e) {
// bla bla
}
}
private String createNewKeys(String alias, Context context) {
try {
if (!keyStore.containsAlias(alias)) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
.setSerialNumber(BigInteger.ONE)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
generator.initialize(spec);
generator.initialize(spec);
generator.generateKeyPair();
}
} catch (Exception e) {
//bla bla
}
return alias;
}
@Override
public String encrypt(String text) {
if (text == null || text.length() == 0) {
return text;
}
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(createNewKeys(alias, context), null);
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
inCipher.init(Cipher.ENCRYPT_MODE, publicKey);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, inCipher);
cipherOutputStream.write(text.getBytes("UTF-8"));
cipherOutputStream.close();
return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
} catch (Exception e) {
//bla bla
}
return text;
}
@Override
public String decrypt(String text) {
if (text == null || text.length() == 0) {
return text;
}
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(createNewKeys(alias, context), null);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKey);
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte) nextByte);
}
byte[] bytes = new byte[values.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
return new String(bytes, 0, bytes.length, "UTF-8");
} catch (Exception e) {
// bla bla
}
return text;
}
}
You can use this class . This is works min SDK 18 and above. You can create Android Keystore key , decrypt and encrypt simple text.
While I don't know the full answer, I can help continue your search. The binder implementation on the other side of mBinder is the native keystore. If I recall fully, it's behavior is to either 1) support key operations at a software level, or 2) delegate to a OEM-provided keymaster library that (presumably) interfaces to the OEM's hardware-backed keystore. More info on this here, here, and here.
Note: I'll forgo SO's normal policy of pulling the external link content into the answer, since I'm linking you out to three articles that are all >1 page and it seems a bit ridiculous to post a 6-page answer ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With