8202299: Java Keystore fails to load PKCS12/PFX certificates created in WindowsServer2016
Reviewed-by: mullan, xuelei
--- a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Tue Jun 26 10:43:43 2018 +0800
+++ b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Tue Jun 26 10:43:50 2018 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -282,6 +282,28 @@
}
/**
+ * Retries an action with password "\0" if "" fails.
+ * @param <T> the return type
+ */
+ @FunctionalInterface
+ private interface RetryWithZero<T> {
+
+ T tryOnce(char[] password) throws Exception;
+
+ static <S> S run(RetryWithZero<S> f, char[] password) throws Exception {
+ try {
+ return f.tryOnce(password);
+ } catch (Exception e) {
+ if (password.length == 0) {
+ // Retry using an empty password with a NUL terminator.
+ return f.tryOnce(new char[1]);
+ }
+ throw e;
+ }
+ }
+ }
+
+ /**
* Private keys and certificates are stored in a map.
* Map entries are keyed by alias names.
*/
@@ -370,26 +392,14 @@
}
}
- byte[] keyInfo;
- while (true) {
- try {
- // Use JCE
- SecretKey skey = getPBEKey(password);
- Cipher cipher = Cipher.getInstance(
+ byte[] keyInfo = RetryWithZero.run(pass -> {
+ // Use JCE
+ SecretKey skey = getPBEKey(pass);
+ Cipher cipher = Cipher.getInstance(
mapPBEParamsToAlgorithm(algOid, algParams));
- cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
- keyInfo = cipher.doFinal(encryptedKey);
- break;
- } catch (Exception e) {
- if (password.length == 0) {
- // Retry using an empty password
- // without a NULL terminator.
- password = new char[1];
- continue;
- }
- throw e;
- }
- }
+ cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
+ return cipher.doFinal(encryptedKey);
+ }, password);
/*
* Parse the key algorithm and then use a JCA key factory
@@ -2079,25 +2089,19 @@
" iterations: " + ic + ")");
}
- while (true) {
- try {
+ byte[] rawData = safeContentsData;
+ try {
+ safeContentsData = RetryWithZero.run(pass -> {
// Use JCE
- SecretKey skey = getPBEKey(password);
+ SecretKey skey = getPBEKey(pass);
Cipher cipher = Cipher.getInstance(algOid.toString());
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
- safeContentsData = cipher.doFinal(safeContentsData);
- break;
- } catch (Exception e) {
- if (password.length == 0) {
- // Retry using an empty password
- // without a NULL terminator.
- password = new char[1];
- continue;
- }
- throw new IOException("keystore password was incorrect",
+ return cipher.doFinal(rawData);
+ }, password);
+ } catch (Exception e) {
+ throw new IOException("keystore password was incorrect",
new UnrecoverableKeyException(
- "failed to decrypt safe contents entry: " + e));
- }
+ "failed to decrypt safe contents entry: " + e));
}
} else {
throw new IOException("public key protected PKCS12" +
@@ -2128,20 +2132,24 @@
Mac m = Mac.getInstance("HmacPBE" + algName);
PBEParameterSpec params =
new PBEParameterSpec(macData.getSalt(), ic);
- SecretKey key = getPBEKey(password);
- m.init(key, params);
- m.update(authSafeData);
- byte[] macResult = m.doFinal();
+
+ RetryWithZero.run(pass -> {
+ SecretKey key = getPBEKey(pass);
+ m.init(key, params);
+ m.update(authSafeData);
+ byte[] macResult = m.doFinal();
- if (debug != null) {
- debug.println("Checking keystore integrity " +
- "(" + m.getAlgorithm() + " iterations: " + ic + ")");
- }
+ if (debug != null) {
+ debug.println("Checking keystore integrity " +
+ "(" + m.getAlgorithm() + " iterations: " + ic + ")");
+ }
- if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
- throw new UnrecoverableKeyException("Failed PKCS12" +
- " integrity checking");
- }
+ if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
+ throw new UnrecoverableKeyException("Failed PKCS12" +
+ " integrity checking");
+ }
+ return (Void)null;
+ }, password);
} catch (Exception e) {
throw new IOException("Integrity check failed: " + e, e);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/pkcs12/EmptyPassword.java Tue Jun 26 10:43:50 2018 +0800
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8202299
+ * @modules java.base/sun.security.tools.keytool
+ * java.base/sun.security.x509
+ * @library /test/lib
+ * @summary Java Keystore fails to load PKCS12/PFX certificates created in WindowsServer2016
+ */
+
+import jdk.test.lib.Asserts;
+import sun.security.tools.keytool.CertAndKeyGen;
+import sun.security.x509.X500Name;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+
+public class EmptyPassword {
+
+ public static void main(String[] args) throws Exception {
+
+ // KeyStore is protected with password "\0".
+ CertAndKeyGen gen = new CertAndKeyGen("RSA", "SHA256withRSA");
+ gen.generate(2048);
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+ ks.setKeyEntry("a", gen.getPrivateKey(), new char[1],
+ new Certificate[] {
+ gen.getSelfCertificate(new X500Name("CN=Me"), 100)
+ });
+ try (FileOutputStream fos = new FileOutputStream("p12")) {
+ ks.store(fos, new char[1]);
+ }
+
+ // It can be loaded with password "".
+ ks = KeyStore.getInstance(new File("p12"), new char[0]);
+ Asserts.assertTrue(ks.getKey("a", new char[0]) != null);
+ Asserts.assertTrue(ks.getCertificate("a") != null);
+ }
+}