1 /* 2 * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.BufferedInputStream; 25 import java.io.ByteArrayInputStream; 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.security.KeyFactory; 30 import java.security.KeyStore; 31 import java.security.KeyStoreException; 32 import java.security.NoSuchProviderException; 33 import java.security.PrivateKey; 34 import java.security.UnrecoverableKeyException; 35 import java.security.cert.Certificate; 36 import java.security.cert.CertificateFactory; 37 import java.security.spec.KeySpec; 38 import java.security.spec.PKCS8EncodedKeySpec; 39 import java.util.Base64; 40 41 /* 42 * @test 43 * @bug 8048621 8133090 44 * @summary Test basic operations with keystores (jks, jceks, pkcs12) 45 * @author Yu-Ching Valerie PENG 46 */ 47 public class TestKeyStoreBasic { 48 49 private static final String PRIVATE_KEY_PKCS8_BASE64 = "" 50 + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpyz97liuWPDYcLH9TX8BiT78o" 51 + "lCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgKmLhuczF3M9VIcWr+" 52 + "JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz7leikne7KmclHvTfvFd0WDI7Gb9v" 53 + "o4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXRv5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfF" 54 + "e1DDsMg/KpKGiILYZ+g2qtVMZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e" 55 + "+sO6H24w2F19AgMBAAECggEBAId/12187dO6wUPCjumuJA1QrrBnbKdKONyai36uoc1Od4s5QFj7" 56 + "+hEIeS7rbGNYQuBvnkgusAbzkW0FIpxpHce3EJez/emux6pEOKoP77BwMt9gy+txyu0+BHi91FQg" 57 + "AGvrnQDO5EYVY4Cz/WjOsJzKu8zVLg+DS0Toa2qRFwmUe9mVAXPNOCZ3Oae/Q6tCDsaINNw0fmjj" 58 + "jn6uohPbS+n6xENG3FkQXB36getXy310xTGED2J27cmAQH6gLR6Kl2iROzNPbbpBqbuemI9kbcld" 59 + "EwBS1jRfZWeaPstYA1niVrE9UgUBzemnoh4TDkG076sYthHMr5QFGjPswnwtJ4ECgYEA0sURQ5+v" 60 + "baH4tdaemI3qpnknXTlzSpuZZmAoyvY0Id0mlduwKwmZ3Y5989wHfnnhFfyNO4IkTKjI2Wp97qP5" 61 + "4eqUNpA7FtNU7KUzMcFDTtwtNZuRYMrKlqo2lLbA+gVrAYpYZFL4b7tcwtX4DnYorDsmude6W8sG" 62 + "4Mx2VdFJC9UCgYEAzjsdXCYH5doWUHb0dvn9ID7IikffEMRM720MRjrnnnVbpzx6ACntkPDNZg7p" 63 + "TRE/mx7iBz81ZaUWE+V0wd0JvCHEdpAz3mksyvDFhU4Bgs6xzf2pSul5muhsx3hHcvvPezz5Bnxs" 64 + "faJlzkxfwotyGmvWN15GA/pyfsZjsbbTpwkCgYAO6NnbysQCIV8SnegCKqfatt9N/O5m7LLhRxQb" 65 + "p2bwrlA4cZ34rWkw/w9x3LK7A6wkfgUPnJkswxPSLXJTG05l6M4rPfCwIKr1Qopojp9QSMr569NQ" 66 + "4YeLOOc7heIIzbFQHpU6I5Rncv2Q2sn9W+ZsqJKIuvX34FjQNiZ406EzMQKBgHSxOGS61D84DuZK" 67 + "2Ps1awhC3kB4eHzJRms3vflDPWoJJ+pSKwpKrzUTPHXiPBqyhtYkPGszVeiE6CAr9sv3YZnFVaBs" 68 + "6hyQUJsob+uE/w/gGvXe8VsFDx0bJOodYfhrCbTHBHWqE81nBcocpxayxsayfAzqWB3KKd0YLrMR" 69 + "K2PZAoGAcZa8915R2m0KZ6HVJUt/JDR85jCbN71kcVDFY2XSFkOJvOdFoHNfRckfLzjq9Y2MSSTV" 70 + "+QDWbDo2doUQCejJUTaN8nP79tfyir24X5uVPvQaeVoGTKYb+LfUqK0F60lStmjuddIGSZH55y3v" 71 + "+9XjmxbVERtd1lqgQg3VlmKlEXY="; 72 73 /* 74 * Certificate: 75 * Data: 76 * Version: 3 (0x2) 77 * Serial Number: 7 (0x7) 78 * Signature Algorithm: sha512WithRSAEncryption 79 * Issuer: CN=Root 80 * Validity 81 * Not Before: Sep 1 18:03:59 2015 GMT 82 * Not After : Jan 17 18:03:59 2043 GMT 83 * Subject: CN=EE 84 */ 85 private static final String CERTIFICATE = "" 86 + "-----BEGIN CERTIFICATE-----\n" 87 + "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n" 88 + "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n" 89 + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n" 90 + "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n" 91 + "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n" 92 + "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n" 93 + "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n" 94 + "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n" 95 + "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n" 96 + "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n" 97 + "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n" 98 + "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n" 99 + "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n" 100 + "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n" 101 + "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n" 102 + "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n" 103 + "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n" 104 + "-----END CERTIFICATE-----\n"; 105 106 private static final char[] PASSWD2 = new char[] { 107 'b', 'o', 'r', 'e', 'd' 108 }; 109 private static final char[] PASSWDK = "cannot be null" 110 .toCharArray(); 111 private static final String[] KS_Type = { 112 "jks", "jceks", "pkcs12", "PKCS11KeyStore" 113 }; 114 private static final String[] PROVIDERS = { 115 "SUN", "SunJCE", "SunJSSE", "SunPKCS11-Solaris" 116 }; 117 private static final String ALIAS_HEAD = "test"; 118 119 public static void main(String args[]) throws Exception { 120 TestKeyStoreBasic jstest = new TestKeyStoreBasic(); 121 jstest.run(); 122 } 123 124 public void run() throws Exception { 125 for (String provider : PROVIDERS) { 126 try { 127 runTest(provider); 128 System.out.println("Test with provider " + provider + "passed"); 129 } catch (java.security.KeyStoreException e) { 130 if (provider.equals("SunPKCS11-Solaris")) { 131 System.out.println("KeyStoreException is expected: " 132 + "PKCS11KeyStore is invalid keystore type: " + e); 133 } else { 134 throw e; 135 } 136 } catch (NoSuchProviderException e) { 137 String osName = System.getProperty("os.name"); 138 if (provider.equals("SunPKCS11-Solaris") 139 && !osName.equals("SunOS")) { 140 System.out.println("Skip SunPKCS11-Solaris provider on " 141 + osName); 142 } else { 143 throw e; 144 } 145 } 146 } 147 } 148 149 public void runTest(String provider) throws Exception { 150 151 // load private key 152 // all keystore types should support private keys 153 KeySpec spec = new PKCS8EncodedKeySpec( 154 Base64.getMimeDecoder().decode(PRIVATE_KEY_PKCS8_BASE64)); 155 PrivateKey privateKey = KeyFactory.getInstance("RSA") 156 .generatePrivate(spec); 157 158 // load x509 certificate 159 Certificate cert; 160 try (InputStream is = new BufferedInputStream( 161 new ByteArrayInputStream(CERTIFICATE.getBytes()))) { 162 cert = CertificateFactory.getInstance("X.509") 163 .generateCertificate(is); 164 } 165 166 int numEntries = 5; 167 String type = null; 168 for (int i = 0; i < PROVIDERS.length; i++) { 169 if (provider.compareTo(PROVIDERS[i]) == 0) { 170 type = KS_Type[i]; 171 break; 172 } 173 } 174 175 System.out.printf("Test %s provider and %s keystore%n", provider, type); 176 KeyStore ks = KeyStore.getInstance(type, provider); 177 KeyStore ks2 = KeyStore.getInstance(type, ks.getProvider().getName()); 178 179 // create an empty key store 180 ks.load(null, null); 181 182 // store the secret keys 183 for (int j = 0; j < numEntries; j++) { 184 ks.setKeyEntry(ALIAS_HEAD + j, privateKey, PASSWDK, 185 new Certificate[] { cert }); 186 } 187 188 // initialize the 2nd key store object with the 1st one 189 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 190 ks.store(baos, PASSWDK); 191 byte[] bArr = baos.toByteArray(); 192 ByteArrayInputStream bais = new ByteArrayInputStream(bArr); 193 ks2.load(bais, null); 194 195 // check 2nd key store type 196 checkType(ks2, type); 197 // check the existing aliases for the 2nd key store 198 checkAlias(ks2, numEntries); 199 200 // compare the creation date of the 2 key stores for all aliases 201 compareCreationDate(ks, ks2, numEntries); 202 // remove the last entry from the 2nd key store 203 numEntries--; 204 ks2.deleteEntry(ALIAS_HEAD + numEntries); 205 206 // re-initialize the 1st key store with the 2nd key store 207 baos.reset(); 208 ks2.store(baos, PASSWD2); 209 bais = new ByteArrayInputStream(baos.toByteArray()); 210 try { 211 // expect an exception since the password is incorrect 212 ks.load(bais, PASSWDK); 213 throw new RuntimeException( 214 "ERROR: passed the loading with incorrect password"); 215 } catch (IOException ex) { 216 System.out.println("Expected exception: " + ex); 217 if (!causedBy(ex, UnrecoverableKeyException.class)) { 218 ex.printStackTrace(System.out); 219 throw new RuntimeException("Unexpected cause"); 220 } 221 System.out.println("Expected cause: " 222 + UnrecoverableKeyException.class.getName()); 223 224 bais.reset(); 225 ks.load(bais, PASSWD2); 226 bais.reset(); 227 ks.load(bais, null); 228 } 229 230 // check key store type 231 checkType(ks, type); 232 233 // check the existing aliases 234 checkAlias(ks, numEntries); 235 236 // compare the creation date of the 2 key stores for all aliases 237 compareCreationDate(ks, ks2, numEntries); 238 239 } 240 241 // check key store type 242 private void checkType(KeyStore obj, String type) { 243 if (!obj.getType().equals(type)) { 244 throw new RuntimeException("ERROR: wrong key store type"); 245 } 246 } 247 248 // check the existing aliases 249 private void checkAlias(KeyStore obj, int range) throws KeyStoreException { 250 for (int k = 0; k < range; k++) { 251 if (!obj.containsAlias(ALIAS_HEAD + k)) { 252 throw new RuntimeException("ERROR: alias (" + k 253 + ") should exist"); 254 } 255 } 256 } 257 258 // compare the creation dates - true if all the same 259 private void compareCreationDate(KeyStore o1, KeyStore o2, int range) 260 throws KeyStoreException { 261 String alias; 262 for (int k = 0; k < range; k++) { 263 alias = ALIAS_HEAD + k; 264 if (!o1.getCreationDate(alias).equals(o2.getCreationDate(alias))) { 265 throw new RuntimeException("ERROR: entry creation time (" + k 266 + ") differs"); 267 } 268 } 269 } 270 271 // checks if an exception was caused by specified exception class 272 private static boolean causedBy(Exception e, Class klass) { 273 Throwable cause = e; 274 while ((cause = cause.getCause()) != null) { 275 if (cause.getClass().equals(klass)) { 276 return true; 277 } 278 } 279 return false; 280 } 281 282 }