8172529: Use PKIXValidator in jarsigner
authorweijun
Wed, 18 Jan 2017 08:02:53 +0800
changeset 43183 b50e0f90d284
parent 43182 66e6655abfde
child 43184 ac0aed3194d3
child 43195 e7f80841643d
8172529: Use PKIXValidator in jarsigner Reviewed-by: xuelei, mullan, alanb
jdk/src/java.base/share/classes/module-info.java
jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh
--- a/jdk/src/java.base/share/classes/module-info.java	Tue Jan 17 11:34:47 2017 -0800
+++ b/jdk/src/java.base/share/classes/module-info.java	Wed Jan 18 08:02:53 2017 +0800
@@ -290,6 +290,8 @@
         jdk.crypto.token,
         jdk.jartool,
         jdk.security.auth;
+    exports sun.security.validator to
+        jdk.jartool;
     exports sun.text.resources to
         jdk.localedata;
     exports sun.util.cldr to
--- a/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Tue Jan 17 11:34:47 2017 -0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Wed Jan 18 08:02:53 2017 +0800
@@ -26,6 +26,8 @@
 package sun.security.tools.jarsigner;
 
 import java.io.*;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.PKIXBuilderParameters;
 import java.util.*;
 import java.util.zip.*;
 import java.util.jar.*;
@@ -40,11 +42,9 @@
 import java.net.SocketTimeoutException;
 import java.net.URL;
 import java.security.cert.CertPath;
-import java.security.cert.CertPathValidator;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.PKIXParameters;
 import java.security.cert.TrustAnchor;
 import java.util.Map.Entry;
 
@@ -54,6 +54,8 @@
 import sun.security.pkcs.SignerInfo;
 import sun.security.timestamp.TimestampToken;
 import sun.security.tools.KeyStoreUtil;
+import sun.security.validator.Validator;
+import sun.security.validator.ValidatorException;
 import sun.security.x509.*;
 import sun.security.util.*;
 
@@ -177,9 +179,7 @@
 
     private boolean seeWeak = false;
 
