API21 中生成密钥对时无法生成证书异常

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

我一直在尝试解决这个只发生在 Android Lollipop (API 21) 中的异常。我试图寻找其他解决方案,但唯一相关的是 Generate KeyPair with KeyPairGeneratorSpec on API < 23,建议的解决方案无效。

我错过了什么吗?谢谢!

这是我在最后一行抛出的代码:

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

这是正在发生的异常:

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.access00(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.access00(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) 

终于解决了。问题出在以下方面:

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

KeyProperties.KEY_ALGORITHM_RSA 仅在 API 23 开始可用,因此在此之前的 API 秒内失败。将其替换为 "RSA"

此外,如果您使用的是 SpongyCastle,则应使用以下方法添加它:

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