8202299: Java Keystore fails to load PKCS12/PFX certificates created in WindowsServer2016

Reviewed-by: mullan, xuelei
This commit is contained in:
Weijun Wang 2018-06-26 10:43:50 +08:00
parent 3ad65642c8
commit 230268522b
2 changed files with 119 additions and 47 deletions

View File

@ -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
@ -281,6 +281,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
}
/**
* 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 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
}
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 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
" 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 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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();
if (debug != null) {
debug.println("Checking keystore integrity " +
"(" + m.getAlgorithm() + " iterations: " + ic + ")");
}
RetryWithZero.run(pass -> {
SecretKey key = getPBEKey(pass);
m.init(key, params);
m.update(authSafeData);
byte[] macResult = m.doFinal();
if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
throw new UnrecoverableKeyException("Failed PKCS12" +
" integrity checking");
}
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");
}
return (Void)null;
}, password);
} catch (Exception e) {
throw new IOException("Integrity check failed: " + e, e);
}

View File

@ -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);
}
}