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 }