diff --git a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java index cbbc54e42e127d878f685f57ab3f0b3a80a3f07c..5ec36fd7287070564c95e5f15e20c0933a8ad5cf 100644 --- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -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 @@ public final class JceKeyStore extends KeyStoreSpi { if (computed[i] != actual[i]) { throw new IOException( "Keystore was tampered with, or " - + "password was incorrect"); + + "password was incorrect", + new UnrecoverableKeyException( + "Password verification failed")); } } } diff --git a/src/share/classes/sun/security/pkcs11/P11KeyStore.java b/src/share/classes/sun/security/pkcs11/P11KeyStore.java index 32002c2885a664f616ab327793889f3f5cdb101a..e5b8fa8d80aac0b72fb9fe650f2521be6a7dadd9 100644 --- a/src/share/classes/sun/security/pkcs11/P11KeyStore.java +++ b/src/share/classes/sun/security/pkcs11/P11KeyStore.java @@ -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 @@ final class P11KeyStore extends KeyStoreSpi { } 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 @@ final class P11KeyStore extends KeyStoreSpi { if (debug != null) { dumpTokenMap(); } - } catch (LoginException | KeyStoreException | PKCS11Exception e) { + } catch (KeyStoreException | PKCS11Exception e) { throw new IOException("load failed", e); } } diff --git a/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index 25fc78c24922d65a14db1b771208125eb3eef4fb..dd3e060fabdac4d1471247aefec2d644927641e8 100644 --- a/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -2051,7 +2051,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } if (!MessageDigest.isEqual(macData.getDigest(), macResult)) { - throw new SecurityException("Failed PKCS12" + + throw new UnrecoverableKeyException("Failed PKCS12" + " integrity checking"); } } catch (Exception e) { diff --git a/test/java/security/KeyStore/TestKeyStoreBasic.java b/test/java/security/KeyStore/TestKeyStoreBasic.java new file mode 100644 index 0000000000000000000000000000000000000000..ea7c1f4a1978528ad9568e59b55195681c98e9c3 --- /dev/null +++ b/test/java/security/KeyStore/TestKeyStoreBasic.java @@ -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; + } + +} diff --git a/test/sun/security/provider/KeyStore/DKSTest.java b/test/sun/security/provider/KeyStore/DKSTest.java index a11210e7d47178f779a4792d4a672a4baa7e94c5..39e4e1844e7d7c2063fdcc8a05025d17e632dde0 100644 --- a/test/sun/security/provider/KeyStore/DKSTest.java +++ b/test/sun/security/provider/KeyStore/DKSTest.java @@ -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,7 +60,37 @@ public class DKSTest { new KeyStore.PasswordProtection("passphrase".toCharArray())); }}; + private static final Map + WRONG_PASSWORDS = new HashMap() {{ + 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 */ @@ -182,4 +212,15 @@ public class DKSTest { 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; + } }