| /* | |
| * Copyright (c) 2017, 2022, 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 8171319 8177569 8182879 8172404 | |
| * @summary keytool should print out warnings when reading or generating | |
| * cert/cert req using weak algorithms | |
| * @library /test/lib | |
| * @modules java.base/sun.security.tools.keytool | |
| * java.base/sun.security.tools | |
| * java.base/sun.security.util | |
| * @build jdk.test.lib.SecurityTools | |
| * jdk.test.lib.Utils | |
| * jdk.test.lib.Asserts | |
| * jdk.test.lib.JDKToolFinder | |
| * jdk.test.lib.JDKToolLauncher | |
| * jdk.test.lib.Platform | |
| * jdk.test.lib.process.* | |
| * @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg | |
| */ | |
| import jdk.test.lib.Asserts; | |
| import jdk.test.lib.SecurityTools; | |
| import jdk.test.lib.process.OutputAnalyzer; | |
| import sun.security.tools.KeyStoreUtil; | |
| import sun.security.util.DisabledAlgorithmConstraints; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.PrintStream; | |
| import java.nio.file.Files; | |
| import java.nio.file.Paths; | |
| import java.nio.file.StandardCopyOption; | |
| import java.security.CryptoPrimitive; | |
| import java.security.KeyStore; | |
| import java.security.cert.X509Certificate; | |
| import java.util.Collections; | |
| import java.util.EnumSet; | |
| import java.util.Set; | |
| import java.util.stream.Collectors; | |
| import java.util.stream.Stream; | |
| public class WeakAlg { | |
| public static void main(String[] args) throws Throwable { | |
| rm("ks"); | |
| // Tests for "disabled" algorithms | |
| // -genkeypair, and -printcert, -list -alias, -exportcert | |
| // (w/ different formats) | |
| checkDisabledGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA"); | |
| checkDisabledGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key"); | |
| checkDisabledGenKeyPair("c", "-keyalg RSA", null); | |
| kt("-list") | |
| .shouldContain("Warning:") | |
| .shouldMatch("<a>.*MD5withRSA.*is disabled") | |
| .shouldMatch("<b>.*512-bit RSA key.*is disabled"); | |
| kt("-list -v") | |
| .shouldContain("Warning:") | |
| .shouldMatch("<a>.*MD5withRSA.*is disabled") | |
| .shouldContain("MD5withRSA (disabled)") | |
| .shouldMatch("<b>.*512-bit RSA key.*is disabled") | |
| .shouldContain("512-bit RSA key (disabled)"); | |
| // Multiple warnings for multiple cert in -printcert | |
| // or -list or -exportcert | |
| // -certreq, -printcertreq, -gencert | |
| checkDisabledCertReq("a", "", null); | |
| gencert("c-a", "") | |
| .shouldNotContain("Warning"); // new sigalg is not weak | |
| gencert("c-a", "-sigalg MD2withRSA") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The generated certificate.*MD2withRSA.*is disabled"); | |
| checkDisabledCertReq("a", "-sigalg MD5withRSA", "MD5withRSA"); | |
| gencert("c-a", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*MD5withRSA.*is disabled"); | |
| gencert("c-a", "-sigalg MD2withRSA") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*MD5withRSA.*is disabled") | |
| .shouldMatch("The generated certificate.*MD2withRSA.*is disabled"); | |
| checkDisabledCertReq("b", "", "512-bit RSA key"); | |
| gencert("c-b", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*512-bit RSA key.*is disabled") | |
| .shouldMatch("The generated certificate.*512-bit RSA key.*is disabled"); | |
| checkDisabledCertReq("c", "", null); | |
| gencert("a-c", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The issuer.*MD5withRSA.*is disabled"); | |
| // but the new cert is not weak | |
| kt("-printcert -file a-c.cert") | |
| .shouldNotContain("Warning") | |
| .shouldNotContain("(disabled)"); | |
| gencert("b-c", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The issuer.*512-bit RSA key.*is disabled"); | |
| // -importcert | |
| checkImport(); | |
| // -importkeystore | |
| checkImportKeyStore(); | |
| // -gencrl, -printcrl | |
| checkDisabledGenCRL("a", "", null); | |
| checkDisabledGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA"); | |
| checkDisabledGenCRL("b", "", "512-bit RSA key"); | |
| checkDisabledGenCRL("c", "", null); | |
| kt("-delete -alias b"); | |
| kt("-printcrl -file b.crl") | |
| .shouldContain("WARNING: not verified"); | |
| jksTypeCheck(); | |
| checkInplaceImportKeyStore(); | |
| rm("ks"); | |
| // Tests for "legacy" algorithms | |
| // -genkeypair, and -printcert, -list -alias, -exportcert | |
| // (w/ different formats) | |
| checkWeakGenKeyPair("x", "-keyalg RSA -sigalg SHA1withRSA", "SHA1withRSA"); | |
| checkWeakGenKeyPair("y", "-keyalg RSA -keysize 1024", "1024-bit RSA key"); | |
| checkWeakGenKeyPair("z", "-keyalg RSA", "nowarn"); | |
| kt("-list") | |
| .shouldContain("Warning:") | |
| .shouldMatch("<x>.*SHA1withRSA.*considered a security risk") | |
| .shouldMatch("<y>.*1024-bit RSA key.*will be disabled"); | |
| kt("-list -v") | |
| .shouldContain("Warning:") | |
| .shouldMatch("<x>.*SHA1withRSA.*considered a security risk") | |
| .shouldContain("SHA1withRSA (weak)") | |
| .shouldMatch("<y>.*1024-bit RSA key.*will be disabled") | |
| .shouldContain("1024-bit RSA key (weak)"); | |
| // Multiple warnings for multiple cert in -printcert | |
| // or -list or -exportcert | |
| // -certreq, -printcertreq, -gencert | |
| checkWeakCertReq("x", "", "nowarn"); | |
| gencert("z-x", "") | |
| .shouldNotContain("Warning"); // new sigalg is not weak | |
| gencert("z-x", "-sigalg SHA1withRSA") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The generated certificate.*SHA1withRSA.*considered a security risk"); | |
| checkWeakCertReq("x", "-sigalg SHA1withRSA", "SHA1withRSA"); | |
| gencert("z-x", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*SHA1withRSA.*considered a security risk"); | |
| gencert("z-x", "-sigalg SHA1withRSA") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*SHA1withRSA.*considered a security risk") | |
| .shouldMatch("The generated certificate.*SHA1withRSA.*considered a security risk"); | |
| checkWeakCertReq("y", "", "1024-bit RSA key"); | |
| gencert("z-y", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The certificate request.*1024-bit RSA key.*will be disabled") | |
| .shouldMatch("The generated certificate.*1024-bit RSA key.*will be disabled"); | |
| checkWeakCertReq("z", "", "nowarn"); | |
| gencert("x-z", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The issuer.*SHA1withRSA.*considered a security risk"); | |
| // but the new cert is not weak | |
| kt("-printcert -file x-z.cert") | |
| .shouldNotContain("Warning") | |
| .shouldNotContain("weak"); | |
| gencert("y-z", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("The issuer.*1024-bit RSA key.*will be disabled"); | |
| // -gencrl, -printcrl | |
| checkWeakGenCRL("x", "", "nowarn"); | |
| checkWeakGenCRL("x", "-sigalg SHA1withRSA", "SHA1withRSA"); | |
| checkWeakGenCRL("y", "", "1024-bit RSA key"); | |
| checkWeakGenCRL("z", "", "nowarn"); | |
| kt("-delete -alias y"); | |
| kt("-printcrl -file y.crl") | |
| .shouldContain("WARNING: not verified"); | |
| jksTypeCheck(); | |
| } | |
| static void jksTypeCheck() throws Exception { | |
| // No warning for cacerts, all certs | |
| kt0("-cacerts -list -storepass changeit") | |
| .shouldNotContain("proprietary format"); | |
| rm("ks"); | |
| rm("ks2"); | |
| kt("-genkeypair -keyalg DSA -alias a -dname CN=A") | |
| .shouldNotContain("Warning:"); | |
| kt("-list") | |
| .shouldNotContain("Warning:"); | |
| kt("-list -storetype jks") // no warning if PKCS12 used as JKS | |
| .shouldNotContain("Warning:"); | |
| kt("-exportcert -alias a -file a.crt") | |
| .shouldNotContain("Warning:"); | |
| // warn if migrating to JKS | |
| importkeystore("ks", "ks2", "-deststoretype jks") | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| rm("ks"); | |
| rm("ks2"); | |
| rm("ks3"); | |
| // no warning if all certs | |
| kt("-importcert -alias b -file a.crt -storetype jks -noprompt") | |
| .shouldNotContain("Warning:"); | |
| kt("-genkeypair -keyalg DSA -alias a -dname CN=A") | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-list") | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-list -storetype pkcs12") // warn if JKS used as PKCS12 | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-exportcert -alias a -file a.crt") | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-printcert -file a.crt") // warning since -keystore option is supported | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-certreq -alias a -file a.req") | |
| .shouldContain("JKS keystore uses a proprietary format"); | |
| kt("-printcertreq -file a.req") // no warning if keystore not touched | |
| .shouldNotContain("Warning:"); | |
| // No warning if migrating from JKS | |
| importkeystore("ks", "ks2", "") | |
| .shouldNotContain("Warning:"); | |
| importkeystore("ks", "ks3", "-deststoretype pkcs12") | |
| .shouldNotContain("Warning:"); | |
| rm("ks"); | |
| kt("-genkeypair -keyalg DSA -alias a -dname CN=A -storetype jceks") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-list") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-importcert -alias b -file a.crt -noprompt") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-exportcert -alias a -file a.crt") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-printcert -file a.crt") // warning since -keystore option is supported | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-certreq -alias a -file a.req") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| kt("-printcertreq -file a.req") | |
| .shouldNotContain("Warning:"); | |
| kt("-genseckey -alias c -keyalg AES -keysize 128") | |
| .shouldContain("JCEKS keystore uses a proprietary format"); | |
| } | |
| static void checkImportKeyStore() throws Exception { | |
| rm("ks2"); | |
| rm("ks3"); | |
| importkeystore("ks", "ks2", "") | |
| .shouldContain("3 entries successfully imported") | |
| .shouldContain("Warning") | |
| .shouldMatch("<b>.*512-bit RSA key.*is disabled") | |
| .shouldMatch("<a>.*MD5withRSA.*is disabled"); | |
| importkeystore("ks", "ks3", "-srcalias a") | |
| .shouldContain("Warning") | |
| .shouldMatch("<a>.*MD5withRSA.*is disabled"); | |
| } | |
| static void checkInplaceImportKeyStore() throws Exception { | |
| rm("ks"); | |
| genkeypair("a", "-keyalg DSA"); | |
| // Same type backup | |
| importkeystore("ks", "ks", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("original.*ks.old"); | |
| importkeystore("ks", "ks", "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("original.*ks.old2"); | |
| importkeystore("ks", "ks", "-srcstoretype jks") // it knows real type | |
| .shouldContain("Warning:") | |
| .shouldMatch("original.*ks.old3"); | |
| String cPath = new File("ks").getCanonicalPath(); | |
| importkeystore("ks", cPath, "") | |
| .shouldContain("Warning:") | |
| .shouldMatch("original.*ks.old4"); | |
| // Migration | |
| importkeystore("ks", "ks", "-deststoretype jks") | |
| .shouldContain("Warning:") | |
| .shouldContain("JKS keystore uses a proprietary format") | |
| .shouldMatch("Migrated.*JKS.*PKCS12.*ks.old5"); | |
| Asserts.assertEQ( | |
| KeyStore.getInstance( | |
| new File("ks"), "changeit".toCharArray()).getType(), | |
| "JKS"); | |
| importkeystore("ks", "ks", "-srcstoretype PKCS12") | |
| .shouldContain("Warning:") | |
| .shouldNotContain("proprietary format") | |
| .shouldMatch("Migrated.*PKCS12.*JKS.*ks.old6"); | |
| Asserts.assertEQ( | |
| KeyStore.getInstance( | |
| new File("ks"), "changeit".toCharArray()).getType(), | |
| "PKCS12"); | |
| Asserts.assertEQ( | |
| KeyStore.getInstance( | |
| new File("ks.old6"), "changeit".toCharArray()).getType(), | |
| "JKS"); | |
| // One password prompt is enough for migration | |
| kt0("-importkeystore -srckeystore ks -destkeystore ks", "changeit") | |
| .shouldMatch("original.*ks.old7"); | |
| // But three if importing to a different keystore | |
| rm("ks2"); | |
| kt0("-importkeystore -srckeystore ks -destkeystore ks2", | |
| "changeit") | |
| .shouldContain("Keystore password is too short"); | |
| kt0("-importkeystore -srckeystore ks -destkeystore ks2", | |
| "changeit", "changeit", "changeit") | |
| .shouldContain("Importing keystore ks to ks2...") | |
| .shouldNotContain("original") | |
| .shouldNotContain("Migrated"); | |
| } | |
| static void checkImport() throws Exception { | |
| saveStore(); | |
| // add trusted cert | |
| // cert already in | |
| kt("-importcert -alias d -file a.cert", "no") | |
| .shouldContain("Certificate already exists in keystore") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*MD5withRSA.*is disabled") | |
| .shouldContain("Do you still want to add it?"); | |
| kt("-importcert -alias d -file a.cert -noprompt") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*MD5withRSA.*is disabled") | |
| .shouldNotContain("[no]"); | |
| // cert is self-signed | |
| kt("-delete -alias a"); | |
| kt("-delete -alias d"); | |
| kt("-importcert -alias d -file a.cert", "no") | |
| .shouldContain("Warning") | |
| .shouldContain("MD5withRSA (disabled)") | |
| .shouldMatch("The input.*MD5withRSA.*is disabled") | |
| .shouldContain("Trust this certificate?"); | |
| kt("-importcert -alias d -file a.cert -noprompt") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*MD5withRSA.*is disabled") | |
| .shouldNotContain("[no]"); | |
| // JDK-8177569: no warning for sigalg of trusted cert | |
| String weakSigAlgCA = null; | |
| KeyStore ks = KeyStoreUtil.getCacertsKeyStore(); | |
| if (ks != null) { | |
| DisabledAlgorithmConstraints disabledCheck = | |
| new DisabledAlgorithmConstraints( | |
| DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); | |
| Set<CryptoPrimitive> sigPrimitiveSet = Collections | |
| .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); | |
| for (String s : Collections.list(ks.aliases())) { | |
| if (ks.isCertificateEntry(s)) { | |
| X509Certificate c = (X509Certificate)ks.getCertificate(s); | |
| String sigAlg = c.getSigAlgName(); | |
| if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) { | |
| weakSigAlgCA = sigAlg; | |
| Files.write(Paths.get("ca.cert"), | |
| ks.getCertificate(s).getEncoded()); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (weakSigAlgCA != null) { | |
| // The following 2 commands still have a warning on why not using | |
| // the -cacerts option directly. | |
| kt("-list -keystore " + KeyStoreUtil.getCacerts()) | |
| .shouldNotMatch("signature algorithm.*risk"); | |
| kt("-list -v -keystore " + KeyStoreUtil.getCacerts()) | |
| .shouldNotMatch("signature algorithm.*risk"); | |
| // -printcert will always show warnings | |
| kt("-printcert -file ca.cert") | |
| .shouldContain("name: " + weakSigAlgCA + " (disabled)") | |
| .shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + weakSigAlgCA + ".*is disabled"); | |
| kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless | |
| .shouldContain("name: " + weakSigAlgCA + " (disabled)") | |
| .shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + weakSigAlgCA + ".*is disabled"); | |
| // Importing with -trustcacerts ignore CA cert's sig alg | |
| kt("-delete -alias d"); | |
| kt("-importcert -alias d -trustcacerts -file ca.cert", "no") | |
| .shouldContain("Certificate already exists in system-wide CA") | |
| .shouldNotMatch("signature algorithm.*risk") | |
| .shouldContain("Do you still want to add it to your own keystore?"); | |
| kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt") | |
| .shouldNotMatch("signature algorithm.*risk") | |
| .shouldNotContain("[no]"); | |
| // but not without -trustcacerts | |
| kt("-delete -alias d"); | |
| kt("-importcert -alias d -file ca.cert", "no") | |
| .shouldContain("name: " + weakSigAlgCA + " (disabled)") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*" + weakSigAlgCA + ".*is disabled") | |
| .shouldContain("Trust this certificate?"); | |
| kt("-importcert -alias d -file ca.cert -noprompt") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*" + weakSigAlgCA + ".*is disabled") | |
| .shouldNotContain("[no]"); | |
| } | |
| // a non self-signed weak cert | |
| reStore(); | |
| certreq("b", ""); | |
| gencert("c-b", ""); | |
| kt("-importcert -alias d -file c-b.cert") // weak only, no prompt | |
| .shouldContain("Warning") | |
| .shouldNotContain("512-bit RSA key (disabled)") | |
| .shouldMatch("The input.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| kt("-delete -alias b"); | |
| kt("-delete -alias c"); | |
| kt("-delete -alias d"); | |
| kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted | |
| .shouldContain("Warning") | |
| .shouldContain("512-bit RSA key (disabled)") | |
| .shouldMatch("The input.*512-bit RSA key.*is disabled") | |
| .shouldContain("Trust this certificate?"); | |
| kt("-importcert -alias d -file c-b.cert -noprompt") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| // a non self-signed strong cert | |
| reStore(); | |
| certreq("a", ""); | |
| gencert("c-a", ""); | |
| kt("-importcert -alias d -file c-a.cert") // trusted | |
| .shouldNotContain("Warning") | |
| .shouldNotContain("[no]"); | |
| kt("-delete -alias a"); | |
| kt("-delete -alias c"); | |
| kt("-delete -alias d"); | |
| kt("-importcert -alias d -file c-a.cert", "no") // not trusted | |
| .shouldNotContain("Warning") | |
| .shouldContain("Trust this certificate?"); | |
| kt("-importcert -alias d -file c-a.cert -noprompt") | |
| .shouldNotContain("Warning") | |
| .shouldNotContain("[no]"); | |
| // install reply | |
| reStore(); | |
| certreq("c", ""); | |
| gencert("a-c", ""); | |
| kt("-importcert -alias c -file a-c.cert") | |
| .shouldContain("Warning") | |
| .shouldMatch("Issuer <a>.*MD5withRSA.*is disabled"); | |
| // JDK-8177569: no warning for sigalg of trusted cert | |
| reStore(); | |
| // Change a into a TrustedCertEntry | |
| kt("-exportcert -alias a -file a.cert"); | |
| kt("-delete -alias a"); | |
| kt("-importcert -alias a -file a.cert -noprompt"); | |
| kt("-list -alias a -v") | |
| .shouldNotContain("disabled") | |
| .shouldNotContain("Warning"); | |
| // This time a is trusted and no warning on its weak sig alg | |
| kt("-importcert -alias c -file a-c.cert") | |
| .shouldNotContain("Warning"); | |
| reStore(); | |
| gencert("a-b", ""); | |
| gencert("b-c", ""); | |
| // Full chain with root | |
| cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert"); | |
| kt("-importcert -alias c -file a-a-b-c.cert") // only weak | |
| .shouldContain("Warning") | |
| .shouldMatch("Reply #2 of 3.*512-bit RSA key.*is disabled") | |
| .shouldMatch("Reply #3 of 3.*MD5withRSA.*is disabled") | |
| .shouldNotContain("[no]"); | |
| // Without root | |
| cat("a-b-c.cert", "b-c.cert", "a-b.cert"); | |
| kt("-importcert -alias c -file a-b-c.cert") // only weak | |
| .shouldContain("Warning") | |
| .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled") | |
| .shouldMatch("Issuer <a>.*MD5withRSA.*is disabled") | |
| .shouldNotContain("[no]"); | |
| reStore(); | |
| gencert("b-a", ""); | |
| kt("-importcert -alias a -file b-a.cert") | |
| .shouldContain("Warning") | |
| .shouldMatch("Issuer <b>.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| kt("-importcert -alias a -file c-a.cert") | |
| .shouldNotContain("Warning"); | |
| kt("-importcert -alias b -file c-b.cert") | |
| .shouldContain("Warning") | |
| .shouldMatch("The input.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| reStore(); | |
| gencert("b-a", ""); | |
| cat("c-b-a.cert", "b-a.cert", "c-b.cert"); | |
| kt("-printcert -file c-b-a.cert") | |
| .shouldContain("Warning") | |
| .shouldMatch("The certificate #2 of 2.*512-bit RSA key.*is disabled"); | |
| kt("-delete -alias b"); | |
| kt("-importcert -alias a -file c-b-a.cert") | |
| .shouldContain("Warning") | |
| .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| kt("-delete -alias c"); | |
| kt("-importcert -alias a -file c-b-a.cert", "no") | |
| .shouldContain("Top-level certificate in reply:") | |
| .shouldContain("512-bit RSA key (disabled)") | |
| .shouldContain("Warning") | |
| .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled") | |
| .shouldContain("Install reply anyway?"); | |
| kt("-importcert -alias a -file c-b-a.cert -noprompt") | |
| .shouldContain("Warning") | |
| .shouldMatch("Reply #2 of 2.*512-bit RSA key.*is disabled") | |
| .shouldNotContain("[no]"); | |
| reStore(); | |
| } | |
| private static void cat(String dest, String... src) throws IOException { | |
| System.out.println("---------------------------------------------"); | |
| System.out.printf("$ cat "); | |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); | |
| for (String s : src) { | |
| System.out.printf(s + " "); | |
| bout.write(Files.readAllBytes(Paths.get(s))); | |
| } | |
| Files.write(Paths.get(dest), bout.toByteArray()); | |
| System.out.println("> " + dest); | |
| } | |
| static void checkDisabledGenCRL(String alias, String options, String bad) { | |
| OutputAnalyzer oa = kt("-gencrl -alias " + alias | |
| + " -id 1 -file " + alias + ".crl " + options); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated CRL.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-printcrl -file " + alias + ".crl"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning") | |
| .shouldContain("Verified by " + alias + " in keystore") | |
| .shouldNotContain("(disabled"); | |
| } else { | |
| oa.shouldContain("Warning:") | |
| .shouldMatch("The CRL.*" + bad + ".*is disabled") | |
| .shouldContain("Verified by " + alias + " in keystore") | |
| .shouldContain(bad + " (disabled)"); | |
| } | |
| } | |
| static void checkDisabledCertReq( | |
| String alias, String options, String bad) { | |
| OutputAnalyzer oa = certreq(alias, options); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate request.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-printcertreq -file " + alias + ".req"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning") | |
| .shouldNotContain("(disabled)"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate request.*" + bad + ".*is disabled") | |
| .shouldContain(bad + " (disabled)"); | |
| } | |
| } | |
| static void checkDisabledGenKeyPair( | |
| String alias, String options, String bad) { | |
| OutputAnalyzer oa = genkeypair(alias, options); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-printcert -rfc -file " + alias + ".cert"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-list -alias " + alias); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| // With cert content | |
| oa = kt("-printcert -file " + alias + ".cert"); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (disabled)") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| oa = kt("-list -v -alias " + alias); | |
| if (bad == null) { | |
| oa.shouldNotContain("Warning"); | |
| } else { | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (disabled)") | |
| .shouldMatch("The certificate.*" + bad + ".*is disabled"); | |
| } | |
| } | |
| static void checkWeakGenKeyPair( | |
| String alias, String options, String bad) { | |
| OutputAnalyzer oa = genkeypair(alias, options); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-printcert -rfc -file " + alias + ".cert"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-list -alias " + alias); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| // With cert content | |
| oa = kt("-printcert -file " + alias + ".cert"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (weak)") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (weak)") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-list -v -alias " + alias); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (weak)") | |
| .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldContain(bad + " (weak)") | |
| .shouldMatch("The certificate.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| } | |
| static void checkWeakGenCRL(String alias, String options, String bad) { | |
| OutputAnalyzer oa = kt("-gencrl -alias " + alias | |
| + " -id 1 -file " + alias + ".crl " + options); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated CRL.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated CRL.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-printcrl -file " + alias + ".crl"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning") | |
| .shouldContain("Verified by " + alias + " in keystore") | |
| .shouldNotContain("(weak"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The CRL.*" + bad + ".*considered a security risk") | |
| .shouldContain("Verified by " + alias + " in keystore") | |
| .shouldContain(bad + " (weak)"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The CRL.*" + bad + ".*will be disabled") | |
| .shouldContain("Verified by " + alias + " in keystore") | |
| .shouldContain(bad + " (weak)"); | |
| break; | |
| } | |
| } | |
| static void checkWeakCertReq( | |
| String alias, String options, String bad) { | |
| OutputAnalyzer oa = certreq(alias, options); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate request.*" + bad + ".*considered a security risk"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The generated certificate request.*" + bad + ".*will be disabled"); | |
| break; | |
| } | |
| oa = kt("-printcertreq -file " + alias + ".req"); | |
| switch (bad) { | |
| case "nowarn": | |
| oa.shouldNotContain("Warning") | |
| .shouldNotContain("(weak)"); | |
| break; | |
| case "SHA1withRSA": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate request.*" + bad + ".*considered a security risk") | |
| .shouldContain(bad + " (weak)"); | |
| break; | |
| case "1024-bit RSA key": | |
| oa.shouldContain("Warning") | |
| .shouldMatch("The certificate request.*" + bad + ".*will be disabled") | |
| .shouldContain(bad + " (weak)"); | |
| break; | |
| } | |
| } | |
| // This is slow, but real keytool process is launched. | |
| static OutputAnalyzer kt1(String cmd, String... input) { | |
| cmd = "-keystore ks -storepass changeit " + | |
| "-keypass changeit " + cmd; | |
| System.out.println("---------------------------------------------"); | |
| try { | |
| SecurityTools.setResponse(input); | |
| return SecurityTools.keytool(cmd); | |
| } catch (Throwable e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| static OutputAnalyzer kt(String cmd, String... input) { | |
| return kt0("-keystore ks -storepass changeit " + | |
| "-keypass changeit " + cmd, input); | |
| } | |
| // Fast keytool execution by directly calling its main() method | |
| static OutputAnalyzer kt0(String cmd, String... input) { | |
| PrintStream out = System.out; | |
| PrintStream err = System.err; | |
| InputStream ins = System.in; | |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); | |
| ByteArrayOutputStream berr = new ByteArrayOutputStream(); | |
| boolean succeed = true; | |
| String sout; | |
| String serr; | |
| try { | |
| System.out.println("---------------------------------------------"); | |
| System.out.println("$ keytool " + cmd); | |
| System.out.println(); | |
| String feed = ""; | |
| if (input.length > 0) { | |
| feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n"; | |
| } | |
| System.setIn(new ByteArrayInputStream(feed.getBytes())); | |
| System.setOut(new PrintStream(bout)); | |
| System.setErr(new PrintStream(berr)); | |
| sun.security.tools.keytool.Main.main( | |
| cmd.trim().split("\\s+")); | |
| } catch (Exception e) { | |
| // Might be a normal exception when -debug is on or | |
| // SecurityException (thrown by jtreg) when System.exit() is called | |
| if (!(e instanceof SecurityException)) { | |
| e.printStackTrace(); | |
| } | |
| succeed = false; | |
| } finally { | |
| System.setOut(out); | |
| System.setErr(err); | |
| System.setIn(ins); | |
| sout = new String(bout.toByteArray()); | |
| serr = new String(berr.toByteArray()); | |
| System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr); | |
| } | |
| if (!succeed) { | |
| throw new RuntimeException(); | |
| } | |
| return new OutputAnalyzer(sout, serr); | |
| } | |
| static OutputAnalyzer importkeystore(String src, String dest, | |
| String options) { | |
| return kt0("-importkeystore " | |
| + "-srckeystore " + src + " -destkeystore " + dest | |
| + " -srcstorepass changeit -deststorepass changeit " + options); | |
| } | |
| static OutputAnalyzer genkeypair(String alias, String options) { | |
| return kt("-genkeypair -alias " + alias + " -dname CN=" + alias | |
| + " -storetype PKCS12 " + options); | |
| } | |
| static OutputAnalyzer certreq(String alias, String options) { | |
| return kt("-certreq -alias " + alias | |
| + " -file " + alias + ".req " + options); | |
| } | |
| static OutputAnalyzer exportcert(String alias) { | |
| return kt("-exportcert -alias " + alias + " -file " + alias + ".cert"); | |
| } | |
| static OutputAnalyzer gencert(String relation, String options) { | |
| int pos = relation.indexOf("-"); | |
| String issuer = relation.substring(0, pos); | |
| String subject = relation.substring(pos + 1); | |
| return kt(" -gencert -alias " + issuer + " -infile " + subject | |
| + ".req -outfile " + relation + ".cert " + options); | |
| } | |
| static void saveStore() throws IOException { | |
| System.out.println("---------------------------------------------"); | |
| System.out.println("$ cp ks ks2"); | |
| Files.copy(Paths.get("ks"), Paths.get("ks2"), | |
| StandardCopyOption.REPLACE_EXISTING); | |
| } | |
| static void reStore() throws IOException { | |
| System.out.println("---------------------------------------------"); | |
| System.out.println("$ cp ks2 ks"); | |
| Files.copy(Paths.get("ks2"), Paths.get("ks"), | |
| StandardCopyOption.REPLACE_EXISTING); | |
| } | |
| static void rm(String s) throws IOException { | |
| System.out.println("---------------------------------------------"); | |
| System.out.println("$ rm " + s); | |
| Files.deleteIfExists(Paths.get(s)); | |
| } | |
| } |