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