--- /dev/null 2021-05-06 23:50:39.000000000 +0300
+++ new/test/sun/security/pkcs12/ParamsTest.java 2021-05-06 23:50:39.000000000 +0300
@@ -0,0 +1,442 @@
+/*
+ * 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);
+ }
+}