8140422: Add mechanism to allow non default root CAs to be not subject to algorithm restrictions
authorascarpino
Mon, 02 May 2016 16:45:38 -0700
changeset 37726 bbecfff95ec3
parent 37725 04ee31f1c2a9
child 37727 f33cc7ee4a9c
8140422: Add mechanism to allow non default root CAs to be not subject to algorithm restrictions Reviewed-by: mullan, xuelei
jdk/make/gendata/Gendata-java.base.gmk
jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java
jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java
jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java
jdk/src/java.base/share/classes/sun/security/util/AnchorCertificates.java
jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java
jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java
jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java
jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java
jdk/src/java.base/share/conf/security/java.security
jdk/test/sun/security/tools/jarsigner/Warning.java
--- a/jdk/make/gendata/Gendata-java.base.gmk	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/make/gendata/Gendata-java.base.gmk	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2016, 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
@@ -41,7 +41,7 @@
 GENDATA_UNINAME := $(JDK_OUTPUTDIR)/modules/java.base/java/lang/uniName.dat
 
 $(GENDATA_UNINAME): $(JDK_TOPDIR)/make/data/unicodedata/UnicodeData.txt $(BUILD_TOOLS_JDK)
-	$(MKDIR) -p $(@D)
+	$(call MakeDir, $(@D))
 	$(TOOL_CHARACTERNAME) $< $@
 
 TARGETS += $(GENDATA_UNINAME)
@@ -51,7 +51,7 @@
 GENDATA_CURDATA := $(JDK_OUTPUTDIR)/modules/java.base/java/util/currency.data
 
 $(GENDATA_CURDATA): $(JDK_TOPDIR)/make/data/currency/CurrencyData.properties $(BUILD_TOOLS_JDK)
-	$(MKDIR) -p $(@D)
+	$(call MakeDir, $(@D))
 	$(RM) $@
 	$(TOOL_GENERATECURRENCYDATA) -o $@.tmp < $<
 	$(MV) $@.tmp $@
@@ -67,10 +67,10 @@
 # RESTRICTED_PKGS_SRC is optionally set in custom extension for this makefile
 
 $(GENDATA_JAVA_SECURITY): $(BUILD_TOOLS) $(GENDATA_JAVA_SECURITY_SRC) $(RESTRICTED_PKGS_SRC)
-	$(ECHO) "Generating java.security"
-	$(MKDIR) -p $(@D)
+	$(call LogInfo, Generating java.security)
+	$(call MakeDir, $(@D))
 	$(TOOL_MAKEJAVASECURITY) $(GENDATA_JAVA_SECURITY_SRC) $@ $(OPENJDK_TARGET_OS) \
-		$(OPENJDK_TARGET_CPU_ARCH) $(RESTRICTED_PKGS_SRC) || exit 1
+	    $(OPENJDK_TARGET_CPU_ARCH) $(RESTRICTED_PKGS_SRC)
 
 TARGETS += $(GENDATA_JAVA_SECURITY)
 
@@ -78,7 +78,7 @@
 
 $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/classlist: \
     $(JDK_TOPDIR)/make/data/classlist/classlist.$(OPENJDK_TARGET_OS)
-	$(MKDIR) -p $(@D)
+	$(call MakeDir, $(@D))
 	$(RM) $@ $@.tmp
 	$(TOOL_ADDJSUM) $< $@.tmp
 	$(MV) $@.tmp $@
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, 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
@@ -31,12 +31,10 @@
 import java.util.Collections;
 import java.util.Set;
 import java.util.EnumSet;
-import java.util.HashSet;
 import java.math.BigInteger;
 import java.security.PublicKey;
 import java.security.KeyFactory;
 import java.security.AlgorithmParameters;
-import java.security.NoSuchAlgorithmException;
 import java.security.GeneralSecurityException;
 import java.security.cert.Certificate;
 import java.security.cert.X509CRL;
@@ -48,10 +46,13 @@
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertPathValidatorException.BasicReason;
 import java.security.cert.PKIXReason;
-import java.io.IOException;
-import java.security.interfaces.*;
-import java.security.spec.*;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
 
+import sun.security.util.AnchorCertificates;
+import sun.security.util.CertConstraintParameters;
+import sun.security.util.Debug;
 import sun.security.util.DisabledAlgorithmConstraints;
 import sun.security.x509.X509CertImpl;
 import sun.security.x509.X509CRLImpl;
