我使用以下命令生成了密钥库:
keytool -genkeypair -keystore test.ks -storetype pkcs12
然后我运行以下测试(base64代表我创建的那个密钥库):
private static final String KEYSTORE_BASE64 = "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";
@Test
public void testWithoutBouncyCastle() throws Exception {
doTest();
}
private void doTest() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
byte[] keyStoreBytes = Base64.getDecoder().decode(KEYSTORE_BASE64);
KeyStore keystore = KeyStore.getInstance("jks");
keystore.load(new ByteArrayInputStream(keyStoreBytes), "secret".toCharArray());
Key key = keystore.getKey("mykey", "secret".toCharArray());
assertThat(key, is(notNullValue()));
}
测试通过。
现在我做同样的事情,但是首先我在位置2(从1开始)添加BouncyCastle提供程序:
@Test
public void testWithBouncyCastle() throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 2);
doTest();
}
测试失败:
java.io.IOException: keystore password was incorrect
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2059)
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:238)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70)
at java.security.KeyStore.load(KeyStore.java:1445)
at PBETest.doTest(PBETest.java:43)
at PBETest.testWithBouncyCastle(PBETest.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: pad block corrupted
... 28 more
请注意,密钥库是PKCS12存储,我尝试将其作为JKS加载。我必须使用的库也是如此,因此代码也一样。此外,JDK似乎已经准备就绪:对于getInstance("jks")
KeyStoreDelegator
类,该类首先尝试加载作为JKS存储库给出的“内容”,然后退回到PKCS12路径。
另一个观察结果是,getInstance("pkcs12")
在两种情况下都可以正常工作(不使用BouncyCastle或不使用BouncyCastle)。在这种情况下使用的代码(不是KeyStoreDelegator
)与JKS场景中与pkcs12相关的路径不同,并且可能是因为它可以成功运行。
当PKCS12相关分支尝试使用密钥库密码解密某些内容时,就会出现问题。为此,首先将密钥库密码转换为PBE密钥
private SecretKey getPBEKey(char[] var1) throws IOException {
SecretKey var2 = null;
try {
PBEKeySpec var3 = new PBEKeySpec(var1);
SecretKeyFactory var4 = SecretKeyFactory.getInstance("PBE");
var2 = var4.generateSecret(var3);
var3.clearPassword();
return var2;
} catch (Exception var5) {
throw new IOException("getSecretKey failed: " + var5.getMessage(), var5);
}
}
,然后使用此密钥(连同算法参数)进行解密:
SecretKey key = this.getPBEKey(var2);
Cipher cipher = Cipher.getInstance(var27.toString());
cipher.init(2, key, var28);
var38 = cipher.doFinal(var38);
对不起,可怕的变量名,这就是IDE给我的。 var2
是代表密钥库密码的char[]
,var27.toString()
给出'1.2.840.113549.1.12.1.6',var28
包含算法参数。
doFinal()
是失败的呼叫。
在“不使用BC”的情况下,Cipher
实例来自SunJCE提供程序(并且可以正常工作);在“ with BC”情况下,Cipher
实例由BC提供并且失败。
以下是安装BouncyCastle之后的提供程序的列表:
SUN
BC
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
因此BC在SunJCE之前,因此赢得了比赛并提供了Cipher
。
不幸的是,几乎不可能避免“将PKCS12加载为JKS”的做法;我也想避免将BouncyCastle提供程序移动到提供程序列表的末尾,因为这可能会破坏其他一些代码。
问题是:
Java是Oracle JDK 1.8.0_161,BouncyCastle版本是1.54(我也尝试过1.57,这似乎是最新版本,但得到的结果相同)。