/* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8076190 * @library /lib/testlibrary /lib * @modules java.base/sun.security.pkcs * java.base/sun.security.x509 * java.base/sun.security.util * @summary Customizing the generation of a PKCS12 keystore */ import jdk.test.lib.Asserts; import jdk.test.lib.SecurityTools; import jdk.test.lib.process.OutputAnalyzer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyStore; import java.util.Base64; import java.util.Objects; import static jdk.testlibrary.security.DerUtils.*; import static sun.security.x509.AlgorithmId.*; import static sun.security.pkcs.ContentInfo.*; public class ParamsTest { public static void main(String[] args) throws Throwable { // De-BASE64 textual files in ./params to `pwd` Files.newDirectoryStream(Paths.get(System.getProperty("test.src"), "params")) .forEach(p -> { try (InputStream is = Base64.getMimeDecoder().wrap(Files.newInputStream(p)); OutputStream os = Files.newOutputStream(p.getFileName())){ byte[] buffer = new byte[2048]; int read; while ((read = is.read(buffer, 0, 2048)) >= 0) { os.write(buffer, 0, read); } } catch (IOException e) { throw new UncheckedIOException(e); } }); byte[] data; // openssl -> keytool interop check // os2. no cert pbe, no mac. check("os2", "a", null, "changeit", true, true, true); check("os2", "a", "changeit", "changeit", true, true, true); // You can even load it with a wrong storepass, controversial check("os2", "a", "wrongpass", "changeit", true, true, true); // os3. no cert pbe, has mac. just like JKS check("os3", "a", null, "changeit", true, true, true); check("os3", "a", "changeit", "changeit", true, true, true); // Cannot load with a wrong storepass, same as JKS check("os3", "a", "wrongpass", "-", IOException.class, "-", "-"); // os4. non default algs check("os4", "a", "changeit", "changeit", true, true, true); check("os4", "a", "wrongpass", "-", IOException.class, "-", "-"); // no storepass no cert check("os4", "a", null, "changeit", true, false, true); // os5. strong non default algs check("os5", "a", "changeit", "changeit", true, true, true); check("os5", "a", "wrongpass", "-", IOException.class, "-", "-"); // no storepass no cert check("os5", "a", null, "changeit", true, false, true); // keytool // Current default pkcs12 setting keytool("-importkeystore -srckeystore ks -srcstorepass changeit " + "-deststoretype PKCS12 -destkeystore ksnormal -deststorepass changeit"); data = Files.readAllBytes(Paths.get("ksnormal")); checkInt(data, "22", 100000); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 50000); // key ic checkAlg(data, "110c10", ENCRYPTED_DATA_OID); checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 50000); // cert ic check("ksnormal", "a", "changeit", "changeit", true, true, true); check("ksnormal", "a", null, "changeit", true, false, true); check("ksnormal", "a", "wrongpass", "-", IOException.class, "-", "-"); // Add a new entry with password-less settings, still has a storepass keytool("-keystore ksnormal -genkeypair -storepass changeit -alias b -dname CN=b " + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " + "-J-Dkeystore.pkcs12.macAlgorithm=NONE"); data = Files.readAllBytes(Paths.get("ksnormal")); checkInt(data, "22", 100000); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 50000); // key ic checkAlg(data, "110c010c11000", pbeWithSHA1AndDESede_oid); // new key alg checkInt(data, "110c010c110011", 50000); // new key ic checkAlg(data, "110c10", ENCRYPTED_DATA_OID); checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 50000); // cert ic check("ksnormal", "b", null, "changeit", true, false, true); check("ksnormal", "b", "changeit", "changeit", true, true, true); // Different keypbe alg, no cert pbe and no mac keytool("-importkeystore -srckeystore ks -srcstorepass changeit " + "-deststoretype PKCS12 -destkeystore ksnopass -deststorepass changeit " + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=PBEWithSHA1AndRC4_128 " + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " + "-J-Dkeystore.pkcs12.macAlgorithm=NONE"); data = Files.readAllBytes(Paths.get("ksnopass")); shouldNotExist(data, "2"); // no Mac checkAlg(data, "110c010c01000", pbeWithSHA1AndRC4_128_oid); checkInt(data, "110c010c010011", 50000); checkAlg(data, "110c10", DATA_OID); check("ksnopass", "a", null, "changeit", true, true, true); check("ksnopass", "a", "changeit", "changeit", true, true, true); check("ksnopass", "a", "wrongpass", "changeit", true, true, true); // Add a new entry with normal settings, still password-less keytool("-keystore ksnopass -genkeypair -storepass changeit -alias b -dname CN=B"); data = Files.readAllBytes(Paths.get("ksnopass")); shouldNotExist(data, "2"); // no Mac checkAlg(data, "110c010c01000", pbeWithSHA1AndRC4_128_oid); checkInt(data, "110c010c010011", 50000); checkAlg(data, "110c010c11000", pbeWithSHA1AndDESede_oid); checkInt(data, "110c010c110011", 50000); checkAlg(data, "110c10", DATA_OID); check("ksnopass", "a", null, "changeit", true, true, true); check("ksnopass", "b", null, "changeit", true, true, true); keytool("-importkeystore -srckeystore ks -srcstorepass changeit " + "-deststoretype PKCS12 -destkeystore ksnewic -deststorepass changeit " + "-J-Dkeystore.pkcs12.macIterationCount=5555 " + "-J-Dkeystore.pkcs12.certPbeIterationCount=6666 " + "-J-Dkeystore.pkcs12.keyPbeIterationCount=7777"); data = Files.readAllBytes(Paths.get("ksnewic")); checkInt(data, "22", 5555); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 7777); // key ic checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 6666); // cert ic // keypbe alg cannot be NONE keytool("-keystore ksnewic -genkeypair -storepass changeit -alias b -dname CN=B " + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=NONE") .shouldContain("NONE AlgorithmParameters not available") .shouldHaveExitValue(1); // new entry new keypbe alg (and default ic), else unchanged keytool("-keystore ksnewic -genkeypair -storepass changeit -alias b -dname CN=B " + "-J-Dkeystore.pkcs12.keyProtectionAlgorithm=PBEWithSHA1AndRC4_128"); data = Files.readAllBytes(Paths.get("ksnewic")); checkInt(data, "22", 5555); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 7777); // key ic checkAlg(data, "110c010c11000", pbeWithSHA1AndRC4_128_oid); // new key alg checkInt(data, "110c010c110011", 50000); // new key ic checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 6666); // cert ic // Check KeyStore loading multiple keystores KeyStore ks = KeyStore.getInstance("pkcs12"); try (FileInputStream fis = new FileInputStream("ksnormal"); FileOutputStream fos = new FileOutputStream("ksnormaldup")) { ks.load(fis, "changeit".toCharArray()); ks.store(fos, "changeit".toCharArray()); } data = Files.readAllBytes(Paths.get("ksnormaldup")); checkInt(data, "22", 100000); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 50000); // key ic checkAlg(data, "110c010c11000", pbeWithSHA1AndDESede_oid); // new key alg checkInt(data, "110c010c110011", 50000); // new key ic checkAlg(data, "110c10", ENCRYPTED_DATA_OID); checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 50000); // cert ic try (FileInputStream fis = new FileInputStream("ksnopass"); FileOutputStream fos = new FileOutputStream("ksnopassdup")) { ks.load(fis, "changeit".toCharArray()); ks.store(fos, "changeit".toCharArray()); } data = Files.readAllBytes(Paths.get("ksnopassdup")); shouldNotExist(data, "2"); // no Mac checkAlg(data, "110c010c01000", pbeWithSHA1AndRC4_128_oid); checkInt(data, "110c010c010011", 50000); checkAlg(data, "110c010c11000", pbeWithSHA1AndDESede_oid); checkInt(data, "110c010c110011", 50000); checkAlg(data, "110c10", DATA_OID); try (FileInputStream fis = new FileInputStream("ksnewic"); FileOutputStream fos = new FileOutputStream("ksnewicdup")) { ks.load(fis, "changeit".toCharArray()); ks.store(fos, "changeit".toCharArray()); } data = Files.readAllBytes(Paths.get("ksnewicdup")); checkInt(data, "22", 5555); // Mac ic checkAlg(data, "2000", SHA_oid); // Mac alg checkAlg(data, "110c010c01000", pbeWithSHA1AndDESede_oid); // key alg checkInt(data, "110c010c010011", 7777); // key ic checkAlg(data, "110c010c11000", pbeWithSHA1AndRC4_128_oid); // new key alg checkInt(data, "110c010c110011", 50000); // new key ic checkAlg(data, "110c110110", pbeWithSHA1AndRC2_40_oid); // cert alg checkInt(data, "110c1101111", 6666); // cert ic // Check keytool behavior // ksnormal has password keytool("-list -keystore ksnormal") .shouldContain("WARNING WARNING WARNING") .shouldContain("Certificate chain length: 0"); SecurityTools.setResponse("changeit"); keytool("-list -keystore ksnormal") .shouldNotContain("WARNING WARNING WARNING") .shouldContain("Certificate fingerprint"); // ksnopass is password-less keytool("-list -keystore ksnopass") .shouldNotContain("WARNING WARNING WARNING") .shouldContain("Certificate fingerprint"); // -certreq prompts for keypass SecurityTools.setResponse("changeit"); keytool("-certreq -alias a -keystore ksnopass") .shouldContain("Enter key password for ") .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") .shouldHaveExitValue(0); // -certreq -storepass works fine keytool("-certreq -alias a -keystore ksnopass -storepass changeit") .shouldNotContain("Enter key password for ") .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") .shouldHaveExitValue(0); // -certreq -keypass also works fine keytool("-certreq -alias a -keystore ksnopass -keypass changeit") .shouldNotContain("Enter key password for ") .shouldContain("-----BEGIN NEW CERTIFICATE REQUEST-----") .shouldHaveExitValue(0); // -importkeystore prompts for srckeypass SecurityTools.setResponse("changeit", "changeit"); keytool("-importkeystore -srckeystore ksnopass " + "-destkeystore jks3 -deststoretype PKCS12 -deststorepass changeit") .shouldContain("Enter key password for ") .shouldContain("Enter key password for ") .shouldContain("2 entries successfully imported"); // ksnopass2 is ksnopass + 2 cert entries ks = KeyStore.getInstance("pkcs12"); try (FileInputStream fis = new FileInputStream("ksnopass")) { ks.load(fis, (char[])null); } ks.setCertificateEntry("aa", ks.getCertificate("a")); ks.setCertificateEntry("bb", ks.getCertificate("b")); try (FileOutputStream fos = new FileOutputStream("ksnopass2")) { ks.store(fos, null); } // -importkeystore prompts for srckeypass for private keys // and no prompt for certs SecurityTools.setResponse("changeit", "changeit"); keytool("-importkeystore -srckeystore ksnopass2 " + "-destkeystore jks5 -deststorepass changeit") .shouldContain("Enter key password for ") .shouldContain("Enter key password for ") .shouldNotContain("Enter key password for ") .shouldNotContain("Enter key password for ") .shouldContain("4 entries successfully imported"); // ksonlycert has only cert entries ks.deleteEntry("a"); ks.deleteEntry("b"); try (FileOutputStream fos = new FileOutputStream("ksonlycert")) { ks.store(fos, null); } // -importkeystore does not prompt at all keytool("-importkeystore -srckeystore ksonlycert " + "-destkeystore jks6 -deststorepass changeit") .shouldNotContain("Enter key password for ") .shouldNotContain("Enter key password for ") .shouldContain("2 entries successfully imported"); // create a new password-less keystore keytool("-keystore ksnopass -exportcert -alias a -file a.cert -rfc"); // Normally storepass is prompted for keytool("-keystore kscert1 -storetype PKCS12 -importcert -alias a -file a.cert -noprompt") .shouldContain("Enter keystore password:"); keytool("-keystore kscert2 -storetype PKCS12 -importcert -alias a -file a.cert -noprompt " + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE") .shouldContain("Enter keystore password:"); keytool("-keystore kscert3 -storetype PKCS12 -importcert -alias a -file a.cert -noprompt " + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") .shouldContain("Enter keystore password:"); // ... but not if it's password-less keytool("-keystore kscert4 -storetype PKCS12 -importcert -alias a -file a.cert -noprompt " + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") .shouldNotContain("Enter keystore password:"); // still prompt for keypass for genkeypair and certreq SecurityTools.setResponse("changeit", "changeit"); keytool("-keystore ksnopassnew -storetype PKCS12 -genkeypair -alias a -dname CN=A " + "-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE " + "-J-Dkeystore.pkcs12.macAlgorithm=NONE") .shouldNotContain("Enter keystore password:") .shouldContain("Enter key password for "); keytool("-keystore ksnopassnew -certreq -alias a") .shouldNotContain("Enter keystore password:") .shouldContain("Enter key password for "); keytool("-keystore ksnopassnew -list -v -alias a") .shouldNotContain("Enter keystore password:") .shouldNotContain("Enter key password for "); // params only read on demand // keyPbeIterationCount is used by -genkeypair keytool("-keystore ksgenbadkeyic -storetype PKCS12 -genkeypair -alias a -dname CN=A " + "-storepass changeit " + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc") .shouldContain("keyPbeIterationCount is not a number: abc") .shouldHaveExitValue(1); keytool("-keystore ksnopassnew -exportcert -alias a -file a.cert"); // but not used by -importcert keytool("-keystore ksimpbadkeyic -importcert -alias a -file a.cert " + "-noprompt -storepass changeit " + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc") .shouldHaveExitValue(0); // None is used by -list keytool("-keystore ksnormal -storepass changeit -list " + "-J-Dkeystore.pkcs12.keyPbeIterationCount=abc " + "-J-Dkeystore.pkcs12.certPbeIterationCount=abc " + "-J-Dkeystore.pkcs12.macIterationCount=abc") .shouldHaveExitValue(0); } /** * Check keystore loading and key/cert reading. * * @param keystore the file name of keystore * @param alias the key/cert to read * @param storePass store pass to try out, can be null * @param keypass key pass to try, can not be null * @param expectedLoad expected result of keystore loading, true if non * null, false if null, exception class if exception * @param expectedCert expected result of cert reading * @param expectedKey expected result of key reading */ private static void check( String keystore, String alias, String storePass, String keypass, Object expectedLoad, Object expectedCert, Object expectedKey) { KeyStore ks = null; Object actualLoad, actualCert, actualKey; String label = keystore + "-" + alias + "-" + storePass + "-" + keypass; try { ks = KeyStore.getInstance("pkcs12"); try (FileInputStream fis = new FileInputStream(keystore)) { ks.load(fis, storePass == null ? null : storePass.toCharArray()); } actualLoad = ks != null; } catch (Exception e) { e.printStackTrace(System.out); actualLoad = e.getClass(); } Asserts.assertEQ(expectedLoad, actualLoad, label + "-load"); // If not loaded correctly, skip cert/key reading if (!Objects.equals(actualLoad, true)) { return; } try { actualCert = (ks.getCertificate(alias) != null); } catch (Exception e) { e.printStackTrace(System.out); actualCert = e.getClass(); } Asserts.assertEQ(expectedCert, actualCert, label + "-cert"); try { actualKey = (ks.getKey(alias, keypass.toCharArray()) != null); } catch (Exception e) { e.printStackTrace(System.out); actualKey = e.getClass(); } Asserts.assertEQ(expectedKey, actualKey, label + "-key"); } static OutputAnalyzer keytool(String s) throws Throwable { return SecurityTools.keytool(s); } }