Android Keystore Error "could not generate key in keystore"
Asked Answered
P

4

41

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.

Panne answered 26/6, 2015 at 18:9 Comment(2)
I've seen this workaround in other places, but doesn't look like you've tried this exact combination: If the lock screen is disabled: uninstall app, set lock screen, unset lock screen, then reinstall. If lock screen is enabled, uninstall, unset lock screen, set lock screen, then reinstall. I don't have a device to reproduce, but I'm looking for a workaround for one of my users.Sitdown
When you are asked to "enter password for credential storage", correct password may be the fallback PIN you choose when you are setting pattern. At least, that was my case.Marginate
G
4
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.

Generally answered 30/5, 2018 at 14:32 Comment(0)
D
2

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 ;-)

Dewy answered 2/2, 2017 at 22:58 Comment(0)
L
0

If your code is OK then remember that you need to set up a PIN/PW/Fingerprint (Secure unlock) for your device so that the keystore starts functioning. A simple swipe will give such error if you try to generate keypairs.

Lockett answered 16/7, 2017 at 1:23 Comment(0)
D
-6

I think this is the right direction: Right click on project > Android Tools > Export Signed Application Package The Export Android Application wizard appears. Select the project I want to export, click Next. The Keystore Selection screen appears.

Deshawndesi answered 24/11, 2016 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.