8134232: KeyStore.load() throws an IOException with a wrong cause in case of wrong password
Reviewed-by: vinnie
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java Sun Sep 13 10:55:58 2015 +0800
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java Mon Sep 14 19:49:28 2015 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, 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
@@ -864,7 +864,9 @@
if (computed[i] != actual[i]) {
throw new IOException(
"Keystore was tampered with, or "
- + "password was incorrect");
+ + "password was incorrect",
+ new UnrecoverableKeyException(
+ "Password verification failed"));
}
}
}
--- a/jdk/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Sun Sep 13 10:55:58 2015 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Mon Sep 14 19:49:28 2015 +0300
@@ -51,6 +51,8 @@
import java.util.*;
import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
@@ -2060,7 +2062,7 @@
}
if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
- throw new SecurityException("Failed PKCS12" +
+ throw new UnrecoverableKeyException("Failed PKCS12" +
" integrity checking");
}
} catch (Exception e) {
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java Sun Sep 13 10:55:58 2015 +0800
+++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11KeyStore.java Mon Sep 14 19:49:28 2015 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, 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
@@ -750,6 +750,21 @@
} else {
login(new PasswordCallbackHandler(password));
}
+ } catch(LoginException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof PKCS11Exception) {
+ PKCS11Exception pe = (PKCS11Exception) cause;
+ if (pe.getErrorCode() == CKR_PIN_INCORRECT) {
+ // if password is wrong, the cause of the IOException
+ // should be an UnrecoverableKeyException
+ throw new IOException("load failed",
+ new UnrecoverableKeyException().initCause(e));
+ }
+ }
+ throw new IOException("load failed", e);
+ }
+
+ try {
if (mapLabels() == true) {
// CKA_LABELs are shared by multiple certs
writeDisabled = true;
@@ -757,7 +772,7 @@
if (debug != null) {
dumpTokenMap();
}
- } catch (LoginException | KeyStoreException | PKCS11Exception e) {
+ } catch (KeyStoreException | PKCS11Exception e) {
throw new IOException("load failed", e);
}
}
--- a/jdk/test/java/security/KeyStore/TestKeyStoreBasic.java Sun Sep 13 10:55:58 2015 +0800
+++ b/jdk/test/java/security/KeyStore/TestKeyStoreBasic.java Mon Sep 14 19:49:28 2015 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2015, 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
@@ -21,36 +21,97 @@
* questions.
*/
-import static java.lang.System.out;
-
+import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
-import java.security.Provider;
-import java.security.Security;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
/*
* @test
- * @bug 8048621
- * @summary Test the basic operations of KeyStore, provided by SunJCE (jceks),
- * and SunPKCS11-Solaris(PKCS11KeyStore)
+ * @bug 8048621 8133090
+ * @summary Test basic operations with keystores (jks, jceks, pkcs12)
* @author Yu-Ching Valerie PENG
*/
+public class TestKeyStoreBasic {
-public class TestKeyStoreBasic {
+ private static final String PRIVATE_KEY_PKCS8_BASE64 = ""
+ + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpyz97liuWPDYcLH9TX8BiT78o"
+ + "lCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgKmLhuczF3M9VIcWr+"
+ + "JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz7leikne7KmclHvTfvFd0WDI7Gb9v"
+ + "o4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXRv5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfF"
+ + "e1DDsMg/KpKGiILYZ+g2qtVMZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e"
+ + "+sO6H24w2F19AgMBAAECggEBAId/12187dO6wUPCjumuJA1QrrBnbKdKONyai36uoc1Od4s5QFj7"
+ + "+hEIeS7rbGNYQuBvnkgusAbzkW0FIpxpHce3EJez/emux6pEOKoP77BwMt9gy+txyu0+BHi91FQg"
+ + "AGvrnQDO5EYVY4Cz/WjOsJzKu8zVLg+DS0Toa2qRFwmUe9mVAXPNOCZ3Oae/Q6tCDsaINNw0fmjj"
+ + "jn6uohPbS+n6xENG3FkQXB36getXy310xTGED2J27cmAQH6gLR6Kl2iROzNPbbpBqbuemI9kbcld"
+ + "EwBS1jRfZWeaPstYA1niVrE9UgUBzemnoh4TDkG076sYthHMr5QFGjPswnwtJ4ECgYEA0sURQ5+v"
+ + "baH4tdaemI3qpnknXTlzSpuZZmAoyvY0Id0mlduwKwmZ3Y5989wHfnnhFfyNO4IkTKjI2Wp97qP5"
+ + "4eqUNpA7FtNU7KUzMcFDTtwtNZuRYMrKlqo2lLbA+gVrAYpYZFL4b7tcwtX4DnYorDsmude6W8sG"
+ + "4Mx2VdFJC9UCgYEAzjsdXCYH5doWUHb0dvn9ID7IikffEMRM720MRjrnnnVbpzx6ACntkPDNZg7p"
+ + "TRE/mx7iBz81ZaUWE+V0wd0JvCHEdpAz3mksyvDFhU4Bgs6xzf2pSul5muhsx3hHcvvPezz5Bnxs"
+ + "faJlzkxfwotyGmvWN15GA/pyfsZjsbbTpwkCgYAO6NnbysQCIV8SnegCKqfatt9N/O5m7LLhRxQb"
+ + "p2bwrlA4cZ34rWkw/w9x3LK7A6wkfgUPnJkswxPSLXJTG05l6M4rPfCwIKr1Qopojp9QSMr569NQ"
+ + "4YeLOOc7heIIzbFQHpU6I5Rncv2Q2sn9W+ZsqJKIuvX34FjQNiZ406EzMQKBgHSxOGS61D84DuZK"
+ + "2Ps1awhC3kB4eHzJRms3vflDPWoJJ+pSKwpKrzUTPHXiPBqyhtYkPGszVeiE6CAr9sv3YZnFVaBs"
+ + "6hyQUJsob+uE/w/gGvXe8VsFDx0bJOodYfhrCbTHBHWqE81nBcocpxayxsayfAzqWB3KKd0YLrMR"
+ + "K2PZAoGAcZa8915R2m0KZ6HVJUt/JDR85jCbN71kcVDFY2XSFkOJvOdFoHNfRckfLzjq9Y2MSSTV"
+ + "+QDWbDo2doUQCejJUTaN8nP79tfyir24X5uVPvQaeVoGTKYb+LfUqK0F60lStmjuddIGSZH55y3v"
+ + "+9XjmxbVERtd1lqgQg3VlmKlEXY=";
+
+ /*
+ * Certificate:
+ * Data:
+ * Version: 3 (0x2)
+ * Serial Number: 7 (0x7)
+ * Signature Algorithm: sha512WithRSAEncryption
+ * Issuer: CN=Root
+ * Validity
+ * Not Before: Sep 1 18:03:59 2015 GMT
+ * Not After : Jan 17 18:03:59 2043 GMT
+ * Subject: CN=EE
+ */
+ private static final String CERTIFICATE = ""
+ + "-----BEGIN CERTIFICATE-----\n"
+ + "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n"
+ + "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n"
+ + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n"
+ + "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n"
+ + "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n"
+ + "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n"
+ + "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n"
+ + "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n"
+ + "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n"
+ + "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n"
+ + "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n"
+ + "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n"
+ + "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n"
+ + "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n"
+ + "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n"
+ + "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n"
+ + "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n"
+ + "-----END CERTIFICATE-----\n";
+
private static final char[] PASSWD2 = new char[] {
'b', 'o', 'r', 'e', 'd'
};
- private static final char[] PASSWDK = new String("cannot be null")
+ private static final char[] PASSWDK = "cannot be null"
.toCharArray();
private static final String[] KS_Type = {
"jks", "jceks", "pkcs12", "PKCS11KeyStore"
};
- private static final String[] PRO_TYPE = {
+ private static final String[] PROVIDERS = {
"SUN", "SunJCE", "SunJSSE", "SunPKCS11-Solaris"
};
private static final String ALIAS_HEAD = "test";
@@ -61,41 +122,58 @@
}
public void run() throws Exception {
- Provider[] providers = Security.getProviders();
- for (Provider p: providers) {
- String prvName = p.getName();
- if (prvName.startsWith("SunJCE")
- || prvName.startsWith("SunPKCS11-Solaris")) {
- try {
- runTest(p);
- out.println("Test with provider " + p.getName() + ""
- + " passed");
- } catch (java.security.KeyStoreException e) {
- if (prvName.startsWith("SunPKCS11-Solaris")) {
- out.println("KeyStoreException is expected "
- + "PKCS11KeyStore is invalid keystore type.");
- e.printStackTrace();
- } else {
- throw e;
- }
+ for (String provider : PROVIDERS) {
+ try {
+ runTest(provider);
+ System.out.println("Test with provider " + provider + "passed");
+ } catch (java.security.KeyStoreException e) {
+ if (provider.equals("SunPKCS11-Solaris")) {
+ System.out.println("KeyStoreException is expected: "
+ + "PKCS11KeyStore is invalid keystore type: " + e);
+ } else {
+ throw e;
+ }
+ } catch (NoSuchProviderException e) {
+ String osName = System.getProperty("os.name");
+ if (provider.equals("SunPKCS11-Solaris")
+ && !osName.equals("SunOS")) {
+ System.out.println("Skip SunPKCS11-Solaris provider on "
+ + osName);
+ } else {
+ throw e;
}
}
}
}
- public void runTest(Provider p) throws Exception {
- SecretKey key = new SecretKeySpec(
- new String("No one knows").getBytes(), "PBE");
+ public void runTest(String provider) throws Exception {
+
+ // load private key
+ // all keystore types should support private keys
+ KeySpec spec = new PKCS8EncodedKeySpec(
+ Base64.getMimeDecoder().decode(PRIVATE_KEY_PKCS8_BASE64));
+ PrivateKey privateKey = KeyFactory.getInstance("RSA")
+ .generatePrivate(spec);
+
+ // load x509 certificate
+ Certificate cert;
+ try (InputStream is = new BufferedInputStream(
+ new ByteArrayInputStream(CERTIFICATE.getBytes()))) {
+ cert = CertificateFactory.getInstance("X.509")
+ .generateCertificate(is);
+ }
+
int numEntries = 5;
- String proName = p.getName();
String type = null;
- for (int i = 0; i < PRO_TYPE.length; i++) {
- if (proName.compareTo(PRO_TYPE[i]) == 0) {
+ for (int i = 0; i < PROVIDERS.length; i++) {
+ if (provider.compareTo(PROVIDERS[i]) == 0) {
type = KS_Type[i];
break;
}
}
- KeyStore ks = KeyStore.getInstance(type, p);
+
+ System.out.printf("Test %s provider and %s keystore%n", provider, type);
+ KeyStore ks = KeyStore.getInstance(type, provider);
KeyStore ks2 = KeyStore.getInstance(type, ks.getProvider().getName());
// create an empty key store
@@ -103,7 +181,8 @@
// store the secret keys
for (int j = 0; j < numEntries; j++) {
- ks.setKeyEntry(ALIAS_HEAD + j, key, PASSWDK, null);
+ ks.setKeyEntry(ALIAS_HEAD + j, privateKey, PASSWDK,
+ new Certificate[] { cert });
}
// initialize the 2nd key store object with the 1st one
@@ -134,13 +213,18 @@
throw new RuntimeException(
"ERROR: passed the loading with incorrect password");
} catch (IOException ex) {
+ System.out.println("Expected exception: " + ex);
+ if (!causedBy(ex, UnrecoverableKeyException.class)) {
+ ex.printStackTrace(System.out);
+ throw new RuntimeException("Unexpected cause");
+ }
+ System.out.println("Expected cause: "
+ + UnrecoverableKeyException.class.getName());
+
bais.reset();
ks.load(bais, PASSWD2);
bais.reset();
ks.load(bais, null);
- } finally {
- bais.close();
- baos.close();
}
// check key store type
@@ -158,7 +242,6 @@
private void checkType(KeyStore obj, String type) {
if (!obj.getType().equals(type)) {
throw new RuntimeException("ERROR: wrong key store type");
-
}
}
@@ -168,7 +251,6 @@
if (!obj.containsAlias(ALIAS_HEAD + k)) {
throw new RuntimeException("ERROR: alias (" + k
+ ") should exist");
-
}
}
}
@@ -176,16 +258,25 @@
// compare the creation dates - true if all the same
private void compareCreationDate(KeyStore o1, KeyStore o2, int range)
throws KeyStoreException {
- boolean result = true;
- String alias = null;
+ String alias;
for (int k = 0; k < range; k++) {
alias = ALIAS_HEAD + k;
if (!o1.getCreationDate(alias).equals(o2.getCreationDate(alias))) {
throw new RuntimeException("ERROR: entry creation time (" + k
+ ") differs");
-
}
}
}
+ // checks if an exception was caused by specified exception class
+ private static boolean causedBy(Exception e, Class klass) {
+ Throwable cause = e;
+ while ((cause = cause.getCause()) != null) {
+ if (cause.getClass().equals(klass)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
--- a/jdk/test/sun/security/provider/KeyStore/DKSTest.java Sun Sep 13 10:55:58 2015 +0800
+++ b/jdk/test/sun/security/provider/KeyStore/DKSTest.java Mon Sep 14 19:49:28 2015 +0300
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -60,8 +60,38 @@
new KeyStore.PasswordProtection("passphrase".toCharArray()));
}};
+ private static final Map<String, KeyStore.ProtectionParameter>
+ WRONG_PASSWORDS = new HashMap<String, KeyStore.ProtectionParameter>() {{
+ put("policy_keystore",
+ new KeyStore.PasswordProtection(
+ "wrong".toCharArray()));
+ put("pw_keystore",
+ new KeyStore.PasswordProtection("wrong".toCharArray()));
+ put("eckeystore1",
+ new KeyStore.PasswordProtection("wrong".toCharArray()));
+ put("eckeystore2",
+ new KeyStore.PasswordProtection("wrong".toCharArray()));
+ }};
+
public static void main(String[] args) throws Exception {
/*
+ * domain keystore: keystores with wrong passwords
+ */
+ try {
+ URI config = new URI(CONFIG + "#keystores");
+ KeyStore ks = KeyStore.getInstance("DKS");
+ ks.load(new DomainLoadStoreParameter(config, WRONG_PASSWORDS));
+ throw new RuntimeException("Expected exception not thrown");
+ } catch (IOException e) {
+ System.out.println("Expected exception: " + e);
+ if (!causedBy(e, UnrecoverableKeyException.class)) {
+ e.printStackTrace(System.out);
+ throw new RuntimeException("Unexpected cause");
+ }
+ System.out.println("Expected cause: " + e);
+ }
+
+ /*
* domain keystore: system
*/
URI config = new URI(CONFIG + "#system");
@@ -182,4 +212,15 @@
return factory.generateCertificate(certStream);
}
}
+
+ // checks if an exception was caused by specified exception class
+ private static boolean causedBy(Exception e, Class klass) {
+ Throwable cause = e;
+ while ((cause = cause.getCause()) != null) {
+ if (cause.getClass().equals(klass)) {
+ return true;
+ }
+ }
+ return false;
+ }
}