8130132: jarsigner should emit warning if weak algorithms or keysizes are used
authorweijun
Wed, 02 Dec 2015 16:44:57 +0800
changeset 34382 5d11306d6969
parent 34381 be5600f6a4da
child 34383 15dce969f2a4
8130132: jarsigner should emit warning if weak algorithms or keysizes are used Reviewed-by: mullan
jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java
jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java
jdk/test/sun/security/tools/jarsigner/TsacertOptionTest.java
jdk/test/sun/security/tools/jarsigner/Warning.java
jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh
jdk/test/sun/security/tools/jarsigner/default_options.sh
jdk/test/sun/security/tools/jarsigner/ec.sh
jdk/test/sun/security/tools/jarsigner/onlymanifest.sh
jdk/test/sun/security/tools/jarsigner/ts.sh
jdk/test/sun/security/tools/jarsigner/warning.sh
jdk/test/sun/security/tools/jarsigner/weaksize.sh
--- a/jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Wed Dec 02 16:44:57 2015 +0800
@@ -38,6 +38,7 @@
 
 import java.security.KeyStore;
 
+import java.security.cert.X509Certificate;
 import java.text.Collator;
 
 import java.util.ArrayList;
@@ -68,6 +69,25 @@
     };
 
     /**
+     * Returns true if the certificate is self-signed, false otherwise.
+     */
+    public static boolean isSelfSigned(X509Certificate cert) {
+        return signedBy(cert, cert);
+    }
+
+    public static boolean signedBy(X509Certificate end, X509Certificate ca) {
+        if (!ca.getSubjectX500Principal().equals(end.getIssuerX500Principal())) {
+            return false;
+        }
+        try {
+            end.verify(ca.getPublicKey());
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns true if KeyStore has a password. This is true except for
      * MSCAPI KeyStores
      */
--- a/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Dec 02 16:44:57 2015 +0800
@@ -1297,7 +1297,7 @@
         for (Certificate ca: keyStore.getCertificateChain(alias)) {
             if (ca instanceof X509Certificate) {
                 X509Certificate xca = (X509Certificate)ca;
-                if (!isSelfSigned(xca)) {
+                if (!KeyStoreUtil.isSelfSigned(xca)) {
                     dumpCert(xca, out);
                 }
             }
@@ -2705,7 +2705,7 @@
 
         // if certificate is self-signed, make sure it verifies
         boolean selfSigned = false;
-        if (isSelfSigned(cert)) {
+        if (KeyStoreUtil.isSelfSigned(cert)) {
             cert.verify(cert.getPublicKey());
             selfSigned = true;
         }
@@ -2965,25 +2965,6 @@
     }
 
     /**
-     * Returns true if the certificate is self-signed, false otherwise.
-     */
-    private boolean isSelfSigned(X509Certificate cert) {
-        return signedBy(cert, cert);
-    }
-
-    private boolean signedBy(X509Certificate end, X509Certificate ca) {
-        if (!ca.getSubjectDN().equals(end.getIssuerDN())) {
-            return false;
-        }
-        try {
-            end.verify(ca.getPublicKey());
-            return true;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /**
      * Locates a signer for a given certificate from a given keystore and
      * returns the signer's certificate.
      * @param cert the certificate whose signer is searched, not null
@@ -3320,7 +3301,7 @@
             // find a cert in the reply who signs thisCert
             int j;
             for (j=i; j<replyCerts.length; j++) {
-                if (signedBy(thisCert, (X509Certificate)replyCerts[j])) {
+                if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) {
                     tmpCert = replyCerts[i];
                     replyCerts[i] = replyCerts[j];
                     replyCerts[j] = tmpCert;
@@ -3451,7 +3432,7 @@
                         Vector<Certificate> chain,
                         Hashtable<Principal, Vector<Certificate>> certs) {
         Principal issuer = certToVerify.getIssuerDN();
-        if (isSelfSigned(certToVerify)) {
+        if (KeyStoreUtil.isSelfSigned(certToVerify)) {
             // reached self-signed root cert;
             // no verification needed because it's trusted.
             chain.addElement(certToVerify);
--- a/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Wed Dec 02 16:44:57 2015 +0800
@@ -150,6 +150,7 @@
     private Date expireDate = new Date(0L);     // used in noTimestamp warning
 
     // Severe warnings
+    private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg
     private boolean hasExpiredCert = false;
     private boolean notYetValidCert = false;
     private boolean chainNotValidated = false;
@@ -159,6 +160,9 @@
     private boolean badKeyUsage = false;
     private boolean badExtendedKeyUsage = false;
     private boolean badNetscapeCertType = false;
+    private boolean signerSelfSigned = false;
+
+    private Throwable chainNotValidatedReason = null;
 
     CertificateFactory certificateFactory;
     CertPathValidator validator;
@@ -243,7 +247,7 @@
 
         if (strict) {
             int exitCode = 0;
-            if (chainNotValidated || hasExpiredCert || notYetValidCert) {
+            if (weakAlg != 0 || chainNotValidated || hasExpiredCert || notYetValidCert || signerSelfSigned) {
                 exitCode |= 4;
             }
             if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
@@ -780,6 +784,12 @@
             if (man == null)
                 System.out.println(rb.getString("no.manifest."));
 
+            // If signer is a trusted cert or private entry in user's own
+            // keystore, it can be self-signed.
+            if (!aliasNotInStore) {
+                signerSelfSigned = false;
+            }
+
             if (!anySigned) {
                 System.out.println(rb.getString(
                       "jar.is.unsigned.signatures.missing.or.not.parsable."));
@@ -788,7 +798,7 @@
                 boolean errorAppeared = false;
                 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
                         notYetValidCert || chainNotValidated || hasExpiredCert ||
-                        hasUnsignedEntry ||
+                        hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
                         aliasNotInStore || notSignedByAlias) {
 
                     if (strict) {
@@ -803,6 +813,12 @@
                         warningAppeared = true;
                     }
 
+                    if (weakAlg != 0) {
+                        // In fact, jarsigner verification did not catch this
+                        // since it has not read the JarFile content itself.
+                        // Everything is done with JarFile API.
+                    }
+
                     if (badKeyUsage) {
                         System.out.println(
                             rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
@@ -832,8 +848,9 @@
                     }
 
                     if (chainNotValidated) {
-                        System.out.println(
-                                rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated."));
+                        System.out.println(String.format(
+                                rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1"),
+                                chainNotValidatedReason.getLocalizedMessage()));
                     }
 
                     if (notSignedByAlias) {
@@ -844,6 +861,11 @@
                     if (aliasNotInStore) {
                         System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
                     }
+
+                    if (signerSelfSigned) {
+                        System.out.println(rb.getString(
+                                "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
+                    }
                 } else {
                     System.out.println(rb.getString("jar.verified."));
                 }
@@ -1074,7 +1096,25 @@
     }
 
     void signJar(String jarName, String alias)
-        throws Exception {
+            throws Exception {
+
+        DisabledAlgorithmConstraints dac =
+                new DisabledAlgorithmConstraints(
+                        DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
+
+        if (digestalg != null && !dac.permits(
+                Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) {
+            weakAlg |= 1;
+        }
+        if (tSADigestAlg != null && !dac.permits(
+                Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) {
+            weakAlg |= 4;
+        }
+        if (sigalg != null && !dac.permits(
+                Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) {
+            weakAlg |= 2;
+        }
+
         boolean aliasUsed = false;
         X509Certificate tsaCert = null;
 
@@ -1255,8 +1295,8 @@
             }
 
             boolean warningAppeared = false;
-            if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
-                    notYetValidCert || chainNotValidated || hasExpiredCert) {
+            if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+                    notYetValidCert || chainNotValidated || hasExpiredCert || signerSelfSigned) {
                 if (strict) {
                     System.out.println(rb.getString("jar.signed.with.signer.errors."));
                     System.out.println();
@@ -1292,8 +1332,31 @@
                 }
 
                 if (chainNotValidated) {
+                    System.out.println(String.format(
+                            rb.getString("The.signer.s.certificate.chain.is.not.validated.reason.1"),
+                            chainNotValidatedReason.getLocalizedMessage()));
+                }
+
+                if (signerSelfSigned) {
                     System.out.println(
-                            rb.getString("The.signer.s.certificate.chain.is.not.validated."));
+                            rb.getString("The.signer.s.certificate.is.self.signed."));
+                }
+
+                if ((weakAlg & 1) == 1) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            digestalg, "-digestalg"));
+                }
+
+                if ((weakAlg & 2) == 2) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            sigalg, "-sigalg"));
+                }
+                if ((weakAlg & 4) == 4) {
+                    System.out.println(String.format(
+                            rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
+                            tSADigestAlg, "-tsadigestalg"));
                 }
             } else {
                 System.out.println(rb.getString("jar.signed."));
@@ -1377,10 +1440,15 @@
                 // No more warning, we alreay have hasExpiredCert or notYetValidCert
             } else {
                 chainNotValidated = true;
+                chainNotValidatedReason = e;
                 sb.append(tab).append(rb.getString(".CertPath.not.validated."))
                         .append(e.getLocalizedMessage()).append("]\n"); // TODO
             }
         }
+        if (certs.size() == 1
+                && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {
+            signerSelfSigned = true;
+        }
         String result = sb.toString();
         cacheForSignerInfo.put(signer, result);
         return result;
@@ -1645,12 +1713,17 @@
                 if (e.getCause() != null &&
                         (e.getCause() instanceof CertificateExpiredException ||
                         e.getCause() instanceof CertificateNotYetValidException)) {
-                    // No more warning, we alreay have hasExpiredCert or notYetValidCert
+                    // No more warning, we already have hasExpiredCert or notYetValidCert
                 } else {
                     chainNotValidated = true;
+                    chainNotValidatedReason = e;
                 }
             }
 
+            if (KeyStoreUtil.isSelfSigned(certChain[0])) {
+                signerSelfSigned = true;
+            }
+
             try {
                 if (!token && keypass == null)
                     key = store.getKey(alias, storepass);
--- a/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java	Wed Dec 02 16:44:57 2015 +0800
@@ -212,6 +212,8 @@
                 "This jar contains entries whose signer certificate will expire within six months. "},
         {"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.",
                 "This jar contains entries whose signer certificate is not yet valid. "},
+        {"This.jar.contains.entries.whose.signer.certificate.is.self.signed.",
+                "This jar contains entries whose signer certificate is self-signed."},
         {"Re.run.with.the.verbose.option.for.more.details.",
                 "Re-run with the -verbose option for more details."},
         {"Re.run.with.the.verbose.and.certs.options.for.more.details.",
@@ -236,10 +238,14 @@
                  "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
         {".{0}.extension.does.not.support.code.signing.",
                  "[{0} extension does not support code signing]"},
-        {"The.signer.s.certificate.chain.is.not.validated.",
-                "The signer's certificate chain is not validated."},
-        {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.",
-                 "This jar contains entries whose certificate chain is not validated."},
+        {"The.signer.s.certificate.chain.is.not.validated.reason.1",
+                "The signer's certificate chain is not validated. Reason: %s"},
+        {"The.signer.s.certificate.is.self.signed.",
+                "The signer's certificate is self-signed."},
+        {"The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.",
+                "The %1$s algorithm specified for the %2$s option is considered a security risk."},
+        {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1",
+                 "This jar contains entries whose certificate chain is not validated. Reason: %s"},
         {"no.timestamp.signing",
                 "No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."},
         {"no.timestamp.verifying",
--- a/jdk/test/sun/security/tools/jarsigner/TsacertOptionTest.java	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/TsacertOptionTest.java	Wed Dec 02 16:44:57 2015 +0800
@@ -52,6 +52,7 @@
             + ".txt";
     private static final String PASSWORD = "changeit";
     private static final String KEYSTORE = "ks.jks";
+    private static final String CA_KEY_ALIAS = "ca";
     private static final String SIGNING_KEY_ALIAS = "sign_alias";
     private static final String TSA_KEY_ALIAS = "ts";
     private static final String KEY_ALG = "RSA";
@@ -79,20 +80,52 @@
 
         // look for free network port for TSA service
         int port = jdk.testlibrary.Utils.getFreePort();
-        String host = jdk.testlibrary.Utils.getHostname();
+        String host = "127.0.0.1";
         String tsaUrl = "http://" + host + ":" + port;
 
         // create key pair for jar signing
         ProcessTools.executeCommand(KEYTOOL,
                 "-genkey",
+                "-alias", CA_KEY_ALIAS,
+                "-keyalg", KEY_ALG,
+                "-keysize", Integer.toString(KEY_SIZE),
+                "-keystore", KEYSTORE,
+                "-storepass", PASSWORD,
+                "-keypass", PASSWORD,
+                "-dname", "CN=CA",
+                "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0);
+        ProcessTools.executeCommand(KEYTOOL,
+                "-genkey",
                 "-alias", SIGNING_KEY_ALIAS,
                 "-keyalg", KEY_ALG,
                 "-keysize", Integer.toString(KEY_SIZE),
                 "-keystore", KEYSTORE,
                 "-storepass", PASSWORD,
                 "-keypass", PASSWORD,
-                "-dname", "CN=Test",
-                "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0);
+                "-dname", "CN=Test").shouldHaveExitValue(0);
+        ProcessTools.executeCommand(KEYTOOL,
+                "-certreq",
+                "-alias", SIGNING_KEY_ALIAS,
+                "-keystore", KEYSTORE,
+                "-storepass", PASSWORD,
+                "-keypass", PASSWORD,
+                "-file", "certreq").shouldHaveExitValue(0);
+        ProcessTools.executeCommand(KEYTOOL,
+                "-gencert",
+                "-alias", CA_KEY_ALIAS,
+                "-keystore", KEYSTORE,
+                "-storepass", PASSWORD,
+                "-keypass", PASSWORD,
+                "-validity", Integer.toString(VALIDITY),
+                "-infile", "certreq",
+                "-outfile", "cert").shouldHaveExitValue(0);
+        ProcessTools.executeCommand(KEYTOOL,
+                "-importcert",
+                "-alias", SIGNING_KEY_ALIAS,
+                "-keystore", KEYSTORE,
+                "-storepass", PASSWORD,
+                "-keypass", PASSWORD,
+                "-file", "cert").shouldHaveExitValue(0);
 
         // create key pair for TSA service
         // SubjectInfoAccess extension contains URL to TSA service
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/Warning.java	Wed Dec 02 16:44:57 2015 +0800
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.JarUtils;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.ProcessTools;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+/**
+ * @test
+ * @bug 8024302 8026037 8130132
+ * @summary warnings, errors and -strict
+ * @library /lib/testlibrary
+ */
+public class Warning {
+
+    public static void main(String[] args) throws Throwable {
+
+        Files.deleteIfExists(Paths.get("ks"));
+
+        newCert("ca", "-validity 365000");
+
+        recreateJar();
+
+        newCert("a");
+        run("jarsigner", "a.jar a")
+                .shouldContain("is self-signed");
+        run("jarsigner", "a.jar a -strict")
+                .shouldContain("is self-signed")
+                .shouldHaveExitValue(4);
+        // Trusted entry can be self-signed without a warning
+        run("jarsigner", "-verify a.jar")
+                .shouldNotContain("is self-signed")
+                .shouldNotContain("not signed by alias in this keystore");
+        run("keytool", "-delete -alias a");
+        // otherwise a warning will be shown
+        run("jarsigner", "-verify a.jar")
+                .shouldContain("is self-signed")
+                .shouldContain("not signed by alias in this keystore");
+
+        recreateJar();
+
+        newCert("b");
+        issueCert("b");
+        run("jarsigner", "a.jar b")
+                .shouldNotContain("is self-signed");
+        run("jarsigner", "-verify a.jar")
+                .shouldNotContain("is self-signed");
+
+        run("jarsigner", "a.jar b -digestalg MD5")
+                .shouldContain("-digestalg option is considered a security risk.");
+        run("jarsigner", "a.jar b -digestalg MD5 -strict")
+                .shouldHaveExitValue(4)
+                .shouldContain("-digestalg option is considered a security risk.");
+        run("jarsigner", "a.jar b -sigalg MD5withRSA")
+                .shouldContain("-sigalg option is considered a security risk");
+
+        issueCert("b", "-sigalg MD5withRSA");
+        run("jarsigner", "a.jar b")
+                .shouldMatch("chain is not validated. Reason:.*MD5withRSA");
+
+        recreateJar();
+
+        newCert("c", "-keysize 512");
+        issueCert("c");
+        run("jarsigner", "a.jar c")
+                .shouldContain("chain is not validated. " +
+                        "Reason: algorithm constraints check failed");
+
+        recreateJar();
+
+        newCert("s1"); issueCert("s1", "-startdate 2000/01/01 -validity 36525");
+        run("jarsigner", "a.jar s1")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp").shouldContain("2100-01-01")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s1 -strict")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp").shouldContain("2100-01-01")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s1 -verify")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp").shouldContain("2100-01-01")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s1 -verify -strict")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp").shouldContain("2100-01-01")
+                .shouldNotContain("with signer errors");
+
+        recreateJar();
+
+        newCert("s2"); issueCert("s2", "-validity 100");
+        run("jarsigner", "a.jar s2")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp")
+                .shouldContain("will expire")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s2 -strict")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp")
+                .shouldContain("will expire")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s2 -verify")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp")
+                .shouldContain("will expire")
+                .shouldNotContain("with signer errors");
+        run("jarsigner", "a.jar s2 -verify -strict")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("Error:")
+                .shouldContain("timestamp")
+                .shouldContain("will expire")
+                .shouldNotContain("with signer errors");
+
+        recreateJar();
+
+        newCert("s3"); issueCert("s3", "-startdate -200d -validity 100");
+        run("jarsigner", "a.jar s3")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldContain("has expired")
+                .shouldNotContain("with signer errors")
+                .shouldNotContain("Error:");
+        run("jarsigner", "a.jar s3 -strict")
+                .shouldHaveExitValue(4)
+                .shouldContain("with signer errors")
+                .shouldMatch("(?s).*Error:.*has expired.*Warning:.*");
+        run("jarsigner", "a.jar s3 -verify")
+                .shouldHaveExitValue(0)
+                .shouldContain("Warning:")
+                .shouldNotContain("with signer errors")
+                .shouldNotContain("Error:");
+        run("jarsigner", "a.jar s3 -verify -strict")
+                .shouldHaveExitValue(4)
+                .shouldContain("with signer errors")
+                .shouldMatch("(?s).*Error:.*has expired.*Warning:.*");
+    }
+
+    // Creates a new jar without signature
+    static void recreateJar() throws Exception {
+        JarUtils.createJar("a.jar", "ks");
+    }
+
+    // Creates a self-signed cert for alias with zero or more -genkey options
+    static void newCert(String alias, String... more) throws Exception {
+        String args = "-genkeypair -alias " + alias + " -dname CN=" + alias;
+        for (String s: more) {
+            args += " " + s;
+        }
+        run("keytool", args).shouldHaveExitValue(0);
+    }
+
+    // Asks ca to issue a cert to alias with zero or more -gencert options
+    static void issueCert(String alias, String...more) throws Exception {
+        String req = run("keytool", "-certreq -alias " + alias)
+                .shouldHaveExitValue(0).getStdout();
+        String args = "-gencert -alias ca -rfc";
+        for (String s: more) {
+            args += " " + s;
+        }
+        String cert = run("keytool", args, req)
+                .shouldHaveExitValue(0).getStdout();
+        run("keytool", "-import -alias " + alias, cert).shouldHaveExitValue(0);
+    }
+
+    // Runs a java tool with command line arguments
+    static OutputAnalyzer run(String command, String args)
+            throws Exception {
+        return run(command, args, null);
+    }
+
+    // Runs a java tool with command line arguments and an optional input block
+    static OutputAnalyzer run(String command, String args, String input)
+            throws Exception {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK(command);
+        launcher.addVMArg("-Duser.language=en").addVMArg("-Duser.country=US");
+        switch (command) {
+            case "keytool":
+                for (String s: new String[] {
+                        "-keystore", "ks", "-storepass", "changeit",
+                        "-storetype", "jks",
+                        "-keypass", "changeit", "-keyalg", "rsa", "-debug"}) {
+                    launcher.addToolArg(s);
+                }
+                break;
+            case "jarsigner":
+                for (String s: new String[] {
+                        "-keystore", "ks", "-storepass", "changeit",
+                        "-storetype", "jks"}) {
+                    launcher.addToolArg(s);
+                }
+                break;
+        }
+        for (String arg: args.split(" ")) {
+            launcher.addToolArg(arg);
+        }
+        String[] cmd = launcher.getCommand();
+        ProcessBuilder pb = new ProcessBuilder(cmd);
+        OutputAnalyzer out = ProcessTools.executeProcess(pb, input);
+        System.out.println("======================");
+        System.out.println(Arrays.toString(cmd));
+        String msg = " stdout: [" + out.getStdout() + "];\n"
+                + " stderr: [" + out.getStderr() + "]\n"
+                + " exitValue = " + out.getExitValue() + "\n";
+        System.out.println(msg);
+        return out;
+    }
+}
+
--- a/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -140,19 +140,19 @@
 # 16 and 32 already covered in the first part
 # ==========================================================
 
-$KT -genkeypair -alias expired -dname CN=expired -startdate -10m
-$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid -startdate +1m
-$KT -genkeypair -alias badku -dname CN=badku -ext KU=cRLSign -validity 365
-$KT -genkeypair -alias badeku -dname CN=badeku -ext EKU=sa -validity 365
-$KT -genkeypair -alias goodku -dname CN=goodku -ext KU=dig -validity 365
-$KT -genkeypair -alias goodeku -dname CN=goodeku -ext EKU=codesign -validity 365
-
-# badchain signed by ca, but ca is removed later
-$KT -genkeypair -alias badchain -dname CN=badchain -validity 365
 $KT -genkeypair -alias ca -dname CN=ca -ext bc -validity 365
-$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
-        $KT -importcert -alias badchain
-$KT -delete -alias ca
+$KT -genkeypair -alias expired -dname CN=expired
+$KT -certreq -alias expired | $KT -gencert -alias ca -startdate -10m | $KT -import -alias expired
+$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid
+$KT -certreq -alias notyetvalid | $KT -gencert -alias ca -startdate +1m | $KT -import -alias notyetvalid
+$KT -genkeypair -alias badku -dname CN=badku
+$KT -certreq -alias badku | $KT -gencert -alias ca -ext KU=cRLSign -validity 365 | $KT -import -alias badku
+$KT -genkeypair -alias badeku -dname CN=badeku
+$KT -certreq -alias badeku | $KT -gencert -alias ca -ext EKU=sa -validity 365 | $KT -import -alias badeku
+$KT -genkeypair -alias goodku -dname CN=goodku
+$KT -certreq -alias goodku | $KT -gencert -alias ca -ext KU=dig -validity 365 | $KT -import -alias goodku
+$KT -genkeypair -alias goodeku -dname CN=goodeku
+$KT -certreq -alias goodeku | $KT -gencert -alias ca -ext EKU=codesign -validity 365 | $KT -import -alias goodeku
 
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar expired
 [ $? = 4 ] || exit $LINENO
@@ -172,6 +172,12 @@
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar goodeku
 [ $? = 0 ] || exit $LINENO
 
+# badchain signed by ca, but ca is removed later
+$KT -genkeypair -alias badchain -dname CN=badchain -validity 365
+$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
+        $KT -importcert -alias badchain
+$KT -delete -alias ca
+
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar badchain
 [ $? = 4 ] || exit $LINENO
 
@@ -182,18 +188,22 @@
 # Third part: -certchain test
 # ==========================================================
 
-# altchain signed by ca2, but ca2 is removed later
+# altchain signed by ca2
 $KT -genkeypair -alias altchain -dname CN=altchain -validity 365
 $KT -genkeypair -alias ca2 -dname CN=ca2 -ext bc -validity 365
 $KT -certreq -alias altchain | $KT -gencert -alias ca2 -validity 365 -rfc > certchain
 $KT -exportcert -alias ca2 -rfc >> certchain
-$KT -delete -alias ca2
 
-# Now altchain is still self-signed
+# Self-signed cert does not work
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar altchain
+[ $? = 4 ] || exit $LINENO
+
+# -certchain works
+$JARSIGNER -strict -keystore $KS -storepass changeit -certchain certchain a.jar altchain
 [ $? = 0 ] || exit $LINENO
 
-# If -certchain is used, then it's bad
+# but if ca2 is removed, -certchain does not work
+$KT -delete -alias ca2
 $JARSIGNER -strict -keystore $KS -storepass changeit -certchain certchain a.jar altchain
 [ $? = 4 ] || exit $LINENO
 
--- a/jdk/test/sun/security/tools/jarsigner/default_options.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/default_options.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -31,19 +31,21 @@
   TESTJAVA=`dirname $JAVAC_CMD`/..
 fi
 
+PASS=changeit
+export PASS
+
 KS=ks
-KEYTOOL="$TESTJAVA/bin/keytool ${TESTTOOLVMOPTS}"
+KEYTOOL="$TESTJAVA/bin/keytool ${TESTTOOLVMOPTS} -storepass:env PASS -keypass:env PASS -keystore $KS"
 JAR="$TESTJAVA/bin/jar ${TESTTOOLVMOPTS}"
 JARSIGNER="$TESTJAVA/bin/jarsigner ${TESTTOOLVMOPTS}"
 
 rm $KS 2> /dev/null
 
-PASS=changeit
-export PASS
-
-$KEYTOOL -genkeypair -dname CN=A -alias a \
-         -storepass:env PASS -keypass:env PASS -keystore $KS \
-         -keyalg rsa || exit 1
+$KEYTOOL -genkeypair -dname CN=A -alias a -keyalg rsa || exit 1
+$KEYTOOL -genkeypair -dname CN=CA -alias ca -keyalg rsa || exit 2
+$KEYTOOL -alias a -certreq |
+    $KEYTOOL -alias ca -gencert |
+    $KEYTOOL -alias a -import || exit 3
 
 cat <<EOF > js.conf
 jarsigner.all = -keystore \${user.dir}/$KS -storepass:env PASS -debug -strict
--- a/jdk/test/sun/security/tools/jarsigner/ec.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/ec.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -53,11 +53,20 @@
 echo A > A
 $JAR cvf $JFILE A
 
-$KT -alias a -dname CN=a -keyalg ec -genkey -validity 300 || exit 11
-$KT -alias b -dname CN=b -keyalg ec -genkey -validity 300 || exit 12
+$KT -alias ca -dname CN=ca -keyalg ec -genkey -validity 300 || exit 11
+
+$KT -alias a -dname CN=a -keyalg ec -genkey || exit 11
+$KT -alias a -certreq | $KT -gencert -alias ca -validity 300 | $KT -import -alias a || exit 111
+
+$KT -alias b -dname CN=b -keyalg ec -genkey || exit 12
+$KT -alias b -certreq | $KT -gencert -alias ca -validity 300 | $KT -import -alias b || exit 121
+
 # Ensure that key length is sufficient for the intended hash (SHA512withECDSA)
-$KT -alias c -dname CN=c -keyalg ec -genkey -validity 300 -keysize 521 || exit 13
+$KT -alias c -dname CN=c -keyalg ec -genkey -keysize 521 || exit 13
+$KT -alias c -certreq | $KT -gencert -alias ca -validity 300 | $KT -import -alias c || exit 131
+
 $KT -alias x -dname CN=x -keyalg ec -genkey -validity 300 || exit 14
+$KT -alias x -certreq | $KT -gencert -alias ca -validity 300 | $KT -import -alias x || exit 141
 
 $JARSIGNER -keystore $KS -storepass changeit $JFILE a -debug -strict || exit 21
 $JARSIGNER -keystore $KS -storepass changeit $JFILE b -debug -strict -sigalg SHA1withECDSA || exit 22
--- a/jdk/test/sun/security/tools/jarsigner/onlymanifest.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/onlymanifest.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -57,12 +57,14 @@
 echo "Key: Value" > manifest
 $JAR cvfm $JFILE manifest
 
-$KT -alias a -dname CN=a -genkey -validity 300 || exit 1
-$JARSIGNER -keystore $KS -storepass changeit $JFILE a -debug -strict || exit 2
+$KT -alias ca -dname CN=ca -genkey -validity 300 || exit 1
+$KT -alias a -dname CN=a -genkey -validity 300 || exit 2
+$KT -alias a -certreq | $KT -gencert -alias ca -validity 300 | $KT -import -alias a || exit 3
+$JARSIGNER -keystore $KS -storepass changeit $JFILE a -debug -strict || exit 4
 $JARSIGNER -keystore $KS -storepass changeit -verify $JFILE a -debug -strict \
-        > onlymanifest.out || exit 3
+        > onlymanifest.out || exit 5
 
-grep unsigned onlymanifest.out && exit 4
+grep unsigned onlymanifest.out && exit 6
 
 exit 0
 
--- a/jdk/test/sun/security/tools/jarsigner/ts.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/ts.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -73,6 +73,10 @@
 $KT -alias tsbad1 -genkeypair -dname CN=tsbad1
 $KT -alias tsbad2 -genkeypair -dname CN=tsbad2
 $KT -alias tsbad3 -genkeypair -dname CN=tsbad3
+
+$KT -alias old -certreq | \
+        $KT -alias ca -gencert | \
+        $KT -alias old -importcert
 $KT -alias ts -certreq | \
         $KT -alias ca -gencert -ext eku:critical=ts | \
         $KT -alias ts -importcert
--- a/jdk/test/sun/security/tools/jarsigner/warning.sh	Wed Dec 02 16:44:54 2015 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-#
-# Copyright (c) 2013, 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 8024302
-# @bug 8026037
-# @summary Clarify jar verifications
-#
-
-if [ "${TESTJAVA}" = "" ] ; then
-  JAVAC_CMD=`which javac`
-  TESTJAVA=`dirname $JAVAC_CMD`/..
-fi
-
-# set platform-dependent variables
-OS=`uname -s`
-case "$OS" in
-  Windows_* )
-    FS="\\"
-    ;;
-  * )
-    FS="/"
-    ;;
-esac
-
-KS=warnings.jks
-JFILE=warnings.jar
-
-KT="$TESTJAVA${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -storepass changeit -keypass changeit \
-        -keystore $KS"
-JAR="$TESTJAVA${FS}bin${FS}jar ${TESTTOOLVMOPTS}"
-JARSIGNER="$TESTJAVA${FS}bin${FS}jarsigner ${TESTTOOLVMOPTS} -keystore $KS -storepass changeit"
-
-rm $KS 2> /dev/null
-
-LANG=C
-export LANG
-
-echo 12345 > file
-
-ERR=""
-
-# Normal signer expiring on 2100-01-01
-$KT -alias s1 -dname CN=s1 -genkey -startdate 2000/01/01 -validity 36525 || ERR="$ERR keytool s1,"
-# Cert expiring soon, informational warning
-$KT -alias s2 -dname CN=s2 -genkey -validity 100 || ERR="$ERR keytool s2,"
-# Cert expired, severe warning
-$KT -alias s3 -dname CN=s3 -genkey -startdate -200d -validity 100 || ERR="$ERR keytool s3,"
-
-# noTimestamp is informatiional warning and includes a date
-$JAR cvf $JFILE file
-$JARSIGNER $JFILE s1 > output1 || ERR="$ERR jarsigner s1,"
-$JARSIGNER -strict $JFILE s1 >> output1 || ERR="$ERR jarsigner s1 strict,"
-$JARSIGNER -verify $JFILE s1 >> output1 || ERR="$ERR jarsigner s1,"
-$JARSIGNER -verify -strict $JFILE s1 >> output1 || ERR="$ERR jarsigner s1 strict,"
-
-cat output1 | grep Warning || ERR="$ERR s1 warning,"
-cat output1 | grep Error && ERR="$ERR s1 error,"
-cat output1 | grep timestamp | grep 2100-01-01 || ERR="$ERR s1 timestamp,"
-cat output1 | grep "with signer errors" && ERR="$ERR s1 err,"
-
-# hasExpiringCert is informatiional warning
-$JAR cvf $JFILE file
-$JARSIGNER $JFILE s2 > output2 || ERR="$ERR jarsigner s2,"
-$JARSIGNER -strict $JFILE s2 >> output2 || ERR="$ERR jarsigner s2 strict,"
-$JARSIGNER -verify $JFILE s2 >> output2 || ERR="$ERR jarsigner s2,"
-$JARSIGNER -verify -strict $JFILE s2 >> output2 || ERR="$ERR jarsigner s2 strict,"
-
-cat output2 | grep Warning || ERR="$ERR s2 warning,"
-cat output2 | grep Error && ERR="$ERR s2 error,"
-cat output2 | grep timestamp || ERR="$ERR s2 timestamp,"
-cat output2 | grep "will expire" || ERR="$ERR s2 expiring,"
-cat output2 | grep "with signer errors" && ERR="$ERR s2 err,"
-
-# hasExpiredCert is severe warning
-$JAR cvf $JFILE file
-$JARSIGNER $JFILE s3 > output3 || ERR="$ERR jarsigner s3,"
-$JARSIGNER -strict $JFILE s3 > output3s && ERR="$ERR jarsigner s3 strict,"
-$JARSIGNER -verify $JFILE s3 >> output3 || ERR="$ERR jarsigner s3,"
-$JARSIGNER -verify -strict $JFILE s3 >> output3s && ERR="$ERR jarsigner s3 strict,"
-
-# warning without -strict
-cat output3 | grep Warning || ERR="$ERR s3 warning,"
-cat output3 | grep Error && ERR="$ERR s3 error,"
-cat output3 | grep "with signer errors" && ERR="$ERR s3 err,"
-
-# error with -strict
-cat output3s | grep Warning || ERR="$ERR s3s warning,"
-cat output3s | grep Error || ERR="$ERR s3s error,"
-cat output3s | grep "with signer errors" || ERR="$ERR s3 err,"
-
-if [ "$ERR" = "" ]; then
-    exit 0
-else
-    echo "ERR is $ERR"
-    exit 1
-fi
--- a/jdk/test/sun/security/tools/jarsigner/weaksize.sh	Wed Dec 02 16:44:54 2015 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/weaksize.sh	Wed Dec 02 16:44:57 2015 +0800
@@ -52,9 +52,9 @@
 $JAR cvf a.jar ks
 
 # We always trust a TrustedCertificateEntry
-$JS a.jar ca || exit 1
+$JS a.jar ca | grep "chain is not validated" && exit 1
 
 # An end-entity cert must follow algorithm constraints
-$JS a.jar signer && exit 2
+$JS a.jar signer | grep "chain is not validated" || exit 2
 
 exit 0