@@ -69,6 +70,7 @@
  * @see PKIXParameters
  */
 public final class AlgorithmChecker extends PKIXCertPathChecker {
+    private static final Debug debug = Debug.getInstance("certpath");
 
     private final AlgorithmConstraints constraints;
     private final PublicKey trustedPubKey;
@@ -88,6 +90,14 @@
         certPathDefaultConstraints = new DisabledAlgorithmConstraints(
             DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
 
+    // If there is no "cacerts" keyword, then disable anchor checking
+    private static final boolean publicCALimits =
+            certPathDefaultConstraints.checkProperty("jdkCA");
+
+    // If anchor checking enabled, this will be true if the trust anchor
+    // has a match in the cacerts file
+    private boolean trustedMatch = false;
+
     /**
      * Create a new <code>AlgorithmChecker</code> with the algorithm
      * constraints specified in security property
@@ -136,6 +146,11 @@
 
         if (anchor.getTrustedCert() != null) {
             this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
+            // Check for anchor certificate restrictions
+            trustedMatch = checkFingerprint(anchor.getTrustedCert());
+            if (trustedMatch && debug != null) {
+                debug.println("trustedMatch = true");
+            }
         } else {
             this.trustedPubKey = anchor.getCAPublicKey();
         }
@@ -144,6 +159,19 @@
         this.constraints = constraints;
     }
 
+    // Check this 'cert' for restrictions in the AnchorCertificates
+    // trusted certificates list
+    private static boolean checkFingerprint(X509Certificate cert) {
+        if (!publicCALimits) {
+            return false;
+        }
+
+        if (debug != null) {
+            debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName());
+        }
+        return AnchorCertificates.contains(cert);
+    }
+
     @Override
     public void init(boolean forward) throws CertPathValidatorException {
         //  Note that this class does not support forward mode.
@@ -181,36 +209,8 @@
             return;
         }
 
-        X509CertImpl x509Cert = null;
-        try {
-            x509Cert = X509CertImpl.toImpl((X509Certificate)cert);
-        } catch (CertificateException ce) {
-            throw new CertPathValidatorException(ce);
-        }
-
-        PublicKey currPubKey = x509Cert.getPublicKey();
-        String currSigAlg = x509Cert.getSigAlgName();
-
-        AlgorithmId algorithmId = null;
-        try {
-            algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG);
-        } catch (CertificateException ce) {
-            throw new CertPathValidatorException(ce);
-        }
-
-        AlgorithmParameters currSigAlgParams = algorithmId.getParameters();
-
-        // Check the current signature algorithm
-        if (!constraints.permits(
-                SIGNATURE_PRIMITIVE_SET,
-                currSigAlg, currSigAlgParams)) {
-            throw new CertPathValidatorException(
-                "Algorithm constraints check failed: " + currSigAlg,
-                null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
-        }
-
         // check the key usage and key size
-        boolean[] keyUsage = x509Cert.getKeyUsage();
+        boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage();
         if (keyUsage != null && keyUsage.length < 9) {
             throw new CertPathValidatorException(
                 "incorrect KeyUsage extension",
@@ -248,27 +248,67 @@
 
             if (primitives.isEmpty()) {
                 throw new CertPathValidatorException(
-                    "incorrect KeyUsage extension",
+                    "incorrect KeyUsage extension bits",
                     null, null, -1, PKIXReason.INVALID_KEY_USAGE);
             }
         }
 
-        if (!constraints.permits(primitives, currPubKey)) {
-            throw new CertPathValidatorException(
-                "algorithm constraints check failed",
-                null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+        PublicKey currPubKey = cert.getPublicKey();
+
+        // Check against DisabledAlgorithmConstraints certpath constraints.
+        // permits() will throw exception on failure.
+        certPathDefaultConstraints.permits(primitives,
+                new CertConstraintParameters((X509Certificate)cert,
+                        trustedMatch));
+                // new CertConstraintParameters(x509Cert, trustedMatch));
+        // If there is no previous key, set one and exit
+        if (prevPubKey == null) {
+            prevPubKey = currPubKey;
+            return;
+        }
+
+        X509CertImpl x509Cert;
+        AlgorithmId algorithmId;
+        try {
+            x509Cert = X509CertImpl.toImpl((X509Certificate)cert);
+            algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG);
+        } catch (CertificateException ce) {
+            throw new CertPathValidatorException(ce);
+        }
+
+        AlgorithmParameters currSigAlgParams = algorithmId.getParameters();
+        String currSigAlg = x509Cert.getSigAlgName();
+
+        // If 'constraints' is not of DisabledAlgorithmConstraints, check all
+        // everything individually
+        if (!(constraints instanceof DisabledAlgorithmConstraints)) {
+            // Check the current signature algorithm
+            if (!constraints.permits(
+                    SIGNATURE_PRIMITIVE_SET,
+                    currSigAlg, currSigAlgParams)) {
+                throw new CertPathValidatorException(
+                        "Algorithm constraints check failed on signature " +
+                                "algorithm: " + currSigAlg, null, null, -1,
+                        BasicReason.ALGORITHM_CONSTRAINED);
+            }
+
+            if (!constraints.permits(primitives, currPubKey)) {
+                throw new CertPathValidatorException(
+                        "Algorithm constraints check failed on keysize: " +
+                                sun.security.util.KeyUtil.getKeySize(currPubKey),
+                        null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+            }
         }
 
         // Check with previous cert for signature algorithm and public key
         if (prevPubKey != null) {
-            if (currSigAlg != null) {
-                if (!constraints.permits(
-                        SIGNATURE_PRIMITIVE_SET,
-                        currSigAlg, prevPubKey, currSigAlgParams)) {
-                    throw new CertPathValidatorException(
-                        "Algorithm constraints check failed: " + currSigAlg,
-                        null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
-                }
+            if (!constraints.permits(
+                    SIGNATURE_PRIMITIVE_SET,
+                    currSigAlg, prevPubKey, currSigAlgParams)) {
+                throw new CertPathValidatorException(
+                    "Algorithm constraints check failed on " +
+                            "signature algorithm: " + currSigAlg,
+                    null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
             }
 
             // Inherit key parameters from previous key
@@ -282,7 +322,7 @@
                 DSAParams params = ((DSAPublicKey)prevPubKey).getParams();
                 if (params == null) {
                     throw new CertPathValidatorException(
-                                    "Key parameters missing");
+                        "Key parameters missing from public key.");
                 }
 
                 try {
@@ -330,6 +370,11 @@
             // Don't bother to change the trustedPubKey.
             if (anchor.getTrustedCert() != null) {
                 prevPubKey = anchor.getTrustedCert().getPublicKey();
+                // Check for anchor certificate restrictions
+                trustedMatch = checkFingerprint(anchor.getTrustedCert());
+                if (trustedMatch && debug != null) {
+                    debug.println("trustedMatch = true");
+                }
             } else {
                 prevPubKey = anchor.getCAPublicKey();
             }
@@ -370,7 +415,8 @@
         if (!certPathDefaultConstraints.permits(
                 SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) {
             throw new CertPathValidatorException(
-                "algorithm check failed: " + sigAlgName + " is disabled",
+                "Algorithm constraints check failed on signature algorithm: " +
+                sigAlgName + " is disabled",
                 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
         }
     }
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java	Mon May 02 16:45:38 2016 -0700
@@ -131,8 +131,8 @@
 
                 } catch (CertPathValidatorException cpve) {
                     throw new CertPathValidatorException(cpve.getMessage(),
-                        cpve.getCause(), cpOriginal, cpSize - (i + 1),
-                        cpve.getReason());
+                        (cpve.getCause() != null) ? cpve.getCause() : cpve,
+                            cpOriginal, cpSize - (i + 1), cpve.getReason());
                 }
             }
 
--- a/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016 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
@@ -29,7 +29,6 @@
 import java.security.AlgorithmConstraints;
 import java.security.PrivilegedAction;
 import java.security.Security;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -45,8 +44,7 @@
     }
 
     // Get algorithm constraints from the specified security property.
-    private static void loadAlgorithmsMap(Map<String, String[]> algorithmsMap,
-            String propertyName) {
+    static String[] getAlgorithms(String propertyName) {
         String property = AccessController.doPrivileged(
                 (PrivilegedAction<String>) () -> Security.getProperty(
                         propertyName));
@@ -68,18 +66,7 @@
         if (algorithmsInProperty == null) {
             algorithmsInProperty = new String[0];
         }
-        algorithmsMap.put(propertyName, algorithmsInProperty);
-    }
-
-    static String[] getAlgorithms(Map<String, String[]> algorithmsMap,
-            String propertyName) {
-        synchronized (algorithmsMap) {
-            if (!algorithmsMap.containsKey(propertyName)) {
-                loadAlgorithmsMap(algorithmsMap, propertyName);
-            }
-
-            return algorithmsMap.get(propertyName);
-        }
+        return algorithmsInProperty;
     }
 
     static boolean checkAlgorithm(String[] algorithms, String algorithm,
--- a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -40,19 +40,7 @@
     private static final Pattern pattern =
             Pattern.compile("with|and|(?<!padd)in", Pattern.CASE_INSENSITIVE);
 
-    /**
-     * Decompose the standard algorithm name into sub-elements.
-     * <p>
-     * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA"
-     * so that we can check the "SHA1" and "RSA" algorithm constraints
-     * separately.
-     * <p>
-     * Please override the method if need to support more name pattern.
-     */
-    public Set<String> decompose(String algorithm) {
-        if (algorithm == null || algorithm.length() == 0) {
-            return new HashSet<>();
-        }
+    private static Set<String> decomposeImpl(String algorithm) {
 
         // algorithm/mode/padding
         String[] transTockens = transPattern.split(algorithm);
@@ -79,6 +67,24 @@
                 elements.add(token);
             }
         }
+        return elements;
+    }
+
+    /**
+     * Decompose the standard algorithm name into sub-elements.
+     * <p>
+     * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA"
+     * so that we can check the "SHA1" and "RSA" algorithm constraints
+     * separately.
+     * <p>
+     * Please override the method if need to support more name pattern.
+     */
+    public Set<String> decompose(String algorithm) {
+        if (algorithm == null || algorithm.length() == 0) {
+            return new HashSet<>();
+        }
+
+        Set<String> elements = decomposeImpl(algorithm);
 
         // In Java standard algorithm name specification, for different
         // purpose, the SHA-1 and SHA-2 algorithm names are different. For
@@ -130,4 +136,40 @@
         return elements;
     }
 
+    private static void hasLoop(Set<String> elements, String find, String replace) {
+        if (elements.contains(find)) {
+            if (!elements.contains(replace)) {
+                elements.add(replace);
+            }
+            elements.remove(find);
+        }
+    }
+
+    /*
+     * This decomposes a standard name into sub-elements with a consistent
+     * message digest algorithm name to avoid overly complicated checking.
+     */
+    public static Set<String> decomposeOneHash(String algorithm) {
+        if (algorithm == null || algorithm.length() == 0) {
+            return new HashSet<>();
+        }
+
+        Set<String> elements = decomposeImpl(algorithm);
+
+        hasLoop(elements, "SHA-1", "SHA1");
+        hasLoop(elements, "SHA-224", "SHA224");
+        hasLoop(elements, "SHA-256", "SHA256");
+        hasLoop(elements, "SHA-384", "SHA384");
+        hasLoop(elements, "SHA-512", "SHA512");
+
+        return elements;
+    }
+
+    /*
+     * The provided message digest algorithm name will return a consistent
+     * naming scheme.
+     */
+    public static String hashName(String algorithm) {
+        return algorithm.replace("-", "");
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/AnchorCertificates.java	Mon May 02 16:45:38 2016 -0700
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.AccessController;
+import java.security.KeyStore;
+import java.security.PrivilegedAction;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.HashSet;
+
+import sun.security.x509.X509CertImpl;
+
+/**
+ * The purpose of this class is to determine the trust anchor certificates is in
+ * the cacerts file.  This is used for PKIX CertPath checking.
+ */
+public class AnchorCertificates {
+
+    private static final Debug debug = Debug.getInstance("certpath");
+    private static final String HASH = "SHA-256";
+    private static HashSet<String> certs;
+
+    static  {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                File f = new File(System.getProperty("java.home"),
+                        "lib/security/cacerts");
+                KeyStore cacerts;
+                try {
+                    cacerts = KeyStore.getInstance("JKS");
+                    try (FileInputStream fis = new FileInputStream(f)) {
+                        cacerts.load(fis, "changeit".toCharArray());
+                        certs = new HashSet<>();
+                        Enumeration<String> list = cacerts.aliases();
+                        String alias;
+                        while (list.hasMoreElements()) {
+                            alias = list.nextElement();
+                            // Check if this cert is labeled a trust anchor.
+                            if (alias.contains(" [jdk")) {
+                                X509Certificate cert = (X509Certificate) cacerts
+                                        .getCertificate(alias);
+                                certs.add(X509CertImpl.getFingerprint(HASH, cert));
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    if (debug != null) {
+                        debug.println("Error parsing cacerts");
+                    }
+                    e.printStackTrace();
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Checks if a certificate is a trust anchor.
+     *
+     * @param cert the certificate to check
+     * @return true if the certificate is trusted.
+     */
+    public static boolean contains(X509Certificate cert) {
+        String key = X509CertImpl.getFingerprint(HASH, cert);
+        boolean result = certs.contains(key);
+        if (result && debug != null) {
+            debug.println("AnchorCertificate.contains: matched " +
+                    cert.getSubjectDN());
+        }
+        return result;
+    }
+
+    private AnchorCertificates() {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java	Mon May 02 16:45:38 2016 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.util;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * This class is a wrapper for keeping state and passing objects between PKIX,
+ * AlgorithmChecker, and DisabledAlgorithmConstraints.
+ */
+public class CertConstraintParameters {
+    // A certificate being passed to check against constraints.
+    private final X509Certificate cert;
+
+    // This is true if the trust anchor in the certificate chain matches a cert
+    // in AnchorCertificates
+    private final boolean trustedMatch;
+
+    public CertConstraintParameters(X509Certificate c, boolean match) {
+        cert = c;
+        trustedMatch = match;
+    }
+
+    public CertConstraintParameters(X509Certificate c) {
+        this(c, false);
+    }
+
+    // Returns if the trust anchor has a match if anchor checking is enabled.
+    public boolean isTrustedMatch() {
+        return trustedMatch;
+    }
+
+    public X509Certificate getCertificate() {
+        return cert;
+    }
+}
--- a/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -28,12 +28,14 @@
 import java.security.CryptoPrimitive;
 import java.security.AlgorithmParameters;
 import java.security.Key;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.HashMap;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
@@ -44,6 +46,7 @@
  * for the syntax of the disabled algorithm string.
  */
 public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
+    private static final Debug debug = Debug.getInstance("certpath");
 
     // the known security property, jdk.certpath.disabledAlgorithms
     public static final String PROPERTY_CERTPATH_DISABLED_ALGS =
@@ -53,13 +56,8 @@
     public static final String PROPERTY_TLS_DISABLED_ALGS =
             "jdk.tls.disabledAlgorithms";
 
-    private static final Map<String, String[]> disabledAlgorithmsMap =
-                                                            new HashMap<>();
-    private static final Map<String, KeySizeConstraints> keySizeConstraintsMap =
-                                                            new HashMap<>();
-
     private final String[] disabledAlgorithms;
-    private final KeySizeConstraints keySizeConstraints;
+    private final Constraints algorithmConstraints;
 
     /**
      * Initialize algorithm constraints with the specified security property.
@@ -74,11 +72,14 @@
     public DisabledAlgorithmConstraints(String propertyName,
             AlgorithmDecomposer decomposer) {
         super(decomposer);
-        disabledAlgorithms = getAlgorithms(disabledAlgorithmsMap, propertyName);
-        keySizeConstraints = getKeySizeConstraints(disabledAlgorithms,
-                propertyName);
+        disabledAlgorithms = getAlgorithms(propertyName);
+        algorithmConstraints = new Constraints(disabledAlgorithms);
     }
 
+    /*
+     * This only checks if the algorithm has been completely disabled.  If
+     * there are keysize or other limit, this method allow the algorithm.
+     */
     @Override
     public final boolean permits(Set<CryptoPrimitive> primitives,
             String algorithm, AlgorithmParameters parameters) {
@@ -91,11 +92,19 @@
         return checkAlgorithm(disabledAlgorithms, algorithm, decomposer);
     }
 
+    /*
+     * Checks if the key algorithm has been disabled or constraints have been
+     * placed on the key.
+     */
     @Override
     public final boolean permits(Set<CryptoPrimitive> primitives, Key key) {
         return checkConstraints(primitives, "", key, null);
     }
 
+    /*
+     * Checks if the key algorithm has been disabled or if constraints have
+     * been placed on the key.
+     */
     @Override
     public final boolean permits(Set<CryptoPrimitive> primitives,
             String algorithm, Key key, AlgorithmParameters parameters) {
@@ -107,7 +116,39 @@
         return checkConstraints(primitives, algorithm, key, parameters);
     }
 
-    // Check algorithm constraints
+    /*
+     * Check if a x509Certificate object is permitted.  Check if all
+     * algorithms are allowed, certificate constraints, and the
+     * public key against key constraints.
+     *
+     * Uses new style permit() which throws exceptions.
+     */
+    public final void permits(Set<CryptoPrimitive> primitives,
+            CertConstraintParameters cp) throws CertPathValidatorException {
+        checkConstraints(primitives, cp);
+    }
+
+    /*
+     * Check if Certificate object is within the constraints.
+     * Uses new style permit() which throws exceptions.
+     */
+    public final void permits(Set<CryptoPrimitive> primitives,
+            X509Certificate cert) throws CertPathValidatorException {
+        checkConstraints(primitives, new CertConstraintParameters(cert));
+    }
+
+    // Check if a string is contained inside the property
+    public boolean checkProperty(String param) {
+        param = param.toLowerCase(Locale.ENGLISH);
+        for (String block : disabledAlgorithms) {
+            if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Check algorithm constraints with key and algorithm
     private boolean checkConstraints(Set<CryptoPrimitive> primitives,
             String algorithm, Key key, AlgorithmParameters parameters) {
 
@@ -116,7 +157,7 @@
             throw new IllegalArgumentException("The key cannot be null");
         }
 
-        // check the target algorithm
+        // check the signature algorithm
         if (algorithm != null && algorithm.length() != 0) {
             if (!permits(primitives, algorithm, parameters)) {
                 return false;
@@ -129,97 +170,203 @@
         }
 
         // check the key constraints
-        if (keySizeConstraints.disables(key)) {
-            return false;
-        }
-
-        return true;
+        return algorithmConstraints.permits(key);
     }
 
-    private static KeySizeConstraints getKeySizeConstraints(
-            String[] disabledAlgorithms, String propertyName) {
-        synchronized (keySizeConstraintsMap) {
-            if(!keySizeConstraintsMap.containsKey(propertyName)) {
-                // map the key constraints
-                KeySizeConstraints keySizeConstraints =
-                        new KeySizeConstraints(disabledAlgorithms);
-                keySizeConstraintsMap.put(propertyName, keySizeConstraints);
-            }
+    /*
+     * Check algorithm constraints with Certificate
+     * Uses new style permit() which throws exceptions.
+     */
+    private void checkConstraints(Set<CryptoPrimitive> primitives,
+            CertConstraintParameters cp) throws CertPathValidatorException {
+
+        X509Certificate cert = cp.getCertificate();
+        String algorithm = cert.getSigAlgName();
 
-            return keySizeConstraintsMap.get(propertyName);
+        // Check signature algorithm is not disabled
+        if (!permits(primitives, algorithm, null)) {
+            throw new CertPathValidatorException(
+                    "Algorithm constraints check failed on disabled "+
+                            "signature algorithm: " + algorithm,
+                    null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
         }
+
+        // Check key algorithm is not disabled
+        if (!permits(primitives, cert.getPublicKey().getAlgorithm(), null)) {
+            throw new CertPathValidatorException(
+                    "Algorithm constraints check failed on disabled "+
+                            "public key algorithm: " + algorithm,
+                    null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+        }
+
+        // Check the certificate and key constraints
+        algorithmConstraints.permits(cp);
+
     }
 
     /**
-     * key constraints
+     * Key and Certificate Constraints
+     *
+     * The complete disabling of an algorithm is not handled by Constraints or
+     * Constraint classes.  That is addressed with
+     *   permit(Set<CryptoPrimitive>, String, AlgorithmParameters)
+     *
+     * When passing a Key to permit(), the boolean return values follow the
+     * same as the interface class AlgorithmConstraints.permit().  This is to
+     * maintain compatibility:
+     * 'true' means the operation is allowed.
+     * 'false' means it failed the constraints and is disallowed.
+     *
+     * When passing CertConstraintParameters through permit(), an exception
+     * will be thrown on a failure to better identify why the operation was
+     * disallowed.
      */
-    private static class KeySizeConstraints {
-        private static final Pattern pattern = Pattern.compile(
-                "(\\S+)\\s+keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)");
 
-        private Map<String, Set<KeySizeConstraint>> constraintsMap =
-            Collections.synchronizedMap(
-                        new HashMap<String, Set<KeySizeConstraint>>());
+    private static class Constraints {
+        private Map<String, Set<Constraint>> constraintsMap = new HashMap<>();
+        private static final Pattern keySizePattern = Pattern.compile(
+                "keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)");
 
-        public KeySizeConstraints(String[] restrictions) {
-            for (String restriction : restrictions) {
-                if (restriction == null || restriction.isEmpty()) {
+        public Constraints(String[] constraintArray) {
+            for (String constraintEntry : constraintArray) {
+                if (constraintEntry == null || constraintEntry.isEmpty()) {
                     continue;
                 }
 
-                Matcher matcher = pattern.matcher(restriction);
-                if (matcher.matches()) {
-                    String algorithm = matcher.group(1);
+                constraintEntry = constraintEntry.trim();
+                if (debug != null) {
+                    debug.println("Constraints: " + constraintEntry);
+                }
 
-                    KeySizeConstraint.Operator operator =
-                             KeySizeConstraint.Operator.of(matcher.group(2));
-                    int length = Integer.parseInt(matcher.group(3));
+                // Check if constraint is a complete disabling of an
+                // algorithm or has conditions.
+                String algorithm;
+                String policy;
+                int space = constraintEntry.indexOf(' ');
+                if (space > 0) {
+                    algorithm = AlgorithmDecomposer.hashName(
+                            constraintEntry.substring(0, space).
+                                    toUpperCase(Locale.ENGLISH));
+                    policy = constraintEntry.substring(space + 1);
+                } else {
+                    constraintsMap.computeIfAbsent(
+                            constraintEntry.toUpperCase(Locale.ENGLISH),
+                            k -> new HashSet<>());
+                    continue;
+                }
 
-                    algorithm = algorithm.toLowerCase(Locale.ENGLISH);
+                // Convert constraint conditions into Constraint classes
+                Constraint c, lastConstraint = null;
+                // Allow only one jdkCA entry per constraint entry
+                boolean jdkCALimit = false;
+
+                for (String entry : policy.split("&")) {
+                    entry = entry.trim();
 
-                    synchronized (constraintsMap) {
-                        if (!constraintsMap.containsKey(algorithm)) {
-                            constraintsMap.put(algorithm,
-                                new HashSet<KeySizeConstraint>());
+                    Matcher matcher = keySizePattern.matcher(entry);
+                    if (matcher.matches()) {
+                        if (debug != null) {
+                            debug.println("Constraints set to keySize: " +
+                                    entry);
                         }
+                        c = new KeySizeConstraint(algorithm,
+                                KeySizeConstraint.Operator.of(matcher.group(1)),
+                                Integer.parseInt(matcher.group(2)));
 
-                        Set<KeySizeConstraint> constraintSet =
-                            constraintsMap.get(algorithm);
-                        KeySizeConstraint constraint =
-                            new KeySizeConstraint(operator, length);
-                        constraintSet.add(constraint);
+                    } else if (entry.equalsIgnoreCase("jdkCA")) {
+                        if (debug != null) {
+                            debug.println("Constraints set to jdkCA.");
+                        }
+                        if (jdkCALimit) {
+                            throw new IllegalArgumentException("Only one " +
+                                    "jdkCA entry allowed in property. " +
+                                    "Constraint: " + constraintEntry);
+                        }
+                        c = new jdkCAConstraint(algorithm);
+                        jdkCALimit = true;
+                    } else {
+                        throw new IllegalArgumentException("Error in security" +
+                                " property. Constraint unknown: " + entry);
                     }
+
+                    // Link multiple conditions for a single constraint
+                    // into a linked list.
+                    if (lastConstraint == null) {
+                        if (!constraintsMap.containsKey(algorithm)) {
+                            constraintsMap.putIfAbsent(algorithm,
+                                    new HashSet<>());
+                        }
+                        constraintsMap.get(algorithm).add(c);
+                    } else {
+                        lastConstraint.nextConstraint = c;
+                    }
+                    lastConstraint = c;
                 }
             }
         }
 
-        // Does this KeySizeConstraints disable the specified key?
-        public boolean disables(Key key) {
-            String algorithm = key.getAlgorithm().toLowerCase(Locale.ENGLISH);
-            synchronized (constraintsMap) {
-                if (constraintsMap.containsKey(algorithm)) {
-                    Set<KeySizeConstraint> constraintSet =
-                                        constraintsMap.get(algorithm);
-                    for (KeySizeConstraint constraint : constraintSet) {
-                        if (constraint.disables(key)) {
-                            return true;
-                        }
+        // Get applicable constraints based off the signature algorithm
+        private Set<Constraint> getConstraints(String algorithm) {
+            return constraintsMap.get(algorithm);
+        }
+
+        // Check if KeySizeConstraints permit the specified key
+        public boolean permits(Key key) {
+            Set<Constraint> set = getConstraints(key.getAlgorithm());
+            if (set == null) {
+                return true;
+            }
+            for (Constraint constraint : set) {
+                if (!constraint.permits(key)) {
+                    if (debug != null) {
+                        debug.println("keySizeConstraint: failed key " +
+                                "constraint check " + KeyUtil.getKeySize(key));
                     }
+                    return false;
                 }
             }
+            return true;
+        }
 
-            return false;
+        // Check if constraints permit this cert.
+        public void permits(CertConstraintParameters cp)
+                throws CertPathValidatorException {
+            X509Certificate cert = cp.getCertificate();
+
+            if (debug != null) {
+                debug.println("Constraints.permits(): " + cert.getSigAlgName());
+            }
+
+            // Get all signature algorithms to check for constraints
+            Set<String> algorithms =
+                    AlgorithmDecomposer.decomposeOneHash(cert.getSigAlgName());
+            if (algorithms == null || algorithms.isEmpty()) {
+                return;
+            }
+
+            // Attempt to add the public key algorithm to the set
+            algorithms.add(cert.getPublicKey().getAlgorithm());
+
+            // Check all applicable constraints
+            for (String algorithm : algorithms) {
+                Set<Constraint> set = getConstraints(algorithm);
+                if (set == null) {
+                    continue;
+                }
+                for (Constraint constraint : set) {
+                    constraint.permits(cp);
+                }
+            }
         }
     }
 
-    /**
-     * Key size constraint.
-     *
-     * e.g.  "keysize <= 1024"
-     */
-    private static class KeySizeConstraint {
+    // Abstract class for algorithm constraint checking
+    private abstract static class Constraint {
+        String algorithm;
+        Constraint nextConstraint = null;
+
         // operator
-        static enum Operator {
+        enum Operator {
             EQ,         // "=="
             NE,         // "!="
             LT,         // "<"
@@ -243,16 +390,77 @@
                         return GE;
                 }
 
-                throw new IllegalArgumentException(
-                        s + " is not a legal Operator");
+                throw new IllegalArgumentException("Error in security " +
+                        "property. " + s + " is not a legal Operator");
             }
         }
 
+        /**
+         * Check if an algorithm constraint permit this key to be used.
+         * @param key Public key
+         * @return true if constraints do not match
+         */
+        public boolean permits(Key key) {
+            return true;
+        }
+
+        /**
+         * Check if an algorithm constraint is permit this certificate to
+         * be used.
+         * @param cp CertificateParameter containing certificate and state info
+         * @return true if constraints do not match
+         */
+        public abstract void permits(CertConstraintParameters cp)
+                throws CertPathValidatorException;
+    }
+
+    /*
+     * This class contains constraints dealing with the certificate chain
+     * of the certificate.
+     */
+    private static class jdkCAConstraint extends Constraint {
+        jdkCAConstraint(String algo) {
+            algorithm = algo;
+        }
+
+        /*
+         * Check if each constraint fails and check if there is a linked
+         * constraint  Any permitted constraint will exit the linked list
+         * to allow the operation.
+         */
+        public void permits(CertConstraintParameters cp)
+                throws CertPathValidatorException {
+            if (debug != null) {
+                debug.println("jdkCAConstraints.permits(): " + algorithm);
+            }
+
+            // Return false if the chain has a trust anchor in cacerts
+            if (cp.isTrustedMatch()) {
+                if (nextConstraint != null) {
+                    nextConstraint.permits(cp);
+                    return;
+                }
+                throw new CertPathValidatorException(
+                        "Algorithm constraints check failed on certificate " +
+                                "anchor limits",
+                        null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+            }
+        }
+    }
+
+
+    /*
+     * This class contains constraints dealing with the key size
+     * support limits per algorithm.   e.g.  "keySize <= 1024"
+     */
+    private static class KeySizeConstraint extends Constraint {
+
         private int minSize;            // the minimal available key size
         private int maxSize;            // the maximal available key size
         private int prohibitedSize = -1;    // unavailable key sizes
 
-        public KeySizeConstraint(Operator operator, int length) {
+        public KeySizeConstraint(String algo, Operator operator, int length) {
+            algorithm = algo;
             switch (operator) {
                 case EQ:      // an unavailable key size
                     this.minSize = 0;
@@ -286,21 +494,59 @@
             }
         }
 
-        // Does this key constraint disable the specified key?
-        public boolean disables(Key key) {
-            int size = KeyUtil.getKeySize(key);
+        /*
+         * If we are passed a certificate, extract the public key and use it.
+         *
+         * Check if each constraint fails and check if there is a linked
+         * constraint  Any permitted constraint will exit the linked list
+         * to allow the operation.
+         */
+        public void permits(CertConstraintParameters cp)
+                throws CertPathValidatorException {
+            if (!permitsImpl(cp.getCertificate().getPublicKey())) {
+                if (nextConstraint != null) {
+                    nextConstraint.permits(cp);
+                    return;
+                }
+                throw new CertPathValidatorException(
+                        "Algorithm constraints check failed on keysize limits",
+                        null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+            }
+        }
+
 
+        // Check if key constraint disable the specified key
+        // Uses old style permit()
+        public boolean permits(Key key) {
+            // If we recursively find a constraint that permits us to use
+            // this key, return true and skip any other constraint checks.
+            if (nextConstraint != null && nextConstraint.permits(key)) {
+                return true;
+            }
+            if (debug != null) {
+                debug.println("KeySizeConstraints.permits(): " + algorithm);
+            }
+
+            return permitsImpl(key);
+        }
+
+        private boolean permitsImpl(Key key) {
+            // Verify this constraint is for this public key algorithm
+            if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) {
+                return true;
+            }
+
+            int size = KeyUtil.getKeySize(key);
             if (size == 0) {
-                return true;    // we don't allow any key of size 0.
+                return false;    // we don't allow any key of size 0.
             } else if (size > 0) {
-                return ((size < minSize) || (size > maxSize) ||
+                return !((size < minSize) || (size > maxSize) ||
                     (prohibitedSize == size));
             }   // Otherwise, the key size is not accessible. Conservatively,
                 // please don't disable such keys.
 
-            return false;
+            return true;
         }
     }
-
 }
 
--- a/jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/LegacyAlgorithmConstraints.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -28,8 +28,6 @@
 import java.security.AlgorithmParameters;
 import java.security.CryptoPrimitive;
 import java.security.Key;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Set;
 import static sun.security.util.AbstractAlgorithmConstraints.getAlgorithms;
 
@@ -42,15 +40,12 @@
     public static final String PROPERTY_TLS_LEGACY_ALGS =
             "jdk.tls.legacyAlgorithms";
 
-    private static final Map<String, String[]> legacyAlgorithmsMap =
-                                                          new HashMap<>();
-
     private final String[] legacyAlgorithms;
 
     public LegacyAlgorithmConstraints(String propertyName,
             AlgorithmDecomposer decomposer) {
         super(decomposer);
-        legacyAlgorithms = getAlgorithms(legacyAlgorithmsMap, propertyName);
+        legacyAlgorithms = getAlgorithms(propertyName);
     }
 
     @Override
--- a/jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CertImpl.java	Mon May 02 16:45:38 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -1924,17 +1924,18 @@
 
     public String getFingerprint(String algorithm) {
         return fingerprints.computeIfAbsent(algorithm,
-                x -> getCertificateFingerPrint(x));
+            x -> getFingerprint(x, this));
     }
 
     /**
      * Gets the requested finger print of the certificate. The result
      * only contains 0-9 and A-F. No small case, no colon.
      */
-    private String getCertificateFingerPrint(String mdAlg) {
+    public static String getFingerprint(String algorithm,
+            X509Certificate cert) {
         try {
-            byte[] encCertInfo = getEncoded();
-            MessageDigest md = MessageDigest.getInstance(mdAlg);
+            byte[] encCertInfo = cert.getEncoded();
+            MessageDigest md = MessageDigest.getInstance(algorithm);
             byte[] digest = md.digest(encCertInfo);
             StringBuilder sb = new StringBuilder(digest.length * 2);
             for (int i = 0; i < digest.length; i++) {
--- a/jdk/src/java.base/share/conf/security/java.security	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/src/java.base/share/conf/security/java.security	Mon May 02 16:45:38 2016 -0700
@@ -497,13 +497,13 @@
 #       " DisabledAlgorithm { , DisabledAlgorithm } "
 #
 #   DisabledAlgorithm:
-#       AlgorithmName [Constraint]
+#       AlgorithmName [Constraint] { '&' Constraint }
 #
 #   AlgorithmName:
 #       (see below)
 #
 #   Constraint:
-#       KeySizeConstraint
+#       KeySizeConstraint, CertConstraint
 #
 #   KeySizeConstraint:
 #       keySize Operator DecimalInteger
@@ -520,6 +520,9 @@
 #   DecimalDigit: one of
 #       1 2 3 4 5 6 7 8 9 0
 #
+#   CertConstraint
+#       jdkCA
+#
 # The "AlgorithmName" is the standard algorithm name of the disabled
 # algorithm. See "Java Cryptography Architecture Standard Algorithm Name
 # Documentation" for information about Standard Algorithm Names.  Matching
@@ -542,6 +545,29 @@
 # be disabled. Note that the "KeySizeConstraint" only makes sense to key
 # algorithms.
 #
+# "CertConstraint" specifies additional constraints for
+# certificates that contain algorithms that are restricted:
+#
+#   "jdkCA" prohibits the specified algorithm only if the algorithm is used
+#     in a certificate chain that terminates at a marked trust anchor in the
+#     lib/security/cacerts keystore.  All other chains are not affected.
+#     If the jdkCA constraint is not set, then all chains using the
+#     specified algorithm are restricted.  jdkCA may only be used once in
+#     a DisabledAlgorithm expression.
+#     Example:  To apply this constraint to SHA-1 certificates, include
+#     the following:  "SHA1 jdkCA"
+#
+# When an algorithm must satisfy more than one constraint, it must be
+# delimited by an ampersand '&'.  For example, to restrict certificates in a
+# chain that terminate at a distribution provided trust anchor and contain
+# RSA keys that are less than or equal to 1024 bits, add the following
+# constraint:  "RSA keySize <= 1024 & jdkCA".
+#
+# All DisabledAlgorithms expressions are processed in the order defined in the
+# property.  This requires lower keysize constraints to be specified
+# before larger keysize constraints of the same algorithm.  For example:
+# "RSA keySize < 1024 & jdkCA, RSA keySize < 2048".
+#
 # Note: This property is currently used by Oracle's PKIX implementation. It
 # is not guaranteed to be examined and used by other implementations.
 #
--- a/jdk/test/sun/security/tools/jarsigner/Warning.java	Mon May 02 13:05:43 2016 -0700
+++ b/jdk/test/sun/security/tools/jarsigner/Warning.java	Mon May 02 16:45:38 2016 -0700
@@ -89,7 +89,7 @@
         issueCert("c");
         run("jarsigner", "a.jar c")
                 .shouldContain("chain is not validated. " +
-                        "Reason: algorithm constraints check failed");
+                        "Reason: Algorithm constraints check failed");
 
         recreateJar();