1 /*
   2  * Copyright (c) 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 /*
  25  * @test
  26  * @bug 8062552
  27  * @run main/othervm TestKeystoreCompat
  28  * @summary test compatibility mode for JKS and PKCS12 keystores
  29  */
  30 
  31 import java.io.*;
  32 import java.security.*;
  33 import java.security.KeyStore.*;
  34 import java.security.cert.*;
  35 import javax.crypto.*;
  36 import javax.security.auth.callback.*;
  37 
  38 public class TestKeystoreCompat {
  39     private static final char[] PASSWORD = "changeit".toCharArray();
  40     private static final String DIR = System.getProperty("test.src", ".");
  41     private static final String CERT_FILE = "trusted.pem";
  42 
  43     public static final void main(String[] args) throws Exception {
  44 
  45         // Testing empty keystores
  46 
  47         init("empty.jks", "JKS");
  48         init("empty.jceks", "JCEKS");
  49         init("empty.p12", "PKCS12");
  50 
  51         load("empty.jks", "JKS");
  52         load("empty.jceks", "JCEKS");
  53         load("empty.p12", "PKCS12");
  54         load("empty.p12", "JKS"); // test compatibility mode
  55         load("empty.jks", "PKCS12", true); // test without compatibility mode
  56         load("empty.jks", "JKS", false); // test without compatibility mode
  57         load("empty.p12", "JKS", true); // test without compatibility mode
  58         load("empty.p12", "PKCS12", false); // test without compatibility mode
  59 
  60         build("empty.jks", "JKS", true);
  61         build("empty.jks", "JKS", false);
  62         build("empty.jceks", "JCEKS", true);
  63         build("empty.jceks", "JCEKS", false);
  64         build("empty.p12", "PKCS12", true);
  65         build("empty.p12", "PKCS12", false);
  66 
  67         // Testing keystores containing an X.509 certificate
  68 
  69         X509Certificate cert = loadCertificate(CERT_FILE);
  70         init("onecert.jks", "JKS", cert);
  71         init("onecert.jceks", "JCEKS", cert);
  72         init("onecert.p12", "PKCS12", cert);
  73 
  74         load("onecert.jks", "JKS");
  75         load("onecert.jceks", "JCEKS");
  76         load("onecert.p12", "PKCS12");
  77         load("onecert.p12", "JKS"); // test compatibility mode
  78         load("onecert.jks", "PKCS12", true); // test without compatibility mode
  79         load("onecert.jks", "JKS", false); // test without compatibility mode
  80         load("onecert.p12", "JKS", true); // test without compatibility mode
  81         load("onecert.p12", "PKCS12", false); // test without compatibility mode
  82 
  83         build("onecert.jks", "JKS", true);
  84         build("onecert.jks", "JKS", false);
  85         build("onecert.jceks", "JCEKS", true);
  86         build("onecert.jceks", "JCEKS", false);
  87         build("onecert.p12", "PKCS12", true);
  88         build("onecert.p12", "PKCS12", false);
  89 
  90         // Testing keystores containing a secret key
  91 
  92         SecretKey key = generateSecretKey("AES", 128);
  93         init("onekey.jceks", "JCEKS", key);
  94         init("onekey.p12", "PKCS12", key);
  95 
  96         load("onekey.jceks", "JCEKS");
  97         load("onekey.p12", "PKCS12");
  98         load("onekey.p12", "JKS"); // test compatibility mode
  99         load("onekey.p12", "JKS", true); // test without compatibility mode
 100         load("onekey.p12", "PKCS12", false); // test without compatibility mode
 101 
 102         build("onekey.jceks", "JCEKS", true);
 103         build("onekey.jceks", "JCEKS", false);
 104         build("onekey.p12", "PKCS12", true);
 105         build("onekey.p12", "PKCS12", false);
 106 
 107         System.out.println("OK.");
 108     }
 109 
 110     // Instantiate an empty keystore using the supplied keystore type
 111     private static void init(String file, String type) throws Exception {
 112         KeyStore ks = KeyStore.getInstance(type);
 113         ks.load(null, null);
 114         try (OutputStream stream = new FileOutputStream(file)) {
 115             ks.store(stream, PASSWORD);
 116         }
 117         System.out.println("Created a " + type + " keystore named '" + file + "'");
 118     }
 119 
 120     // Instantiate a keystore using the supplied keystore type & create an entry
 121     private static void init(String file, String type, X509Certificate cert)
 122         throws Exception {
 123         KeyStore ks = KeyStore.getInstance(type);
 124         ks.load(null, null);
 125         ks.setEntry("mycert", new KeyStore.TrustedCertificateEntry(cert), null);
 126         try (OutputStream stream = new FileOutputStream(file)) {
 127             ks.store(stream, PASSWORD);
 128         }
 129         System.out.println("Created a " + type + " keystore named '" + file + "'");
 130     }
 131 
 132     // Instantiate a keystore using the supplied keystore type & create an entry
 133     private static void init(String file, String type, SecretKey key)
 134         throws Exception {
 135         KeyStore ks = KeyStore.getInstance(type);
 136         ks.load(null, null);
 137         ks.setEntry("mykey", new KeyStore.SecretKeyEntry(key),
 138             new PasswordProtection(PASSWORD));
 139         try (OutputStream stream = new FileOutputStream(file)) {
 140             ks.store(stream, PASSWORD);
 141         }
 142         System.out.println("Created a " + type + " keystore named '" + file + "'");
 143     }
 144 
 145     // Instantiate a keystore by probing the supplied file for the keystore type
 146     private static void build(String file, String type, boolean usePassword)
 147         throws Exception {
 148 
 149         Builder builder;
 150         if (usePassword) {
 151             builder = Builder.newInstance(type, null, new File(file),
 152                 new PasswordProtection(PASSWORD));
 153         } else {
 154             builder = Builder.newInstance(type, null, new File(file),
 155                 new CallbackHandlerProtection(new DummyHandler()));
 156         }
 157         KeyStore ks = builder.getKeyStore();
 158         if (!type.equalsIgnoreCase(ks.getType())) {
 159             throw new Exception("ERROR: expected a " + type + " keystore, " +
 160                 "got a " + ks.getType() + " keystore instead");
 161         } else {
 162             System.out.println("Built a " + type + " keystore named '" + file + "'");
 163         }
 164     }
 165 
 166     // Load the keystore entries
 167     private static void load(String file, String type) throws Exception {
 168         KeyStore ks = KeyStore.getInstance(type);
 169         try (InputStream stream = new FileInputStream(file)) {
 170             ks.load(stream, PASSWORD);
 171         }
 172         if (!type.equalsIgnoreCase(ks.getType())) {
 173             throw new Exception("ERROR: expected a " + type + " keystore, " +
 174                 "got a " + ks.getType() + " keystore instead");
 175         } else {
 176             System.out.println("Loaded a " + type + " keystore named '" + file + "'");
 177         }
 178     }
 179 
 180     // Load the keystore entries (with compatibility mode disabled)
 181     private static void load(String file, String type, boolean expectFailure)
 182         throws Exception {
 183         Security.setProperty("keystore.type.compat", "false");
 184         try {
 185             load(file, type);
 186             if (expectFailure) {
 187                 throw new Exception("ERROR: expected load to fail but it didn't");
 188             }
 189         } catch (IOException e) {
 190             if (expectFailure) {
 191                 System.out.println("Failed to load a " + type + " keystore named '" + file + "' (as expected)");
 192             } else {
 193                 throw e;
 194             }
 195         } finally {
 196             Security.setProperty("keystore.type.compat", "true");
 197         }
 198     }
 199 
 200     // Read an X.509 certificate from the supplied file
 201     private static X509Certificate loadCertificate(String certFile)
 202         throws Exception {
 203         X509Certificate cert = null;
 204         try (FileInputStream certStream =
 205             new FileInputStream(DIR + "/" + certFile)) {
 206             CertificateFactory factory =
 207                 CertificateFactory.getInstance("X.509");
 208             return (X509Certificate) factory.generateCertificate(certStream);
 209         }
 210     }
 211 
 212     // Generate a secret key using the supplied algorithm name and key size
 213     private static SecretKey generateSecretKey(String algorithm, int size)
 214         throws NoSuchAlgorithmException {
 215         KeyGenerator generator = KeyGenerator.getInstance(algorithm);
 216         generator.init(size);
 217         return generator.generateKey();
 218     }
 219 
 220     private static class DummyHandler implements CallbackHandler {
 221         public void handle(Callback[] callbacks)
 222             throws IOException, UnsupportedCallbackException {
 223             System.out.println("** Callbackhandler invoked");
 224             for (int i = 0; i < callbacks.length; i++) {
 225                 Callback cb = callbacks[i];
 226                 if (cb instanceof PasswordCallback) {
 227                     PasswordCallback pcb = (PasswordCallback)cb;
 228                     pcb.setPassword(PASSWORD);
 229                     break;
 230                 }
 231             }
 232         }
 233     }
 234 }