Я сгенерировал хранилище ключей с помощью следующей команды:
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()));
}
Тест проходит.
Теперь я делаю то же самое, но сначала я добавляю провайдера BouncyCastle в позицию 2 (на основе 1):
@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 и с ним).Код, используемый в этом случае (не 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 (и работает правильно);в сценарии «с BC» экземпляр Cipher
предоставляется BC и дает сбой.
Вот список поставщиков после установки BouncyCastle:
SUN
BC
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
То есть BC доSunJCE, следовательно, он побеждает в конкурсе и обеспечивает Cipher
.
К сожалению, почти невозможно избежать того, чтобы «загрузить PKCS12 как JKS»;также я хотел бы избежать перемещения провайдера BouncyCastle в конец списка провайдеров, так как это может привести к поломке другого кода.
Вопросы:
- Это ошибка?в BouncyCastle?
- Есть ли способ обойти эту проблему?
Java - Oracle JDK 1.8.0_161, версия BouncyCastle - 1.54 (Я также попробовал 1.57, который, кажется, самая последняя версия, но я получаю те же результаты).