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 8167371 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 private static final String CRYPTO_ALG = "PBEWithHmacSHA256AndAES_128"; 120 121 public static void main(String args[]) throws Exception { 122 TestKeyStoreBasic jstest = new TestKeyStoreBasic(); 123 jstest.run(); 124 } 125 126 public void run() throws Exception { 127 for (String provider : PROVIDERS) { 128 try { 129 runTest(provider); 130 System.out.println("Test with provider " + provider + " passed"); 131 } catch (java.security.KeyStoreException e) { 132 if (provider.equals("SunPKCS11-Solaris")) { 133 System.out.println("KeyStoreException is expected: " 134 + "PKCS11KeyStore is invalid keystore type: " + e); 135 } else { 136 throw e; 137 } 138 } catch (NoSuchProviderException e) { 139 String osName = System.getProperty("os.name"); 140 if (provider.equals("SunPKCS11-Solaris") 141 && !osName.equals("SunOS")) { 142 System.out.println("Skip SunPKCS11-Solaris provider on " 143 + osName); 144 } else { 145 throw e; 146 } 147 } 148 } 149 } 150 151 public void runTest(String provider) throws Exception { 152 153 // load private key 154 // all keystore types should support private keys 155 KeySpec spec = new PKCS8EncodedKeySpec( 156 Base64.getMimeDecoder().decode(PRIVATE_KEY_PKCS8_BASE64)); 157 PrivateKey privateKey = KeyFactory.getInstance("RSA") 158 .generatePrivate(spec); 159 160 // load x509 certificate 161 Certificate cert; 162 try (InputStream is = new BufferedInputStream( 163 new ByteArrayInputStream(CERTIFICATE.getBytes()))) { 164 cert = CertificateFactory.getInstance("X.509") 165 .generateCertificate(is); 166 } 167 168 int numEntries = 5; 169 String type = null; 170 for (int i = 0; i < PROVIDERS.length; i++) { 171 if (provider.compareTo(PROVIDERS[i]) == 0) { 172 type = KS_Type[i]; 173 break; 174 } 175 } 176 177 System.out.printf("Test %s provider and %s keystore%n", provider, type); 178 KeyStore ks = KeyStore.getInstance(type, provider); 179 KeyStore ks2 = KeyStore.getInstance(type, ks.getProvider().getName()); 180 181 // create an empty key store 182 ks.load(null, null); 183 184 // store the secret keys 185 for (int j = 0; j < numEntries; j++) { 186 ks.setKeyEntry(ALIAS_HEAD + j, privateKey, PASSWDK, 187 new Certificate[] { cert }); 188 } 189 190 // initialize the 2nd key store object with the 1st one 191 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 192 ks.store(baos, PASSWDK); 193 byte[] bArr = baos.toByteArray(); 194 ByteArrayInputStream bais = new ByteArrayInputStream(bArr); 195 ks2.load(bais, null); 196 197 // check 2nd key store type 198 checkType(ks2, type); 199 // check the existing aliases for the 2nd key store 200 checkAlias(ks2, numEntries); 201 202 // compare the creation date of the 2 key stores for all aliases 203 compareCreationDate(ks, ks2, numEntries); 204 // remove the last entry from the 2nd key store 205 numEntries--; 206 ks2.deleteEntry(ALIAS_HEAD + numEntries); 207 208 // re-initialize the 1st key store with the 2nd key store 209 baos.reset(); 210 ks2.store(baos, PASSWD2); 211 bais = new ByteArrayInputStream(baos.toByteArray()); 212 try { 213 // expect an exception since the password is incorrect 214 ks.load(bais, PASSWDK); 215 throw new RuntimeException( 216 "ERROR: passed the loading with incorrect password"); 217 } catch (IOException ex) { 218 System.out.println("Expected exception: " + ex); 219 if (!causedBy(ex, UnrecoverableKeyException.class)) { 220 ex.printStackTrace(System.out); 221 throw new RuntimeException("Unexpected cause"); 222 } 223 System.out.println("Expected cause: " 224 + UnrecoverableKeyException.class.getName()); 225 226 bais.reset(); 227 ks.load(bais, PASSWD2); 228 bais.reset(); 229 ks.load(bais, null); 230 } 231 232 // check key store type 233 checkType(ks, type); 234 235 // check the existing aliases 236 checkAlias(ks, numEntries); 237 238 // compare the creation date of the 2 key stores for all aliases 239 compareCreationDate(ks, ks2, numEntries); 240 241 // check setEntry/getEntry with a password protection algorithm 242 if ("PKCS12".equalsIgnoreCase(ks.getType())) { 243 System.out.println( 244 "Skipping the setEntry/getEntry check for PKCS12 keystore..."); 245 return; 246 } 247 String alias = ALIAS_HEAD + ALIAS_HEAD; 248 KeyStore.PasswordProtection pw = 249 new KeyStore.PasswordProtection(PASSWD2, CRYPTO_ALG, null); 250 KeyStore.PrivateKeyEntry entry = 251 new KeyStore.PrivateKeyEntry(privateKey, new Certificate[]{ cert }); 252 checkSetEntry(ks, alias, pw, entry); 253 ks.setEntry(alias, entry, new KeyStore.PasswordProtection(PASSWD2)); 254 checkGetEntry(ks, alias, pw); 255 } 256 257 // check setEntry with a password protection algorithm 258 private void checkSetEntry(KeyStore ks, String alias, 259 KeyStore.PasswordProtection pw, KeyStore.Entry entry) throws Exception { 260 try { 261 ks.setEntry(alias, entry, pw); 262 throw new Exception( 263 "ERROR: expected KeyStore.setEntry to throw an exception"); 264 } catch (KeyStoreException e) { 265 // ignore the expected exception 266 } 267 } 268 269 // check getEntry with a password protection algorithm 270 private void checkGetEntry(KeyStore ks, String alias, 271 KeyStore.PasswordProtection pw) throws Exception { 272 try { 273 ks.getEntry(alias, pw); 274 throw new Exception( 275 "ERROR: expected KeyStore.getEntry to throw an exception"); 276 } catch (KeyStoreException e) { 277 // ignore the expected exception 278 } 279 } 280 281 // check key store type 282 private void checkType(KeyStore obj, String type) { 283 if (!obj.getType().equals(type)) { 284 throw new RuntimeException("ERROR: wrong key store type"); 285 } 286 } 287 288 // check the existing aliases 289 private void checkAlias(KeyStore obj, int range) throws KeyStoreException { 290 for (int k = 0; k < range; k++) { 291 if (!obj.containsAlias(ALIAS_HEAD + k)) { 292 throw new RuntimeException("ERROR: alias (" + k 293 + ") should exist"); 294 } 295 } 296 } 297 298 // compare the creation dates - true if all the same 299 private void compareCreationDate(KeyStore o1, KeyStore o2, int range) 300 throws KeyStoreException { 301 String alias; 302 for (int k = 0; k < range; k++) { 303 alias = ALIAS_HEAD + k; 304 if (!o1.getCreationDate(alias).equals(o2.getCreationDate(alias))) { 305 throw new RuntimeException("ERROR: entry creation time (" + k 306 + ") differs"); 307 } 308 } 309 } 310 311 // checks if an exception was caused by specified exception class 312 private static boolean causedBy(Exception e, Class klass) { 313 Throwable cause = e; 314 while ((cause = cause.getCause()) != null) { 315 if (cause.getClass().equals(klass)) { 316 return true; 317 } 318 } 319 return false; 320 } 321 322 }