Search code examples
androidsecurityandroid-5.0-lollipopandroid-keystore

Can't Generate Certificate Exception When Generating Key Pair In API 21


I've been trying to solve this exception that only happens in Android Lollipop (API 21). I tried to look for other solutions but the only one related is Generate KeyPair with KeyPairGeneratorSpec on API < 23 and the suggested solution didn't work.

Am I missing anything? Thanks!

Here's my code that's throwing at the last line:

Calendar start = Calendar.getInstance();
                    Calendar end = Calendar.getInstance();
                    end.add(Calendar.YEAR, 30);
                    KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(appContext)
                            .setAlias(getSecureSettingsKeyAlias())
                            .setSubject(new X500Principal("CN=" + getSecureSettingsKeyAlias()))
                            .setSerialNumber(BigInteger.TEN)
                            .setStartDate(start.getTime())
                            .setEndDate(end.getTime())
                            .build();
                    KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
                    kpg.initialize(spec);
                    kpg.generateKeyPair();

Here's the exception that's happening:

Caused by: java.lang.IllegalStateException: Can't generate certificate
   at android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:136)
   at java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:276)
   at com.sampleid.sampleid_sdk.common.FileSettingsStorage.getKeystore(FileSettingsStorage.java:302)
   at com.sampleid.sampleid_sdk.common.FileSettingsStorage.writeSecureSettings(FileSettingsStorage.java:185) 
   at com.sampleid.sampleid_sdk.common.SettingsImpl.saveSecureSettings(SettingsImpl.java:473) 
   at com.sampleid.sampleid_sdk.common.SettingsImpl.saveSettings(SettingsImpl.java:321) 
   at com.sampleid.sampleid_sdk.sampleIDService.saveSettingsToStorage(sampleIDService.java:102) 
   at com.sampleid.sampleid_sdk.sampleIDService.saveSettings(sampleIDService.java:91) 
   at com.sampleid.sampleid.features.shared.BaseActivity.onStop(BaseActivity.java:182) 
   at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1261) 
   at android.app.Activity.performStop(Activity.java:6089) 
   at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3341) 
   at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3390) 
   at android.app.ActivityThread.access$1100(ActivityThread.java:144) 
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1307) 
   at android.os.Handler.dispatchMessage(Handler.java:102) 
   at android.os.Looper.loop(Looper.java:135) 
   at android.app.ActivityThread.main(ActivityThread.java:5221) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at java.lang.reflect.Method.invoke(Method.java:372) 
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 
Caused by: java.lang.UnsupportedOperationException: private exponent cannot be extracted
   at com.android.org.conscrypt.OpenSSLRSAPrivateKey.getPrivateExponent(OpenSSLRSAPrivateKey.java:161)
   at org.spongycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePrivateKeyParameter(RSAUtil.java:63)
   at org.spongycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi.engineInitSign(DigestSignatureSpi.java:95)
   at java.security.Signature$SignatureImpl.engineInitSign(Signature.java:679)
   at java.security.Signature.initSign(Signature.java:330)
   at com.android.org.bouncycastle.x509.X509Util.calculateSignature(X509Util.java:243)
   at com.android.org.bouncycastle.x509.X509V3CertificateGenerator.generate(X509V3CertificateGenerator.java:434)
   at com.android.org.bouncycastle.x509.X509V3CertificateGenerator.generate(X509V3CertificateGenerator.java:412)
   at android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:133)
   at java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:276) 
   at com.sampleid.sampleid_sdk.common.FileSettingsStorage.getKeystore(FileSettingsStorage.java:302) 
   at com.sampleid.sampleid_sdk.common.FileSettingsStorage.writeSecureSettings(FileSettingsStorage.java:185) 
   at com.sampleid.sampleid_sdk.common.SettingsImpl.saveSecureSettings(SettingsImpl.java:473) 
   at com.sampleid.sampleid_sdk.common.SettingsImpl.saveSettings(SettingsImpl.java:321) 
   at com.sampleid.sampleid_sdk.sampleIDService.saveSettingsToStorage(sampleIDService.java:102) 
   at com.sampleid.sampleid_sdk.sampleIDService.saveSettings(sampleIDService.java:91) 
   at com.sampleid.sampleid.features.shared.BaseActivity.onStop(BaseActivity.java:182) 
   at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1261) 
   at android.app.Activity.performStop(Activity.java:6089) 
   at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3341) 
   at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3390) 
   at android.app.ActivityThread.access$1100(ActivityThread.java:144) 
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1307) 
   at android.os.Handler.dispatchMessage(Handler.java:102) 
   at android.os.Looper.loop(Looper.java:135) 
   at android.app.ActivityThread.main(ActivityThread.java:5221) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at java.lang.reflect.Method.invoke(Method.java:372) 
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 

Solution

  • Was finally able to solve this. The issue is with the following:

    KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, AndroidKeyStore);
    

    KeyProperties.KEY_ALGORITHM_RSA is only available starting API 23, so it fails in APIs before that. Replace it with "RSA"

    Also, if you're using SpongyCastle, it should be added using the following approach:

    static {
            // as per https://github.com/rtyley/spongycastle/issues/27
            Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
        }