changeset 11674:d9878355b466

8134232: KeyStore.load() throws an IOException with a wrong cause in case of wrong password Reviewed-by: vinnie
author bgopularam
date Wed, 18 May 2016 22:27:10 -0700
parents 28fef0942517
children d108c790e561
files src/share/classes/com/sun/crypto/provider/JceKeyStore.java src/share/classes/sun/security/pkcs11/P11KeyStore.java src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java test/java/security/KeyStore/TestKeyStoreBasic.java test/sun/security/provider/KeyStore/DKSTest.java
diffstat 5 files changed, 346 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Wed May 18 18:59:19 2016 +0300
+++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Wed May 18 22:27:10 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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/src/share/classes/sun/security/pkcs11/P11KeyStore.java	Wed May 18 18:59:19 2016 +0300
+++ b/src/share/classes/sun/security/pkcs11/P11KeyStore.java	Wed May 18 22:27:10 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -752,6 +752,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;
@@ -759,7 +774,7 @@
             if (debug != null) {
                 dumpTokenMap();
             }
-        } catch (LoginException | KeyStoreException | PKCS11Exception e) {
+        } catch (KeyStoreException | PKCS11Exception e) {
             throw new IOException("load failed", e);
         }
     }
--- a/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java	Wed May 18 18:59:19 2016 +0300
+++ b/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java	Wed May 18 22:27:10 2016 -0700
@@ -2051,7 +2051,7 @@
                 }
 
                 if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
-                   throw new SecurityException("Failed PKCS12" +
+                   throw new UnrecoverableKeyException("Failed PKCS12" +
                                         " integrity checking");
                 }
            } catch (Exception e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/security/KeyStore/TestKeyStoreBasic.java	Wed May 18 22:27:10 2016 -0700
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2001, 2016, 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.
+ */
+
+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.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 8133090
+ * @summary Test basic operations with keystores (jks, jceks, pkcs12)
+ * @author Yu-Ching Valerie PENG
+ */
+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 = "cannot be null"
+            .toCharArray();
+    private static final String[] KS_Type = {
+            "jks", "jceks", "pkcs12", "PKCS11KeyStore"
+    };
+    private static final String[] PROVIDERS = {
+            "SUN", "SunJCE", "SunJSSE", "SunPKCS11-Solaris"
+    };
+    private static final String ALIAS_HEAD = "test";
+
+    public static void main(String args[]) throws Exception {
+        TestKeyStoreBasic jstest = new TestKeyStoreBasic();
+        jstest.run();
+    }
+
+    public void run() throws Exception {
+        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(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 type = null;
+        for (int i = 0; i < PROVIDERS.length; i++) {
+            if (provider.compareTo(PROVIDERS[i]) == 0) {
+                type = KS_Type[i];
+                break;
+            }
+        }
+
+        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
+        ks.load(null, null);
+
+        // store the secret keys
+        for (int j = 0; j < numEntries; j++) {
+            ks.setKeyEntry(ALIAS_HEAD + j, privateKey, PASSWDK,
+                    new Certificate[] { cert });
+        }
+
+        // initialize the 2nd key store object with the 1st one
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ks.store(baos, PASSWDK);
+        byte[] bArr = baos.toByteArray();
+        ByteArrayInputStream bais = new ByteArrayInputStream(bArr);
+        ks2.load(bais, null);
+
+        // check 2nd key store type
+        checkType(ks2, type);
+        // check the existing aliases for the 2nd key store
+        checkAlias(ks2, numEntries);
+
+        // compare the creation date of the 2 key stores for all aliases
+        compareCreationDate(ks, ks2, numEntries);
+        // remove the last entry from the 2nd key store
+        numEntries--;
+        ks2.deleteEntry(ALIAS_HEAD + numEntries);
+
+        // re-initialize the 1st key store with the 2nd key store
+        baos.reset();
+        ks2.store(baos, PASSWD2);
+        bais = new ByteArrayInputStream(baos.toByteArray());
+        try {
+            // expect an exception since the password is incorrect
+            ks.load(bais, PASSWDK);
+            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);
+        }
+
+        // check key store type
+        checkType(ks, type);
+
+        // check the existing aliases
+        checkAlias(ks, numEntries);
+
+        // compare the creation date of the 2 key stores for all aliases
+        compareCreationDate(ks, ks2, numEntries);
+
+    }
+
+    // check key store type
+    private void checkType(KeyStore obj, String type) {
+        if (!obj.getType().equals(type)) {
+            throw new RuntimeException("ERROR: wrong key store type");
+        }
+    }
+
+    // check the existing aliases
+    private void checkAlias(KeyStore obj, int range) throws KeyStoreException {
+        for (int k = 0; k < range; k++) {
+            if (!obj.containsAlias(ALIAS_HEAD + k)) {
+                throw new RuntimeException("ERROR: alias (" + k
+                        + ") should exist");
+            }
+        }
+    }
+
+    // compare the creation dates - true if all the same
+    private void compareCreationDate(KeyStore o1, KeyStore o2, int range)
+            throws KeyStoreException {
+        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/test/sun/security/provider/KeyStore/DKSTest.java	Wed May 18 18:59:19 2016 +0300
+++ b/test/sun/security/provider/KeyStore/DKSTest.java	Wed May 18 22:27:10 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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;
+    }
 }