-    CertificateFactory certificateFactory;
-    CertPathValidator validator;
-    PKIXParameters pkixParameters;
+    PKIXBuilderParameters pkixParameters;
 
     public void run(String args[]) {
         try {
@@ -1623,19 +1623,10 @@
         try {
             validateCertChain(certs);
         } catch (Exception e) {
-            if (debug) {
-                e.printStackTrace();
-            }
-            if (e.getCause() != null &&
-                    (e.getCause() instanceof CertificateExpiredException ||
-                     e.getCause() instanceof CertificateNotYetValidException)) {
-                // 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
-            }
+            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))) {
@@ -1654,9 +1645,6 @@
         }
 
         try {
-
-            certificateFactory = CertificateFactory.getInstance("X.509");
-            validator = CertPathValidator.getInstance("PKIX");
             Set<TrustAnchor> tas = new HashSet<>();
             try {
                 KeyStore caks = KeyStoreUtil.getCacertsKeyStore();
@@ -1732,7 +1720,7 @@
                 }
             } finally {
                 try {
-                    pkixParameters = new PKIXParameters(tas);
+                    pkixParameters = new PKIXBuilderParameters(tas, null);
                     pkixParameters.setRevocationEnabled(false);
                 } catch (InvalidAlgorithmParameterException ex) {
                     // Only if tas is empty
@@ -1899,17 +1887,8 @@
             try {
                 validateCertChain(Arrays.asList(certChain));
             } catch (Exception e) {
-                if (debug) {
-                    e.printStackTrace();
-                }
-                if (e.getCause() != null &&
-                        (e.getCause() instanceof CertificateExpiredException ||
-                        e.getCause() instanceof CertificateNotYetValidException)) {
-                    // No more warning, we already have hasExpiredCert or notYetValidCert
-                } else {
-                    chainNotValidated = true;
-                    chainNotValidatedReason = e;
-                }
+                chainNotValidated = true;
+                chainNotValidatedReason = e;
             }
 
             if (KeyStoreUtil.isSelfSigned(certChain[0])) {
@@ -1966,18 +1945,40 @@
     }
 
     void validateCertChain(List<? extends Certificate> certs) throws Exception {
-        int cpLen = 0;
-        out: for (; cpLen<certs.size(); cpLen++) {
-            for (TrustAnchor ta: pkixParameters.getTrustAnchors()) {
-                if (ta.getTrustedCert().equals(certs.get(cpLen))) {
-                    break out;
+        try {
+            Validator.getInstance(Validator.TYPE_PKIX,
+                    Validator.VAR_CODE_SIGNING,
+                    pkixParameters)
+                    .validate(certs.toArray(new X509Certificate[certs.size()]));
+        } catch (Exception e) {
+            if (debug) {
+                e.printStackTrace();
+            }
+            if (e instanceof ValidatorException) {
+                // Throw cause if it's CertPathValidatorException,
+                if (e.getCause() != null &&
+                        e.getCause() instanceof CertPathValidatorException) {
+                    e = (Exception) e.getCause();
+                    Throwable t = e.getCause();
+                    if ((t instanceof CertificateExpiredException &&
+                                hasExpiredCert) ||
+                            (t instanceof CertificateNotYetValidException &&
+                                    notYetValidCert)) {
+                        // we already have hasExpiredCert and notYetValidCert
+                        return;
+                    }
+                }
+                if (e instanceof ValidatorException) {
+                    ValidatorException ve = (ValidatorException)e;
+                    if (ve.getErrorType() == ValidatorException.T_EE_EXTENSIONS &&
+                            (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType)) {
+                        // We already have badKeyUsage, badExtendedKeyUsage
+                        // and badNetscapeCertType
+                        return;
+                    }
                 }
             }
-        }
-        if (cpLen > 0) {
-            CertPath cp = certificateFactory.generateCertPath(
-                    (cpLen == certs.size())? certs: certs.subList(0, cpLen));
-            validator.validate(cp, pkixParameters);
+            throw e;
         }
     }
 
--- a/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh	Tue Jan 17 11:34:47 2017 -0800
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh	Wed Jan 18 08:02:53 2017 +0800
@@ -22,7 +22,7 @@
 #
 
 # @test
-# @bug 6802846
+# @bug 6802846 8172529
 # @summary jarsigner needs enhanced cert validation(options)
 #
 # @run shell/timeout=240 concise_jarsigner.sh
@@ -52,7 +52,7 @@
 KS=js.ks
 KT="$TESTJAVA${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -storepass changeit -keypass changeit -keystore $KS -keyalg rsa -keysize 1024"
 JAR="$TESTJAVA${FS}bin${FS}jar ${TESTTOOLVMOPTS}"
-JARSIGNER="$TESTJAVA${FS}bin${FS}jarsigner ${TESTTOOLVMOPTS}"
+JARSIGNER="$TESTJAVA${FS}bin${FS}jarsigner ${TESTTOOLVMOPTS} -debug"
 JAVAC="$TESTJAVA${FS}bin${FS}javac ${TESTTOOLVMOPTS} ${TESTJAVACOPTS}"
 
 rm $KS
@@ -138,7 +138,7 @@
 [ $LINES = 4 ] || exit $LINENO
 
 # ==========================================================
-# Second part: exit code 2, 4, 8
+# Second part: exit code 2, 4, 8.
 # 16 and 32 already covered in the first part
 # ==========================================================
 
@@ -174,11 +174,14 @@
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar goodeku
 [ $? = 0 ] || exit $LINENO
 
-# badchain signed by ca, but ca is removed later
+# badchain signed by ca1, but ca1 is removed later
 $KT -genkeypair -alias badchain -dname CN=badchain -validity 365
-$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
+$KT -genkeypair -alias ca1 -dname CN=ca1 -ext bc -validity 365
+$KT -certreq -alias badchain | $KT -gencert -alias ca1 -validity 365 | \
         $KT -importcert -alias badchain
-$KT -delete -alias ca
+# save ca1.cert for easy replay
+$KT -exportcert -file ca1.cert -alias ca1
+$KT -delete -alias ca1
 
 $JARSIGNER -strict -keystore $KS -storepass changeit a.jar badchain
 [ $? = 4 ] || exit $LINENO
@@ -204,13 +207,41 @@
 $JARSIGNER -strict -keystore $KS -storepass changeit -certchain certchain a.jar altchain
 [ $? = 0 ] || exit $LINENO
 
-# but if ca2 is removed, -certchain does not work
+# if ca2 is removed, -certchain still work because altchain is a self-signed entry and
+# it is trusted by jarsigner
+# save ca2.cert for easy replay
+$KT -exportcert -file ca2.cert -alias ca2
 $KT -delete -alias ca2
 $JARSIGNER -strict -keystore $KS -storepass changeit -certchain certchain a.jar altchain
+[ $? = 0 ] || exit $LINENO
+
+# if cert is imported, -certchain won't work because this certificate entry is not trusted
+$KT -importcert -file certchain -alias altchain -noprompt
+$JARSIGNER -strict -keystore $KS -storepass changeit -certchain certchain a.jar altchain
 [ $? = 4 ] || exit $LINENO
 
 $JARSIGNER -verify a.jar
 [ $? = 0 ] || exit $LINENO
 
+# ==========================================================
+# 8172529
+# ==========================================================
+
+$KT -genkeypair -alias ee -dname CN=ee
+$KT -genkeypair -alias caone -dname CN=caone
+$KT -genkeypair -alias catwo -dname CN=catwo
+
+$KT -certreq -alias ee | $KT -gencert -alias catwo -rfc > ee.cert
+$KT -certreq -alias catwo | $KT -gencert -alias caone -sigalg MD5withRSA -rfc > catwo.cert
+
+# This certchain contains a cross-signed weak catwo.cert
+cat ee.cert catwo.cert | $KT -importcert -alias ee
+
+$JAR cvf a.jar A1.class
+$JARSIGNER -strict -keystore $KS -storepass changeit a.jar ee
+[ $? = 0 ] || exit $LINENO
+$JARSIGNER -strict -keystore $KS -storepass changeit -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
 echo OK
 exit 0