| /* |
| * Copyright (c) 2010, 2022, 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 sun.security.validator.Validator; |
| |
| import java.security.AlgorithmParameters; |
| import java.security.CryptoPrimitive; |
| import java.security.Key; |
| import java.security.cert.CertPathValidatorException; |
| import java.security.cert.CertPathValidatorException.BasicReason; |
| import java.security.interfaces.ECKey; |
| import java.security.interfaces.XECKey; |
| import java.security.spec.AlgorithmParameterSpec; |
| import java.security.spec.InvalidParameterSpecException; |
| import java.security.spec.MGF1ParameterSpec; |
| import java.security.spec.NamedParameterSpec; |
| import java.security.spec.PSSParameterSpec; |
| import java.time.DateTimeException; |
| import java.time.Instant; |
| import java.time.ZonedDateTime; |
| import java.time.ZoneId; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Collection; |
| import java.util.StringTokenizer; |
| import java.util.regex.Pattern; |
| import java.util.regex.Matcher; |
| |
| /** |
| * Algorithm constraints for disabled algorithms property |
| * |
| * See the "jdk.certpath.disabledAlgorithms" specification in java.security |
| * for the syntax of the disabled algorithm string. |
| */ |
| public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { |
| private static final Debug debug = Debug.getInstance("certpath"); |
| |
| // Disabled algorithm security property for certificate path |
| public static final String PROPERTY_CERTPATH_DISABLED_ALGS = |
| "jdk.certpath.disabledAlgorithms"; |
| |
| // Legacy algorithm security property for certificate path and jar |
| public static final String PROPERTY_SECURITY_LEGACY_ALGS = |
| "jdk.security.legacyAlgorithms"; |
| |
| // Disabled algorithm security property for TLS |
| public static final String PROPERTY_TLS_DISABLED_ALGS = |
| "jdk.tls.disabledAlgorithms"; |
| |
| // Disabled algorithm security property for jar |
| public static final String PROPERTY_JAR_DISABLED_ALGS = |
| "jdk.jar.disabledAlgorithms"; |
| |
| // Property for disabled EC named curves |
| private static final String PROPERTY_DISABLED_EC_CURVES = |
| "jdk.disabled.namedCurves"; |
| |
| private static final Pattern INCLUDE_PATTERN = Pattern.compile("include " + |
| PROPERTY_DISABLED_EC_CURVES, Pattern.CASE_INSENSITIVE); |
| |
| private static class CertPathHolder { |
| static final DisabledAlgorithmConstraints CONSTRAINTS = |
| new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS); |
| } |
| |
| private static class JarHolder { |
| static final DisabledAlgorithmConstraints CONSTRAINTS = |
| new DisabledAlgorithmConstraints(PROPERTY_JAR_DISABLED_ALGS); |
| } |
| |
| private final Set<String> disabledAlgorithms; |
| private final Constraints algorithmConstraints; |
| |
| public static DisabledAlgorithmConstraints certPathConstraints() { |
| return CertPathHolder.CONSTRAINTS; |
| } |
| |
| public static DisabledAlgorithmConstraints jarConstraints() { |
| return JarHolder.CONSTRAINTS; |
| } |
| |
| /** |
| * Initialize algorithm constraints with the specified security property. |
| * |
| * @param propertyName the security property name that define the disabled |
| * algorithm constraints |
| */ |
| public DisabledAlgorithmConstraints(String propertyName) { |
| this(propertyName, new AlgorithmDecomposer()); |
| } |
| |
| /** |
| * Initialize algorithm constraints with the specified security property |
| * for a specific usage type. |
| * |
| * @param propertyName the security property name that define the disabled |
| * algorithm constraints |
| * @param decomposer an alternate AlgorithmDecomposer. |
| */ |
| public DisabledAlgorithmConstraints(String propertyName, |
| AlgorithmDecomposer decomposer) { |
| super(decomposer); |
| disabledAlgorithms = getAlgorithms(propertyName); |
| |
| // Check for alias |
| for (String s : disabledAlgorithms) { |
| Matcher matcher = INCLUDE_PATTERN.matcher(s); |
| if (matcher.matches()) { |
| disabledAlgorithms.remove(matcher.group()); |
| disabledAlgorithms.addAll( |
| getAlgorithms(PROPERTY_DISABLED_EC_CURVES)); |
| break; |
| } |
| } |
| algorithmConstraints = new Constraints(propertyName, 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) { |
| if (primitives == null || primitives.isEmpty()) { |
| throw new IllegalArgumentException("The primitives cannot be null" + |
| " or empty."); |
| } |
| |
| if (!checkAlgorithm(disabledAlgorithms, algorithm, decomposer)) { |
| return false; |
| } |
| |
| if (parameters != null) { |
| return algorithmConstraints.permits(algorithm, parameters); |
| } |
| |
| return true; |
| } |
| |
| /* |
| * 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) { |
| |
| if (algorithm == null || algorithm.isEmpty()) { |
| throw new IllegalArgumentException("No algorithm name specified"); |
| } |
| |
| return checkConstraints(primitives, algorithm, key, parameters); |
| } |
| |
| public final void permits(String algorithm, AlgorithmParameters ap, |
| ConstraintsParameters cp, boolean checkKey) |
| throws CertPathValidatorException { |
| permits(algorithm, cp, checkKey); |
| if (ap != null) { |
| permits(ap, cp); |
| } |
| } |
| |
| private void permits(AlgorithmParameters ap, ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| |
| switch (ap.getAlgorithm().toUpperCase(Locale.ENGLISH)) { |
| case "RSASSA-PSS": |
| permitsPSSParams(ap, cp); |
| break; |
| default: |
| // unknown algorithm, just ignore |
| } |
| } |
| |
| private void permitsPSSParams(AlgorithmParameters ap, |
| ConstraintsParameters cp) throws CertPathValidatorException { |
| |
| try { |
| PSSParameterSpec pssParams = |
| ap.getParameterSpec(PSSParameterSpec.class); |
| String digestAlg = pssParams.getDigestAlgorithm(); |
| permits(digestAlg, cp, false); |
| AlgorithmParameterSpec mgfParams = pssParams.getMGFParameters(); |
| if (mgfParams instanceof MGF1ParameterSpec) { |
| String mgfDigestAlg = |
| ((MGF1ParameterSpec)mgfParams).getDigestAlgorithm(); |
| if (!mgfDigestAlg.equalsIgnoreCase(digestAlg)) { |
| permits(mgfDigestAlg, cp, false); |
| } |
| } |
| } catch (InvalidParameterSpecException ipse) { |
| // ignore |
| } |
| } |
| |
| public final void permits(String algorithm, ConstraintsParameters cp, |
| boolean checkKey) throws CertPathValidatorException { |
| if (checkKey) { |
| // Check if named curves in the key are disabled. |
| for (Key key : cp.getKeys()) { |
| for (String curve : getNamedCurveFromKey(key)) { |
| if (!checkAlgorithm(disabledAlgorithms, curve, decomposer)) { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on disabled " + |
| "algorithm: " + curve, |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| } |
| } |
| algorithmConstraints.permits(algorithm, cp, checkKey); |
| } |
| |
| private static List<String> getNamedCurveFromKey(Key key) { |
| if (key instanceof ECKey) { |
| NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams()); |
| return (nc == null ? List.of() |
| : Arrays.asList(nc.getNameAndAliases())); |
| } else if (key instanceof XECKey) { |
| return List.of( |
| ((NamedParameterSpec)((XECKey)key).getParams()).getName()); |
| } else { |
| return List.of(); |
| } |
| } |
| |
| // Check algorithm constraints with key and algorithm |
| private boolean checkConstraints(Set<CryptoPrimitive> primitives, |
| String algorithm, Key key, AlgorithmParameters parameters) { |
| |
| if (primitives == null || primitives.isEmpty()) { |
| throw new IllegalArgumentException("The primitives cannot be null" + |
| " or empty."); |
| } |
| |
| if (key == null) { |
| throw new IllegalArgumentException("The key cannot be null"); |
| } |
| |
| // check the signature algorithm with parameters |
| if (algorithm != null && !algorithm.isEmpty()) { |
| if (!permits(primitives, algorithm, parameters)) { |
| return false; |
| } |
| } |
| |
| // check the key algorithm |
| if (!permits(primitives, key.getAlgorithm(), null)) { |
| return false; |
| } |
| |
| // If this is an elliptic curve, check if it is disabled |
| for (String curve : getNamedCurveFromKey(key)) { |
| if (!permits(primitives, curve, null)) { |
| return false; |
| } |
| } |
| |
| // check the key constraints |
| return algorithmConstraints.permits(key); |
| } |
| |
| |
| /** |
| * Key and Certificate Constraints |
| * |
| * 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 ConstraintsParameters through permit(), an exception |
| * will be thrown on a failure to better identify why the operation was |
| * disallowed. |
| */ |
| private static class Constraints { |
| private Map<String, List<Constraint>> constraintsMap = new HashMap<>(); |
| |
| private static class Holder { |
| private static final Pattern DENY_AFTER_PATTERN = Pattern.compile( |
| "denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})"); |
| } |
| |
| public Constraints(String propertyName, Set<String> constraintSet) { |
| for (String constraintEntry : constraintSet) { |
| if (constraintEntry == null || constraintEntry.isEmpty()) { |
| continue; |
| } |
| |
| constraintEntry = constraintEntry.trim(); |
| if (debug != null) { |
| debug.println("Constraints: " + constraintEntry); |
| } |
| |
| // Check if constraint is a complete disabling of an |
| // algorithm or has conditions. |
| int space = constraintEntry.indexOf(' '); |
| String algorithm = AlgorithmDecomposer.decomposeDigestName( |
| space > 0 ? constraintEntry.substring(0, space) : |
| constraintEntry); |
| List<Constraint> constraintList = |
| constraintsMap.getOrDefault( |
| algorithm.toUpperCase(Locale.ENGLISH), |
| new ArrayList<>(1)); |
| |
| // Consider the impact of algorithm aliases. |
| for (String alias : AlgorithmDecomposer.getAliases(algorithm)) { |
| constraintsMap.putIfAbsent( |
| alias.toUpperCase(Locale.ENGLISH), constraintList); |
| } |
| |
| // If there is no whitespace, it is a algorithm name; however, |
| // if there is a whitespace, could be a multi-word EC curve too. |
| if (space <= 0 || CurveDB.lookup(constraintEntry) != null) { |
| constraintList.add(new DisabledConstraint(algorithm)); |
| continue; |
| } |
| |
| String policy = constraintEntry.substring(space + 1); |
| |
| // Convert constraint conditions into Constraint classes |
| 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(); |
| |
| Matcher matcher; |
| if (entry.startsWith("keySize")) { |
| if (debug != null) { |
| debug.println("Constraints set to keySize: " + |
| entry); |
| } |
| StringTokenizer tokens = new StringTokenizer(entry); |
| if (!"keySize".equals(tokens.nextToken())) { |
| throw new IllegalArgumentException("Error in " + |
| "security property. Constraint unknown: " + |
| entry); |
| } |
| c = new KeySizeConstraint(algorithm, |
| KeySizeConstraint.Operator.of(tokens.nextToken()), |
| Integer.parseInt(tokens.nextToken())); |
| |
| } 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 if (entry.startsWith("denyAfter") && |
| (matcher = Holder.DENY_AFTER_PATTERN.matcher(entry)) |
| .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 if (entry.startsWith("usage")) { |
| String s[] = (entry.substring(5)).trim().split(" "); |
| c = new UsageConstraint(algorithm, s); |
| if (debug != null) { |
| debug.println("Constraints usage length is " + s.length); |
| } |
| } 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) { |
| constraintList.add(c); |
| } else { |
| lastConstraint.nextConstraint = c; |
| } |
| lastConstraint = c; |
| } |
| } |
| } |
| |
| // Get applicable constraints based off the algorithm |
| private List<Constraint> getConstraints(String algorithm) { |
| return constraintsMap.get(algorithm.toUpperCase(Locale.ENGLISH)); |
| } |
| |
| // Check if KeySizeConstraints permit the specified key |
| public boolean permits(Key key) { |
| List<Constraint> list = getConstraints(key.getAlgorithm()); |
| if (list == null) { |
| return true; |
| } |
| for (Constraint constraint : list) { |
| if (!constraint.permits(key)) { |
| if (debug != null) { |
| debug.println("Constraints: failed key size " + |
| "constraint check " + KeyUtil.getKeySize(key)); |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Check if constraints permit this AlgorithmParameters. |
| public boolean permits(String algorithm, AlgorithmParameters aps) { |
| List<Constraint> list = getConstraints(algorithm); |
| if (list == null) { |
| return true; |
| } |
| |
| for (Constraint constraint : list) { |
| if (!constraint.permits(aps)) { |
| if (debug != null) { |
| debug.println("Constraints: failed algorithm " + |
| "parameters constraint check " + aps); |
| } |
| |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| public void permits(String algorithm, ConstraintsParameters cp, |
| boolean checkKey) throws CertPathValidatorException { |
| |
| if (debug != null) { |
| debug.println("Constraints.permits(): " + algorithm + ", " |
| + cp.toString()); |
| } |
| |
| // Get all signature algorithms to check for constraints |
| Set<String> algorithms = new HashSet<>(); |
| if (algorithm != null) { |
| algorithms.addAll(AlgorithmDecomposer.decomposeName(algorithm)); |
| algorithms.add(algorithm); |
| } |
| |
| if (checkKey) { |
| for (Key key : cp.getKeys()) { |
| algorithms.add(key.getAlgorithm()); |
| } |
| } |
| |
| // Check all applicable constraints |
| for (String alg : algorithms) { |
| List<Constraint> list = getConstraints(alg); |
| if (list == null) { |
| continue; |
| } |
| for (Constraint constraint : list) { |
| if (!checkKey && constraint instanceof KeySizeConstraint) { |
| continue; |
| } |
| constraint.permits(cp); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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; |
| |
| // operator |
| enum Operator { |
| EQ, // "==" |
| NE, // "!=" |
| LT, // "<" |
| LE, // "<=" |
| GT, // ">" |
| GE; // ">=" |
| |
| static Operator of(String s) { |
| switch (s) { |
| case "==": |
| return EQ; |
| case "!=": |
| return NE; |
| case "<": |
| return LT; |
| case "<=": |
| return LE; |
| case ">": |
| return GT; |
| case ">=": |
| return GE; |
| } |
| |
| throw new IllegalArgumentException("Error in security " + |
| "property. " + s + " is not a legal Operator"); |
| } |
| } |
| |
| /** |
| * 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 constraint is allowed, 'false' if disallowed. |
| */ |
| public boolean permits(Key key) { |
| return true; |
| } |
| |
| /** |
| * Check if the algorithm constraint permits a given cryptographic |
| * parameters. |
| * |
| * @param parameters the cryptographic parameters |
| * @return 'true' if the cryptographic parameters is allowed, |
| * 'false' ortherwise. |
| */ |
| public boolean permits(AlgorithmParameters parameters) { |
| return true; |
| } |
| |
| /** |
| * Check if an algorithm constraint is permitted with a given |
| * ConstraintsParameters. |
| * |
| * If the check inside of {@code permits()} fails, it must call |
| * {@code next()} with the same {@code ConstraintsParameters} |
| * parameter passed if multiple constraints need to be checked. |
| * |
| * @param cp ConstraintsParameter containing certificate info |
| * @throws CertPathValidatorException if constraint disallows. |
| * |
| */ |
| public abstract void permits(ConstraintsParameters 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 ConstraintsParameters |
| * @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(ConstraintsParameters 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) { |
| return nextConstraint != null && nextConstraint.permits(key); |
| } |
| } |
| |
| /* |
| * 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 ConstraintsParameters 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. |
| */ |
| @Override |
| public void permits(ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| if (debug != null) { |
| debug.println("jdkCAConstraints.permits(): " + algorithm); |
| } |
| |
| // Check if any certs chain back to at least one trust anchor in |
| // cacerts |
| if (cp.anchorIsJdkCA()) { |
| if (next(cp)) { |
| return; |
| } |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on certificate " + |
| "anchor limits. " + algorithm + cp.extendedExceptionMsg(), |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| } |
| |
| /* |
| * This class handles the denyAfter constraint. The date is in the UTC/GMT |
| * timezone. |
| */ |
| private static class DenyAfterConstraint extends Constraint { |
| private ZonedDateTime zdt; |
| private Instant denyAfterDate; |
| |
| DenyAfterConstraint(String algo, int year, int month, int day) { |
| |
| algorithm = algo; |
| |
| if (debug != null) { |
| debug.println("DenyAfterConstraint read in as: year " + |
| year + ", month = " + month + ", day = " + day); |
| } |
| |
| try { |
| zdt = ZonedDateTime |
| .of(year, month, day, 0, 0, 0, 0, ZoneId.of("GMT")); |
| denyAfterDate = zdt.toInstant(); |
| } catch (DateTimeException dte) { |
| throw new IllegalArgumentException( |
| "Invalid denyAfter date", dte); |
| } |
| |
| if (debug != null) { |
| debug.println("DenyAfterConstraint date set to: " + |
| zdt.toLocalDate()); |
| } |
| } |
| |
| /* |
| * 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(ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| Instant currentDate; |
| |
| if (cp.getDate() != null) { |
| currentDate = cp.getDate().toInstant(); |
| } else { |
| currentDate = Instant.now(); |
| } |
| |
| if (!denyAfterDate.isAfter(currentDate)) { |
| if (next(cp)) { |
| return; |
| } |
| throw new CertPathValidatorException( |
| "denyAfter constraint check failed: " + algorithm + |
| " used with Constraint date: " + |
| zdt.toLocalDate() + "; params date: " + |
| currentDate + cp.extendedExceptionMsg(), |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| |
| /* |
| * Return result if the constraint's date is beyond the current date |
| * in UTC timezone. |
| */ |
| @Override |
| public boolean permits(Key key) { |
| if (next(key)) { |
| return true; |
| } |
| if (debug != null) { |
| debug.println("DenyAfterConstraints.permits(): " + algorithm); |
| } |
| |
| return denyAfterDate.isAfter(Instant.now()); |
| } |
| } |
| |
| /* |
| * The usage constraint is for the "usage" keyword. It checks against the |
| * variant value in ConstraintsParameters. |
| */ |
| private static class UsageConstraint extends Constraint { |
| String[] usages; |
| |
| UsageConstraint(String algorithm, String[] usages) { |
| this.algorithm = algorithm; |
| this.usages = usages; |
| } |
| |
| @Override |
| public void permits(ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| String variant = cp.getVariant(); |
| for (String usage : usages) { |
| |
| boolean match = false; |
| switch (usage.toLowerCase(Locale.ENGLISH)) { |
| case "tlsserver": |
| match = variant.equals(Validator.VAR_TLS_SERVER); |
| break; |
| case "tlsclient": |
| match = variant.equals(Validator.VAR_TLS_CLIENT); |
| break; |
| case "signedjar": |
| match = |
| variant.equals(Validator.VAR_CODE_SIGNING) || |
| variant.equals(Validator.VAR_TSA_SERVER); |
| break; |
| } |
| |
| if (debug != null) { |
| debug.println("Checking if usage constraint \"" + usage + |
| "\" matches \"" + cp.getVariant() + "\""); |
| if (Debug.isVerbose()) { |
| // Because usage checking can come from many places |
| // a stack trace is very helpful. |
| (new Exception()).printStackTrace(debug.getPrintStream()); |
| } |
| } |
| if (match) { |
| if (next(cp)) { |
| return; |
| } |
| throw new CertPathValidatorException("Usage constraint " + |
| usage + " check failed: " + algorithm + |
| cp.extendedExceptionMsg(), |
| 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(String algo, Operator operator, int length) { |
| algorithm = algo; |
| switch (operator) { |
| case EQ: // an unavailable key size |
| this.minSize = 0; |
| this.maxSize = Integer.MAX_VALUE; |
| prohibitedSize = length; |
| break; |
| case NE: |
| this.minSize = length; |
| this.maxSize = length; |
| break; |
| case LT: |
| this.minSize = length; |
| this.maxSize = Integer.MAX_VALUE; |
| break; |
| case LE: |
| this.minSize = length + 1; |
| this.maxSize = Integer.MAX_VALUE; |
| break; |
| case GT: |
| this.minSize = 0; |
| this.maxSize = length; |
| break; |
| case GE: |
| this.minSize = 0; |
| this.maxSize = length > 1 ? (length - 1) : 0; |
| break; |
| default: |
| // unlikely to happen |
| this.minSize = Integer.MAX_VALUE; |
| this.maxSize = -1; |
| } |
| } |
| |
| /* |
| * For each key, 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. |
| */ |
| @Override |
| public void permits(ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| for (Key key : cp.getKeys()) { |
| if (!permitsImpl(key)) { |
| if (nextConstraint != null) { |
| nextConstraint.permits(cp); |
| continue; |
| } |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on keysize limits: " + |
| algorithm + " " + KeyUtil.getKeySize(key) + " bit key" + |
| cp.extendedExceptionMsg(), |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| } |
| } |
| |
| |
| // Check if key constraint disable the specified key |
| // Uses old style permit() |
| @Override |
| 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); |
| } |
| |
| @Override |
| public boolean permits(AlgorithmParameters parameters) { |
| String paramAlg = parameters.getAlgorithm(); |
| if (!algorithm.equalsIgnoreCase(parameters.getAlgorithm())) { |
| // Consider the impact of the algorithm aliases. |
| Collection<String> aliases = |
| AlgorithmDecomposer.getAliases(algorithm); |
| if (!aliases.contains(paramAlg)) { |
| return true; |
| } |
| } |
| |
| int keySize = KeyUtil.getKeySize(parameters); |
| if (keySize == 0) { |
| return false; |
| } else if (keySize > 0) { |
| return !((keySize < minSize) || (keySize > maxSize) || |
| (prohibitedSize == keySize)); |
| } // Otherwise, the key size is not accessible or determined. |
| // Conservatively, please don't disable such keys. |
| |
| return true; |
| } |
| |
| 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 false; // we don't allow any key of size 0. |
| } else if (size > 0) { |
| return !((size < minSize) || (size > maxSize) || |
| (prohibitedSize == size)); |
| } // Otherwise, the key size is not accessible. Conservatively, |
| // please don't disable such keys. |
| |
| return true; |
| } |
| } |
| |
| /* |
| * This constraint is used for the complete disabling of the algorithm. |
| */ |
| private static class DisabledConstraint extends Constraint { |
| DisabledConstraint(String algo) { |
| algorithm = algo; |
| } |
| |
| @Override |
| public void permits(ConstraintsParameters cp) |
| throws CertPathValidatorException { |
| throw new CertPathValidatorException( |
| "Algorithm constraints check failed on disabled " + |
| "algorithm: " + algorithm + cp.extendedExceptionMsg(), |
| null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); |
| } |
| |
| @Override |
| public boolean permits(Key key) { |
| return false; |
| } |
| } |
| } |