8154005: Add algorithm constraint that specifies the restriction date
Reviewed-by: mullan, igerasim
--- 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