8154005: Add algorithm constraint that specifies the restriction date
authorascarpino
Thu, 26 May 2016 13:33:27 -0700
changeset 38576 ccaac80108c5
parent 38575 69a809ef2aa4
child 38577 003898efad7e
8154005: Add algorithm constraint that specifies the restriction date Reviewed-by: mullan, igerasim
jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java
jdk/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.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/conf/security/java.security
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java	Thu May 26 13:33:27 2016 -0700
@@ -29,6 +29,7 @@
 import java.security.CryptoPrimitive;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.Set;
 import java.util.EnumSet;
 import java.math.BigInteger;
@@ -59,7 +60,7 @@
 import sun.security.x509.AlgorithmId;
 
 /**
- * A <code>PKIXCertPathChecker</code> implementation to check whether a
+ * A {@code PKIXCertPathChecker} implementation to check whether a
  * specified certificate contains the required algorithm constraints.
  * <p>
  * Certificate fields such as the subject public key, the signature
@@ -74,6 +75,7 @@
 
     private final AlgorithmConstraints constraints;
     private final PublicKey trustedPubKey;
+    private final Date pkixdate;
     private PublicKey prevPubKey;
 
     private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
@@ -99,7 +101,7 @@
     private boolean trustedMatch = false;
 
     /**
-     * Create a new <code>AlgorithmChecker</code> with the algorithm
+     * Create a new {@code AlgorithmChecker} with the algorithm
      * constraints specified in security property
      * "jdk.certpath.disabledAlgorithms".
      *
@@ -107,11 +109,26 @@
      *     certificate
      */
     public AlgorithmChecker(TrustAnchor anchor) {
-        this(anchor, certPathDefaultConstraints);
+        this(anchor, certPathDefaultConstraints, null);
     }
 
     /**
-     * Create a new <code>AlgorithmChecker</code> with the
+     * Create a new {@code AlgorithmChecker} with the
+     * given {@code TrustAnchor} and {@code AlgorithmConstraints}.
+     *
+     * @param anchor the trust anchor selected to validate the target
+     *     certificate
+     * @param constraints the algorithm constraints (or null)
+     *
+     * @throws IllegalArgumentException if the {@code anchor} is null
+     */
+    public AlgorithmChecker(TrustAnchor anchor,
+            AlgorithmConstraints constraints) {
+        this(anchor, constraints, null);
+    }
+
+    /**
+     * Create a new {@code AlgorithmChecker} with the
      * given {@code AlgorithmConstraints}.
      * <p>
      * Note that this constructor will be used to check a certification
@@ -124,20 +141,24 @@
         this.prevPubKey = null;
         this.trustedPubKey = null;
         this.constraints = constraints;
+        this.pkixdate = null;
     }
 
     /**
-     * Create a new <code>AlgorithmChecker</code> with the
-     * given <code>TrustAnchor</code> and <code>AlgorithmConstraints</code>.
+     * Create a new {@code AlgorithmChecker} with the
+     * given {@code TrustAnchor} and {@code AlgorithmConstraints}.
      *
      * @param anchor the trust anchor selected to validate the target
      *     certificate
      * @param constraints the algorithm constraints (or null)
+     * @param pkixdate Date the constraints are checked against. The value is
+     *             either the PKIXParameter date or null for the current date.
      *
-     * @throws IllegalArgumentException if the <code>anchor</code> is null
+     * @throws IllegalArgumentException if the {@code anchor} is null
      */
     public AlgorithmChecker(TrustAnchor anchor,
-            AlgorithmConstraints constraints) {
+            AlgorithmConstraints constraints,
+            Date pkixdate) {
 
         if (anchor == null) {
             throw new IllegalArgumentException(
@@ -157,6 +178,22 @@
 
         this.prevPubKey = trustedPubKey;
         this.constraints = constraints;
+        this.pkixdate = pkixdate;
+    }
+
+    /**
+     * Create a new {@code AlgorithmChecker} with the
+     * given {@code TrustAnchor} and {@code PKIXParameter} date.
+     *
+     * @param anchor the trust anchor selected to validate the target
+     *     certificate
+     * @param pkixdate Date the constraints are checked against. The value is
+     *             either the PKIXParameter date or null for the current date.
+     *
+     * @throws IllegalArgumentException if the {@code anchor} is null
+     */
+    public AlgorithmChecker(TrustAnchor anchor, Date pkixdate) {
+        this(anchor, certPathDefaultConstraints, pkixdate);
     }
 
     // Check this 'cert' for restrictions in the AnchorCertificates
@@ -259,7 +296,7 @@
         // permits() will throw exception on failure.
         certPathDefaultConstraints.permits(primitives,
                 new CertConstraintParameters((X509Certificate)cert,
-                        trustedMatch));
+                        trustedMatch, pkixdate));
                 // new CertConstraintParameters(x509Cert, trustedMatch));
         // If there is no previous key, set one and exit
         if (prevPubKey == null) {
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java	Thu May 26 13:33:27 2016 -0700
@@ -172,7 +172,7 @@
         List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
         // add standard checkers that we will be using
         certPathCheckers.add(untrustedChecker);
-        certPathCheckers.add(new AlgorithmChecker(anchor));
+        certPathCheckers.add(new AlgorithmChecker(anchor, params.date()));
         certPathCheckers.add(new KeyChecker(certPathLen,
                                             params.targetCertConstraints()));
         certPathCheckers.add(new ConstraintsChecker(certPathLen));
--- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java	Thu May 26 13:33:27 2016 -0700
@@ -343,7 +343,8 @@
                 checkers.add(policyChecker);
 
                 // add the algorithm checker
-                checkers.add(new AlgorithmChecker(builder.trustAnchor));
+                checkers.add(new AlgorithmChecker(builder.trustAnchor,
+                        buildParams.date()));
 
                 BasicChecker basicChecker = null;
                 if (nextState.keyParamsNeeded()) {
--- a/jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/CertConstraintParameters.java	Thu May 26 13:33:27 2016 -0700
@@ -26,6 +26,7 @@
 package sun.security.util;
 
 import java.security.cert.X509Certificate;
+import java.util.Date;
 
 /**
  * This class is a wrapper for keeping state and passing objects between PKIX,
@@ -34,18 +35,21 @@
 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;
+    // PKIXParameter date
+    private final Date pkixDate;
 
-    public CertConstraintParameters(X509Certificate c, boolean match) {
+    public CertConstraintParameters(X509Certificate c, boolean match,
+            Date pkixdate) {
         cert = c;
         trustedMatch = match;
+        pkixDate = pkixdate;
     }
 
     public CertConstraintParameters(X509Certificate c) {
-        this(c, false);
+        this(c, false, null);
     }
 
     // Returns if the trust anchor has a match if anchor checking is enabled.
@@ -56,4 +60,9 @@
     public X509Certificate getCertificate() {
         return cert;
     }
+
+    public Date getPKIXParamDate() {
+        return pkixDate;
+    }
+
 }
--- a/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java	Thu May 26 13:33:27 2016 -0700
@@ -31,11 +31,15 @@
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertPathValidatorException.BasicReason;
 import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
@@ -226,6 +230,8 @@
         private Map<String, Set<Constraint>> constraintsMap = new HashMap<>();
         private static final Pattern keySizePattern = Pattern.compile(
                 "keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)");
+        private static final Pattern denyAfterPattern = Pattern.compile(
+                "denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})");
 
         public Constraints(String[] constraintArray) {
             for (String constraintEntry : constraintArray) {
@@ -259,6 +265,8 @@
                 Constraint c, lastConstraint = null;
                 // Allow only one jdkCA entry per constraint entry
                 boolean jdkCALimit = false;
+                // Allow only one denyAfter entry per constraint entry
+                boolean denyAfterLimit = false;
 
                 for (String entry : policy.split("&")) {
                     entry = entry.trim();
@@ -284,6 +292,22 @@
                         }
                         c = new jdkCAConstraint(algorithm);
                         jdkCALimit = true;
+
+                    } else if(matcher.usePattern(denyAfterPattern).matches()) {
+                        if (debug != null) {
+                            debug.println("Constraints set to denyAfter");
+                        }
+                        if (denyAfterLimit) {
+                            throw new IllegalArgumentException("Only one " +
+                                    "denyAfter entry allowed in property. " +
+                                    "Constraint: " + constraintEntry);
+                        }
+                        int year = Integer.parseInt(matcher.group(1));
+                        int month = Integer.parseInt(matcher.group(2));
+                        int day = Integer.parseInt(matcher.group(3));
+                        c = new DenyAfterConstraint(algorithm, year, month,
+                                day);
+                        denyAfterLimit = true;
                     } else {
                         throw new IllegalArgumentException("Error in security" +
                                 " property. Constraint unknown: " + entry);
@@ -360,7 +384,15 @@
         }
     }
 
-    // Abstract class for algorithm constraint checking
+    /**
+     * This abstract Constraint class for algorithm-based checking
+     * may contain one or more constraints.  If the '&' on the {@Security}
+     * property is used, multiple constraints have been grouped together
+     * requiring all the constraints to fail for the check to be disallowed.
+     *
+     * If the class contains multiple constraints, the next constraint
+     * is stored in {@code nextConstraint} in linked-list fashion.
+     */
     private abstract static class Constraint {
         String algorithm;
         Constraint nextConstraint = null;
@@ -396,22 +428,79 @@
         }
 
         /**
-         * Check if an algorithm constraint permit this key to be used.
+         * Check if an algorithm constraint is permitted with a given key.
+         *
+         * If the check inside of {@code permit()} fails, it must call
+         * {@code next()} with the same {@code Key} parameter passed if
+         * multiple constraints need to be checked.
+         *
          * @param key Public key
-         * @return true if constraints do not match
+         * @return 'true' if constraint is allowed, 'false' if disallowed.
          */
         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
+         * Check if an algorithm constraint is permitted with a given
+         * CertConstraintParameters.
+         *
+         * If the check inside of {@code permits()} fails, it must call
+         * {@code next()} with the same {@code CertConstraintParameters}
+         * parameter passed if multiple constraints need to be checked.
+         *
+         * @param cp CertConstraintParameter containing certificate info
+         * @throws CertPathValidatorException if constraint disallows.
+         *
          */
         public abstract void permits(CertConstraintParameters cp)
                 throws CertPathValidatorException;
+
+        /**
+         * Recursively check if the constraints are allowed.
+         *
+         * If {@code nextConstraint} is non-null, this method will
+         * call {@code nextConstraint}'s {@code permits()} to check if the
+         * constraint is allowed or denied.  If the constraint's
+         * {@code permits()} is allowed, this method will exit this and any
+         * recursive next() calls, returning 'true'.  If the constraints called
+         * were disallowed, the last constraint will throw
+         * {@code CertPathValidatorException}.
+         *
+         * @param cp CertConstraintParameters
+         * @return 'true' if constraint allows the operation, 'false' if
+         * we are at the end of the constraint list or,
+         * {@code nextConstraint} is null.
+         */
+        boolean next(CertConstraintParameters cp)
+                throws CertPathValidatorException {
+            if (nextConstraint != null) {
+                nextConstraint.permits(cp);
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Recursively check if this constraint is allowed,
+         *
+         * If {@code nextConstraint} is non-null, this method will
+         * call {@code nextConstraint}'s {@code permit()} to check if the
+         * constraint is allowed or denied.  If the constraint's
+         * {@code permit()} is allowed, this method will exit this and any
+         * recursive next() calls, returning 'true'.  If the constraints
+         * called were disallowed the check will exit with 'false'.
+         *
+         * @param key Public key
+         * @return 'true' if constraint allows the operation, 'false' if
+         * the constraint denies the operation.
+         */
+        boolean next(Key key) {
+            if (nextConstraint != null && nextConstraint.permits(key)) {
+                return true;
+            }
+            return false;
+        }
     }
 
     /*
@@ -424,9 +513,9 @@
         }
 
         /*
-         * 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.
+         * Check if CertConstraintParameters has a trusted match, if it does
+         * call next() for any following constraints. If it does not, exit
+         * as this constraint(s) does not restrict the operation.
          */
         public void permits(CertConstraintParameters cp)
                 throws CertPathValidatorException {
@@ -434,10 +523,9 @@
                 debug.println("jdkCAConstraints.permits(): " + algorithm);
             }
 
-            // Return false if the chain has a trust anchor in cacerts
+            // Check chain has a trust anchor in cacerts
             if (cp.isTrustedMatch()) {
-                if (nextConstraint != null) {
-                    nextConstraint.permits(cp);
+                if (next(cp)) {
                     return;
                 }
                 throw new CertPathValidatorException(
@@ -448,6 +536,99 @@
         }
     }
 
+    /*
+     * This class handles the denyAfter constraint.  The date is in the UTC/GMT
+     * timezone.
+     */
+     private static class DenyAfterConstraint extends Constraint {
+         private Date denyAfterDate;
+         private static final SimpleDateFormat dateFormat =
+                 new SimpleDateFormat("EEE, MMM d HH:mm:ss z YYYY");
+
+         DenyAfterConstraint(String algo, int year, int month, int day) {
+             Calendar c;
+
+             algorithm = algo;
+
+             if (debug != null) {
+                 debug.println("DenyAfterConstraint read in as:  year " +
+                         year + ", month = " + month + ", day = " + day);
+             }
+
+             c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
+                     .setDate(year, month - 1, day).build();
+
+             if (year > c.getActualMaximum(Calendar.YEAR) ||
+                     year < c.getActualMinimum(Calendar.YEAR)) {
+                 throw new IllegalArgumentException(
+                         "Invalid year given in constraint: " + year);
+             }
+             if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
+                     (month - 1) < c.getActualMinimum(Calendar.MONTH)) {
+                 throw new IllegalArgumentException(
+                         "Invalid month given in constraint: " + month);
+             }
+             if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
+                     day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
+                 throw new IllegalArgumentException(
+                         "Invalid Day of Month given in constraint: " + day);
+             }
+
+             denyAfterDate = c.getTime();
+             if (debug != null) {
+                 debug.println("DenyAfterConstraint date set to: " +
+                         dateFormat.format(denyAfterDate));
+             }
+         }
+
+         /*
+          * Checking that the provided date is not beyond the constraint date.
+          * The provided date can be the PKIXParameter date if given,
+          * otherwise it is the current date.
+          *
+          * If the constraint disallows, call next() for any following
+          * constraints. Throw an exception if this is the last constraint.
+          */
+         @Override
+         public void permits(CertConstraintParameters cp)
+                 throws CertPathValidatorException {
+             Date currentDate;
+
+             if (cp.getPKIXParamDate() != null) {
+                 currentDate = cp.getPKIXParamDate();
+             } else {
+                 currentDate = new Date();
+             }
+
+             if (!denyAfterDate.after(currentDate)) {
+                 if (next(cp)) {
+                     return;
+                 }
+                 throw new CertPathValidatorException(
+                         "denyAfter constraint check failed.  " +
+                                 "Constraint date: " +
+                                 dateFormat.format(denyAfterDate) +
+                                 "; Cert date: " +
+                                 dateFormat.format(currentDate),
+                          null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
+             }
+         }
+
+         /*
+          * Return result if the constraint's date is beyond the current date
+          * in UTC timezone.
+          */
+         public boolean permits(Key key) {
+             if (next(key)) {
+                 return true;
+             }
+             if (debug != null) {
+                 debug.println("DenyAfterConstraints.permits(): " + algorithm);
+             }
+
+             return denyAfterDate.after(new Date());
+         }
+     }
 
     /*
      * This class contains constraints dealing with the key size
--- a/jdk/src/java.base/share/conf/security/java.security	Thu May 26 13:18:32 2016 -0700
+++ b/jdk/src/java.base/share/conf/security/java.security	Thu May 26 13:33:27 2016 -0700
@@ -570,9 +570,7 @@
 # describes the mechanism for disabling algorithms based on algorithm name
 # and/or key length.  This includes algorithms used in certificates, as well
 # as revocation information such as CRLs and signed OCSP Responses.
-#
-# The syntax of the disabled algorithm string is described as this Java
-# BNF-style:
+# The syntax of the disabled algorithm string is described as follows:
 #   DisabledAlgorithms:
 #       " DisabledAlgorithm { , DisabledAlgorithm } "
 #
@@ -583,25 +581,22 @@
 #       (see below)
 #
 #   Constraint:
-#       KeySizeConstraint, CertConstraint
+#       KeySizeConstraint | CAConstraint | DenyAfterConstraint
 #
 #   KeySizeConstraint:
-#       keySize Operator DecimalInteger
+#       keySize Operator KeyLength
 #
 #   Operator:
 #       <= | < | == | != | >= | >
 #
-#   DecimalInteger:
-#       DecimalDigits
-#
-#   DecimalDigits:
-#       DecimalDigit {DecimalDigit}
+#   KeyLength:
+#       Integer value of the algorithm's key length in bits
 #
-#   DecimalDigit: one of
-#       1 2 3 4 5 6 7 8 9 0
+#   CAConstraint:
+#       jdkCA
 #
-#   CertConstraint
-#       jdkCA
+#   DenyAfterConstraint:
+#       denyAfter YYYY-MM-DD
 #
 # The "AlgorithmName" is the standard algorithm name of the disabled
 # algorithm. See "Java Cryptography Architecture Standard Algorithm Name
@@ -615,27 +610,42 @@
 # that rely on DSA, such as NONEwithDSA, SHA1withDSA.  However, the assertion
 # will not disable algorithms related to "ECDSA".
 #
-# A "Constraint" provides further guidance for the algorithm being specified.
-# The "KeySizeConstraint" requires a key of a valid size range if the
-# "AlgorithmName" is of a key algorithm.  The "DecimalInteger" indicates the
-# key size specified in number of bits.  For example, "RSA keySize <= 1024"
-# indicates that any RSA key with key size less than or equal to 1024 bits
-# should be disabled, and "RSA keySize < 1024, RSA keySize > 2048" indicates
-# that any RSA key with key size less than 1024 or greater than 2048 should
-# be disabled. Note that the "KeySizeConstraint" only makes sense to key
-# algorithms.
+# A "Constraint" defines restrictions on the keys and/or certificates for
+# a specified AlgorithmName:
+#
+#   KeySizeConstraint:
+#     keySize Operator KeyLength
+#       The constraint requires a key of a valid size range if the
+#       "AlgorithmName" is of a key algorithm.  The "KeyLength" indicates
+#       the key size specified in number of bits.  For example,
+#       "RSA keySize <= 1024" indicates that any RSA key with key size less
+#       than or equal to 1024 bits should be disabled, and
+#       "RSA keySize < 1024, RSA keySize > 2048" indicates that any RSA key
+#       with key size less than 1024 or greater than 2048 should be disabled.
+#       This constraint is only used on algorithms that have a key size.
 #
-# "CertConstraint" specifies additional constraints for
-# certificates that contain algorithms that are restricted:
+#   CAConstraint:
+#     jdkCA
+#       This constraint 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.  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"
 #
-#   "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"
+#   DenyAfterConstraint:
+#     denyAfter YYYY-MM-DD
+#       This constraint prohibits a certificate with the specified algorithm
+#       from being used after the date regardless of the certificate's
+#       validity.  JAR files that are signed and timestamped before the
+#       constraint date with certificates containing the disabled algorithm
+#       will not be restricted.  The date is processed in the UTC timezone.
+#       This constraint can only be used once in a DisabledAlgorithm
+#       expression.
+#       Example:  To deny usage of RSA 2048 bit certificates after Feb 3 2020,
+#       use the following:  "RSA keySize == 2048 & denyAfter 2020-02-03"
 #
 # When an algorithm must satisfy more than one constraint, it must be
 # delimited by an ampersand '&'.  For example, to restrict certificates in a