1 /*
   2  * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.util;
  27 
  28 import sun.security.validator.Validator;
  29 
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.PrintStream;
  32 import java.security.CryptoPrimitive;
  33 import java.security.AlgorithmParameters;
  34 import java.security.Key;
  35 import java.security.cert.CertPathValidatorException;
  36 import java.security.cert.CertPathValidatorException.BasicReason;
  37 import java.security.cert.X509Certificate;
  38 import java.text.SimpleDateFormat;
  39 import java.util.ArrayList;
  40 import java.util.Calendar;
  41 import java.util.Date;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.List;
  45 import java.util.Locale;
  46 import java.util.Map;
  47 import java.util.Set;
  48 import java.util.Collection;
  49 import java.util.StringTokenizer;
  50 import java.util.TimeZone;
  51 import java.util.regex.Pattern;
  52 import java.util.regex.Matcher;
  53 
  54 /**
  55  * Algorithm constraints for disabled algorithms property
  56  *
  57  * See the "jdk.certpath.disabledAlgorithms" specification in java.security
  58  * for the syntax of the disabled algorithm string.
  59  */
  60 public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
  61     private static final Debug debug = Debug.getInstance("certpath");
  62 
  63     // Disabled algorithm security property for certificate path
  64     public static final String PROPERTY_CERTPATH_DISABLED_ALGS =
  65             "jdk.certpath.disabledAlgorithms";
  66 
  67     // Disabled algorithm security property for TLS
  68     public static final String PROPERTY_TLS_DISABLED_ALGS =
  69             "jdk.tls.disabledAlgorithms";
  70 
  71     // Disabled algorithm security property for jar
  72     public static final String PROPERTY_JAR_DISABLED_ALGS =
  73             "jdk.jar.disabledAlgorithms";
  74 
  75     // Property for disabled EC named curves
  76     private static final String PROPERTY_DISABLED_EC_CURVES =
  77             "jdk.disabled.namedCurves";
  78 
  79     private final List<String> disabledAlgorithms;
  80     private final Constraints algorithmConstraints;
  81 
  82     /**
  83      * Initialize algorithm constraints with the specified security property.
  84      *
  85      * @param propertyName the security property name that define the disabled
  86      *        algorithm constraints
  87      */
  88     public DisabledAlgorithmConstraints(String propertyName) {
  89         this(propertyName, new AlgorithmDecomposer());
  90     }
  91 
  92     /**
  93      * Initialize algorithm constraints with the specified security property
  94      * for a specific usage type.
  95      *
  96      * @param propertyName the security property name that define the disabled
  97      *        algorithm constraints
  98      * @param decomposer an alternate AlgorithmDecomposer.
  99      */
 100     public DisabledAlgorithmConstraints(String propertyName,
 101             AlgorithmDecomposer decomposer) {
 102         super(decomposer);
 103         disabledAlgorithms = getAlgorithms(propertyName);
 104 
 105         // Check for alias
 106         int ecindex = -1, i = 0;
 107         for (String s : disabledAlgorithms) {
 108             if (s.regionMatches(true, 0,"include ", 0, 8)) {
 109                 if (s.regionMatches(true, 8, PROPERTY_DISABLED_EC_CURVES, 0,
 110                         PROPERTY_DISABLED_EC_CURVES.length())) {
 111                     ecindex = i;
 112                     break;
 113                 }
 114             }
 115             i++;
 116         }
 117         if (ecindex > -1) {
 118             disabledAlgorithms.remove(ecindex);
 119             disabledAlgorithms.addAll(ecindex,
 120                     getAlgorithms(PROPERTY_DISABLED_EC_CURVES));
 121         }
 122         algorithmConstraints = new Constraints(disabledAlgorithms);
 123     }
 124 
 125     /*
 126      * This only checks if the algorithm has been completely disabled.  If
 127      * there are keysize or other limit, this method allow the algorithm.
 128      */
 129     @Override
 130     public final boolean permits(Set<CryptoPrimitive> primitives,
 131             String algorithm, AlgorithmParameters parameters) {
 132         if (!checkAlgorithm(disabledAlgorithms, algorithm, decomposer)) {
 133             return false;
 134         }
 135 
 136         if (parameters != null) {
 137             return algorithmConstraints.permits(algorithm, parameters);
 138         }
 139 
 140         return true;
 141     }
 142 
 143     /*
 144      * Checks if the key algorithm has been disabled or constraints have been
 145      * placed on the key.
 146      */
 147     @Override
 148     public final boolean permits(Set<CryptoPrimitive> primitives, Key key) {
 149         return checkConstraints(primitives, "", key, null);
 150     }
 151 
 152     /*
 153      * Checks if the key algorithm has been disabled or if constraints have
 154      * been placed on the key.
 155      */
 156     @Override
 157     public final boolean permits(Set<CryptoPrimitive> primitives,
 158             String algorithm, Key key, AlgorithmParameters parameters) {
 159 
 160         if (algorithm == null || algorithm.length() == 0) {
 161             throw new IllegalArgumentException("No algorithm name specified");
 162         }
 163 
 164         return checkConstraints(primitives, algorithm, key, parameters);
 165     }
 166 
 167     public final void permits(ConstraintsParameters cp)
 168             throws CertPathValidatorException {
 169         permits(cp.getAlgorithm(), cp);
 170     }
 171 
 172     public final void permits(String algorithm, Key key,
 173             AlgorithmParameters params, String variant)
 174             throws CertPathValidatorException {
 175         permits(algorithm, new ConstraintsParameters(algorithm, params, key,
 176                 (variant == null) ? Validator.VAR_GENERIC : variant));
 177     }
 178 
 179     /*
 180      * Check if a x509Certificate object is permitted.  Check if all
 181      * algorithms are allowed, certificate constraints, and the
 182      * public key against key constraints.
 183      *
 184      * Uses new style permit() which throws exceptions.
 185      */
 186 
 187     public final void permits(String algorithm, ConstraintsParameters cp)
 188             throws CertPathValidatorException {
 189 
 190         // Check if named curves in the ConstraintParameters are disabled.
 191         if (cp.getNamedCurve() != null) {
 192             for (String curve : cp.getNamedCurve()) {
 193                 if (!checkAlgorithm(disabledAlgorithms, curve, decomposer)) {
 194                     throw new CertPathValidatorException(
 195                             "Algorithm constraints check failed on disabled " +
 196                                     "algorithm: " + curve,
 197                             null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 198                 }
 199             }
 200         }
 201 
 202         algorithmConstraints.permits(algorithm, cp);
 203     }
 204 
 205     // Check if a string is contained inside the property
 206     public boolean checkProperty(String param) {
 207         param = param.toLowerCase(Locale.ENGLISH);
 208         for (String block : disabledAlgorithms) {
 209             if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) {
 210                 return true;
 211             }
 212         }
 213         return false;
 214     }
 215 
 216     // Check algorithm constraints with key and algorithm
 217     private boolean checkConstraints(Set<CryptoPrimitive> primitives,
 218             String algorithm, Key key, AlgorithmParameters parameters) {
 219 
 220         // check the key parameter, it cannot be null.
 221         if (key == null) {
 222             throw new IllegalArgumentException("The key cannot be null");
 223         }
 224 
 225         // check the signature algorithm with parameters
 226         if (algorithm != null && algorithm.length() != 0) {
 227             if (!permits(primitives, algorithm, parameters)) {
 228                 return false;
 229             }
 230         }
 231 
 232         // check the key algorithm
 233         if (!permits(primitives, key.getAlgorithm(), null)) {
 234             return false;
 235         }
 236 
 237         // If this is an elliptic curve, check disabled the named curve.
 238         for (String curve : ConstraintsParameters.getNamedCurveFromKey(key)) {
 239             if (!permits(primitives, curve, null)) {
 240                 return false;
 241             }
 242         }
 243 
 244         // check the key constraints
 245         return algorithmConstraints.permits(key);
 246     }
 247 
 248 
 249     /**
 250      * Key and Certificate Constraints
 251      *
 252      * The complete disabling of an algorithm is not handled by Constraints or
 253      * Constraint classes.  That is addressed with
 254      *   permit(Set<CryptoPrimitive>, String, AlgorithmParameters)
 255      *
 256      * When passing a Key to permit(), the boolean return values follow the
 257      * same as the interface class AlgorithmConstraints.permit().  This is to
 258      * maintain compatibility:
 259      * 'true' means the operation is allowed.
 260      * 'false' means it failed the constraints and is disallowed.
 261      *
 262      * When passing ConstraintsParameters through permit(), an exception
 263      * will be thrown on a failure to better identify why the operation was
 264      * disallowed.
 265      */
 266 
 267     private static class Constraints {
 268         private Map<String, List<Constraint>> constraintsMap = new HashMap<>();
 269 
 270         private static class Holder {
 271             private static final Pattern DENY_AFTER_PATTERN = Pattern.compile(
 272                     "denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})");
 273         }
 274 
 275         public Constraints(List<String> constraintArray) {
 276             for (String constraintEntry : constraintArray) {
 277                 if (constraintEntry == null || constraintEntry.isEmpty()) {
 278                     continue;
 279                 }
 280 
 281                 constraintEntry = constraintEntry.trim();
 282                 if (debug != null) {
 283                     debug.println("Constraints: " + constraintEntry);
 284                 }
 285 
 286                 // Check if constraint is a complete disabling of an
 287                 // algorithm or has conditions.
 288                 int space = constraintEntry.indexOf(' ');
 289                 String algorithm = AlgorithmDecomposer.hashName(
 290                         ((space > 0 ? constraintEntry.substring(0, space) :
 291                                 constraintEntry)));
 292                 List<Constraint> constraintList =
 293                         constraintsMap.getOrDefault(
 294                                 algorithm.toUpperCase(Locale.ENGLISH),
 295                                 new ArrayList<>(1));
 296 
 297                 // Consider the impact of algorithm aliases.
 298                 for (String alias : AlgorithmDecomposer.getAliases(algorithm)) {
 299                     constraintsMap.putIfAbsent(
 300                             alias.toUpperCase(Locale.ENGLISH), constraintList);
 301                 }
 302 
 303                 // If there is no whitespace, it is a algorithm name; however,
 304                 // if there is a whitespace, could be a multi-word EC curve too.
 305                 if (space <= 0 || CurveDB.lookup(constraintEntry) != null) {
 306                     constraintList.add(new DisabledConstraint(algorithm));
 307                     continue;
 308                 }
 309 
 310                 String policy = constraintEntry.substring(space + 1);
 311 
 312                 // Convert constraint conditions into Constraint classes
 313                 Constraint c, lastConstraint = null;
 314                 // Allow only one jdkCA entry per constraint entry
 315                 boolean jdkCALimit = false;
 316                 // Allow only one denyAfter entry per constraint entry
 317                 boolean denyAfterLimit = false;
 318 
 319                 for (String entry : policy.split("&")) {
 320                     entry = entry.trim();
 321 
 322                     Matcher matcher;
 323                     if (entry.startsWith("keySize")) {
 324                         if (debug != null) {
 325                             debug.println("Constraints set to keySize: " +
 326                                     entry);
 327                         }
 328                         StringTokenizer tokens = new StringTokenizer(entry);
 329                         if (!"keySize".equals(tokens.nextToken())) {
 330                             throw new IllegalArgumentException("Error in " +
 331                                     "security property. Constraint unknown: " +
 332                                     entry);
 333                         }
 334                         c = new KeySizeConstraint(algorithm,
 335                                 KeySizeConstraint.Operator.of(tokens.nextToken()),
 336                                 Integer.parseInt(tokens.nextToken()));
 337 
 338                     } else if (entry.equalsIgnoreCase("jdkCA")) {
 339                         if (debug != null) {
 340                             debug.println("Constraints set to jdkCA.");
 341                         }
 342                         if (jdkCALimit) {
 343                             throw new IllegalArgumentException("Only one " +
 344                                     "jdkCA entry allowed in property. " +
 345                                     "Constraint: " + constraintEntry);
 346                         }
 347                         c = new jdkCAConstraint(algorithm);
 348                         jdkCALimit = true;
 349 
 350                     } else if (entry.startsWith("denyAfter") &&
 351                             (matcher = Holder.DENY_AFTER_PATTERN.matcher(entry))
 352                                     .matches()) {
 353                         if (debug != null) {
 354                             debug.println("Constraints set to denyAfter");
 355                         }
 356                         if (denyAfterLimit) {
 357                             throw new IllegalArgumentException("Only one " +
 358                                     "denyAfter entry allowed in property. " +
 359                                     "Constraint: " + constraintEntry);
 360                         }
 361                         int year = Integer.parseInt(matcher.group(1));
 362                         int month = Integer.parseInt(matcher.group(2));
 363                         int day = Integer.parseInt(matcher.group(3));
 364                         c = new DenyAfterConstraint(algorithm, year, month,
 365                                 day);
 366                         denyAfterLimit = true;
 367                     } else if (entry.startsWith("usage")) {
 368                         String s[] = (entry.substring(5)).trim().split(" ");
 369                         c = new UsageConstraint(algorithm, s);
 370                         if (debug != null) {
 371                             debug.println("Constraints usage length is " + s.length);
 372                         }
 373                     } else {
 374                         throw new IllegalArgumentException("Error in security" +
 375                                 " property. Constraint unknown: " + entry);
 376                     }
 377 
 378                     // Link multiple conditions for a single constraint
 379                     // into a linked list.
 380                     if (lastConstraint == null) {
 381                         constraintList.add(c);
 382                     } else {
 383                         lastConstraint.nextConstraint = c;
 384                     }
 385                     lastConstraint = c;
 386                 }
 387             }
 388         }
 389 
 390         // Get applicable constraints based off the signature algorithm
 391         private List<Constraint> getConstraints(String algorithm) {
 392             return constraintsMap.get(algorithm.toUpperCase(Locale.ENGLISH));
 393         }
 394 
 395         // Check if KeySizeConstraints permit the specified key
 396         public boolean permits(Key key) {
 397             List<Constraint> list = getConstraints(key.getAlgorithm());
 398             if (list == null) {
 399                 return true;
 400             }
 401             for (Constraint constraint : list) {
 402                 if (!constraint.permits(key)) {
 403                     if (debug != null) {
 404                         debug.println("Constraints: failed key size" +
 405                                 "constraint check " + KeyUtil.getKeySize(key));
 406                     }
 407                     return false;
 408                 }
 409             }
 410             return true;
 411         }
 412 
 413         // Check if constraints permit this AlgorithmParameters.
 414         public boolean permits(String algorithm, AlgorithmParameters aps) {
 415             List<Constraint> list = getConstraints(algorithm);
 416             if (list == null) {
 417                 return true;
 418             }
 419 
 420             for (Constraint constraint : list) {
 421                 if (!constraint.permits(aps)) {
 422                     if (debug != null) {
 423                         debug.println("Constraints: failed algorithm " +
 424                                 "parameters constraint check " + aps);
 425                     }
 426 
 427                     return false;
 428                 }
 429             }
 430 
 431             return true;
 432         }
 433 
 434         // Check if constraints permit this cert.
 435         public void permits(String algorithm, ConstraintsParameters cp)
 436                 throws CertPathValidatorException {
 437             X509Certificate cert = cp.getCertificate();
 438 
 439             if (debug != null) {
 440                 debug.println("Constraints.permits(): " + cp.toString());
 441             }
 442 
 443             // Get all signature algorithms to check for constraints
 444             Set<String> algorithms = new HashSet<>();
 445             if (algorithm != null) {
 446                 algorithms.addAll(AlgorithmDecomposer.decomposeOneHash(algorithm));
 447                 algorithms.add(algorithm);
 448             }
 449 
 450             // Attempt to add the public key algorithm if cert provided
 451             if (cert != null) {
 452                 algorithms.add(cert.getPublicKey().getAlgorithm());
 453             }
 454             if (cp.getKey() != null) {
 455                 algorithms.add(cp.getKey().getAlgorithm());
 456             }
 457             // Check all applicable constraints
 458             for (String alg : algorithms) {
 459                 List<Constraint> list = getConstraints(alg);
 460                 if (list == null) {
 461                     continue;
 462                 }
 463                 for (Constraint constraint : list) {
 464                     constraint.permits(cp);
 465                 }
 466             }
 467         }
 468     }
 469 
 470     /**
 471      * This abstract Constraint class for algorithm-based checking
 472      * may contain one or more constraints.  If the '&' on the {@Security}
 473      * property is used, multiple constraints have been grouped together
 474      * requiring all the constraints to fail for the check to be disallowed.
 475      *
 476      * If the class contains multiple constraints, the next constraint
 477      * is stored in {@code nextConstraint} in linked-list fashion.
 478      */
 479     private abstract static class Constraint {
 480         String algorithm;
 481         Constraint nextConstraint = null;
 482 
 483         // operator
 484         enum Operator {
 485             EQ,         // "=="
 486             NE,         // "!="
 487             LT,         // "<"
 488             LE,         // "<="
 489             GT,         // ">"
 490             GE;         // ">="
 491 
 492             static Operator of(String s) {
 493                 switch (s) {
 494                     case "==":
 495                         return EQ;
 496                     case "!=":
 497                         return NE;
 498                     case "<":
 499                         return LT;
 500                     case "<=":
 501                         return LE;
 502                     case ">":
 503                         return GT;
 504                     case ">=":
 505                         return GE;
 506                 }
 507 
 508                 throw new IllegalArgumentException("Error in security " +
 509                         "property. " + s + " is not a legal Operator");
 510             }
 511         }
 512 
 513         /**
 514          * Check if an algorithm constraint is permitted with a given key.
 515          *
 516          * If the check inside of {@code permit()} fails, it must call
 517          * {@code next()} with the same {@code Key} parameter passed if
 518          * multiple constraints need to be checked.
 519          *
 520          * @param key Public key
 521          * @return 'true' if constraint is allowed, 'false' if disallowed.
 522          */
 523         public boolean permits(Key key) {
 524             return true;
 525         }
 526 
 527         /**
 528          * Check if the algorithm constraint permits a given cryptographic
 529          * parameters.
 530          *
 531          * @param parameters the cryptographic parameters
 532          * @return 'true' if the cryptographic parameters is allowed,
 533          *         'false' ortherwise.
 534          */
 535         public boolean permits(AlgorithmParameters parameters) {
 536             return true;
 537         }
 538 
 539         /**
 540          * Check if an algorithm constraint is permitted with a given
 541          * ConstraintsParameters.
 542          *
 543          * If the check inside of {@code permits()} fails, it must call
 544          * {@code next()} with the same {@code ConstraintsParameters}
 545          * parameter passed if multiple constraints need to be checked.
 546          *
 547          * @param cp CertConstraintParameter containing certificate info
 548          * @throws CertPathValidatorException if constraint disallows.
 549          *
 550          */
 551         public abstract void permits(ConstraintsParameters cp)
 552                 throws CertPathValidatorException;
 553 
 554         /**
 555          * Recursively check if the constraints are allowed.
 556          *
 557          * If {@code nextConstraint} is non-null, this method will
 558          * call {@code nextConstraint}'s {@code permits()} to check if the
 559          * constraint is allowed or denied.  If the constraint's
 560          * {@code permits()} is allowed, this method will exit this and any
 561          * recursive next() calls, returning 'true'.  If the constraints called
 562          * were disallowed, the last constraint will throw
 563          * {@code CertPathValidatorException}.
 564          *
 565          * @param cp ConstraintsParameters
 566          * @return 'true' if constraint allows the operation, 'false' if
 567          * we are at the end of the constraint list or,
 568          * {@code nextConstraint} is null.
 569          */
 570         boolean next(ConstraintsParameters cp)
 571                 throws CertPathValidatorException {
 572             if (nextConstraint != null) {
 573                 nextConstraint.permits(cp);
 574                 return true;
 575             }
 576             return false;
 577         }
 578 
 579         /**
 580          * Recursively check if this constraint is allowed,
 581          *
 582          * If {@code nextConstraint} is non-null, this method will
 583          * call {@code nextConstraint}'s {@code permit()} to check if the
 584          * constraint is allowed or denied.  If the constraint's
 585          * {@code permit()} is allowed, this method will exit this and any
 586          * recursive next() calls, returning 'true'.  If the constraints
 587          * called were disallowed the check will exit with 'false'.
 588          *
 589          * @param key Public key
 590          * @return 'true' if constraint allows the operation, 'false' if
 591          * the constraint denies the operation.
 592          */
 593         boolean next(Key key) {
 594             return nextConstraint != null && nextConstraint.permits(key);
 595         }
 596 
 597         String extendedMsg(ConstraintsParameters cp) {
 598             return (cp.getCertificate() == null ? "." :
 599                     " used with certificate: " +
 600                             cp.getCertificate().getSubjectX500Principal() +
 601                     (cp.getVariant() != Validator.VAR_GENERIC ?
 602                             ".  Usage was " + cp.getVariant() : "."));
 603         }
 604     }
 605 
 606     /*
 607      * This class contains constraints dealing with the certificate chain
 608      * of the certificate.
 609      */
 610     private static class jdkCAConstraint extends Constraint {
 611         jdkCAConstraint(String algo) {
 612             algorithm = algo;
 613         }
 614 
 615         /*
 616          * Check if ConstraintsParameters has a trusted match, if it does
 617          * call next() for any following constraints. If it does not, exit
 618          * as this constraint(s) does not restrict the operation.
 619          */
 620         @Override
 621         public void permits(ConstraintsParameters cp)
 622                 throws CertPathValidatorException {
 623             if (debug != null) {
 624                 debug.println("jdkCAConstraints.permits(): " + algorithm);
 625             }
 626 
 627             // Check chain has a trust anchor in cacerts
 628             if (cp.isTrustedMatch()) {
 629                 if (next(cp)) {
 630                     return;
 631                 }
 632                 throw new CertPathValidatorException(
 633                         "Algorithm constraints check failed on certificate " +
 634                         "anchor limits. " + algorithm + extendedMsg(cp),
 635                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 636             }
 637         }
 638     }
 639 
 640     /*
 641      * This class handles the denyAfter constraint.  The date is in the UTC/GMT
 642      * timezone.
 643      */
 644     private static class DenyAfterConstraint extends Constraint {
 645         private Date denyAfterDate;
 646         private static final SimpleDateFormat dateFormat =
 647                 new SimpleDateFormat("EEE, MMM d HH:mm:ss z yyyy");
 648 
 649         DenyAfterConstraint(String algo, int year, int month, int day) {
 650             Calendar c;
 651 
 652             algorithm = algo;
 653 
 654             if (debug != null) {
 655                 debug.println("DenyAfterConstraint read in as:  year " +
 656                         year + ", month = " + month + ", day = " + day);
 657             }
 658 
 659             c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
 660                     .setDate(year, month - 1, day).build();
 661 
 662             if (year > c.getActualMaximum(Calendar.YEAR) ||
 663                     year < c.getActualMinimum(Calendar.YEAR)) {
 664                 throw new IllegalArgumentException(
 665                         "Invalid year given in constraint: " + year);
 666             }
 667             if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
 668                     (month - 1) < c.getActualMinimum(Calendar.MONTH)) {
 669                 throw new IllegalArgumentException(
 670                         "Invalid month given in constraint: " + month);
 671             }
 672             if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
 673                     day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
 674                 throw new IllegalArgumentException(
 675                         "Invalid Day of Month given in constraint: " + day);
 676             }
 677 
 678             denyAfterDate = c.getTime();
 679             if (debug != null) {
 680                 debug.println("DenyAfterConstraint date set to: " +
 681                         dateFormat.format(denyAfterDate));
 682             }
 683         }
 684 
 685         /*
 686          * Checking that the provided date is not beyond the constraint date.
 687          * The provided date can be the PKIXParameter date if given,
 688          * otherwise it is the current date.
 689          *
 690          * If the constraint disallows, call next() for any following
 691          * constraints. Throw an exception if this is the last constraint.
 692          */
 693         @Override
 694         public void permits(ConstraintsParameters cp)
 695                 throws CertPathValidatorException {
 696             Date currentDate;
 697             String errmsg;
 698 
 699             if (cp.getJARTimestamp() != null) {
 700                 currentDate = cp.getJARTimestamp().getTimestamp();
 701                 errmsg = "JAR Timestamp date: ";
 702             } else if (cp.getPKIXParamDate() != null) {
 703                 currentDate = cp.getPKIXParamDate();
 704                 errmsg = "PKIXParameter date: ";
 705             } else {
 706                 currentDate = new Date();
 707                 errmsg = "Current date: ";
 708             }
 709 
 710             if (!denyAfterDate.after(currentDate)) {
 711                 if (next(cp)) {
 712                     return;
 713                 }
 714                 throw new CertPathValidatorException(
 715                         "denyAfter constraint check failed: " + algorithm +
 716                         " used with Constraint date: " +
 717                         dateFormat.format(denyAfterDate) + "; " + errmsg +
 718                         dateFormat.format(currentDate) + extendedMsg(cp),
 719                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 720             }
 721         }
 722 
 723         /*
 724          * Return result if the constraint's date is beyond the current date
 725          * in UTC timezone.
 726          */
 727         @Override
 728         public boolean permits(Key key) {
 729             if (next(key)) {
 730                 return true;
 731             }
 732             if (debug != null) {
 733                 debug.println("DenyAfterConstraints.permits(): " + algorithm);
 734             }
 735 
 736             return denyAfterDate.after(new Date());
 737         }
 738     }
 739 
 740     /*
 741      * The usage constraint is for the "usage" keyword.  It checks against the
 742      * variant value in ConstraintsParameters.
 743      */
 744     private static class UsageConstraint extends Constraint {
 745         String[] usages;
 746 
 747         UsageConstraint(String algorithm, String[] usages) {
 748             this.algorithm = algorithm;
 749             this.usages = usages;
 750         }
 751 
 752         @Override
 753         public void permits(ConstraintsParameters cp)
 754                 throws CertPathValidatorException {
 755             for (String usage : usages) {
 756 
 757                 String v = null;
 758                 if (usage.compareToIgnoreCase("TLSServer") == 0) {
 759                     v = Validator.VAR_TLS_SERVER;
 760                 } else if (usage.compareToIgnoreCase("TLSClient") == 0) {
 761                     v = Validator.VAR_TLS_CLIENT;
 762                 } else if (usage.compareToIgnoreCase("SignedJAR") == 0) {
 763                     v = Validator.VAR_PLUGIN_CODE_SIGNING;
 764                 }
 765 
 766                 if (debug != null) {
 767                     debug.println("Checking if usage constraint \"" + v +
 768                             "\" matches \"" + cp.getVariant() + "\"");
 769                     // Because usage checking can come from many places
 770                     // a stack trace is very helpful.
 771                     ByteArrayOutputStream ba = new ByteArrayOutputStream();
 772                     PrintStream ps = new PrintStream(ba);
 773                     (new Exception()).printStackTrace(ps);
 774                     debug.println(ba.toString());
 775                 }
 776                 if (cp.getVariant().compareTo(v) == 0) {
 777                     if (next(cp)) {
 778                         return;
 779                     }
 780                     throw new CertPathValidatorException("Usage constraint " +
 781                             usage + " check failed: " + algorithm +
 782                             extendedMsg(cp),
 783                             null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 784                 }
 785             }
 786         }
 787     }
 788 
 789     /*
 790      * This class contains constraints dealing with the key size
 791      * support limits per algorithm.   e.g.  "keySize <= 1024"
 792      */
 793     private static class KeySizeConstraint extends Constraint {
 794 
 795         private int minSize;            // the minimal available key size
 796         private int maxSize;            // the maximal available key size
 797         private int prohibitedSize = -1;    // unavailable key sizes
 798         private int size;
 799 
 800         public KeySizeConstraint(String algo, Operator operator, int length) {
 801             algorithm = algo;
 802             switch (operator) {
 803                 case EQ:      // an unavailable key size
 804                     this.minSize = 0;
 805                     this.maxSize = Integer.MAX_VALUE;
 806                     prohibitedSize = length;
 807                     break;
 808                 case NE:
 809                     this.minSize = length;
 810                     this.maxSize = length;
 811                     break;
 812                 case LT:
 813                     this.minSize = length;
 814                     this.maxSize = Integer.MAX_VALUE;
 815                     break;
 816                 case LE:
 817                     this.minSize = length + 1;
 818                     this.maxSize = Integer.MAX_VALUE;
 819                     break;
 820                 case GT:
 821                     this.minSize = 0;
 822                     this.maxSize = length;
 823                     break;
 824                 case GE:
 825                     this.minSize = 0;
 826                     this.maxSize = length > 1 ? (length - 1) : 0;
 827                     break;
 828                 default:
 829                     // unlikely to happen
 830                     this.minSize = Integer.MAX_VALUE;
 831                     this.maxSize = -1;
 832             }
 833         }
 834 
 835         /*
 836          * If we are passed a certificate, extract the public key and use it.
 837          *
 838          * Check if each constraint fails and check if there is a linked
 839          * constraint  Any permitted constraint will exit the linked list
 840          * to allow the operation.
 841          */
 842         @Override
 843         public void permits(ConstraintsParameters cp)
 844                 throws CertPathValidatorException {
 845             Key key = null;
 846             if (cp.getKey() != null) {
 847                 key = cp.getKey();
 848             } else if (cp.getCertificate() != null) {
 849                 key = cp.getCertificate().getPublicKey();
 850             }
 851             if (key != null && !permitsImpl(key)) {
 852                 if (nextConstraint != null) {
 853                     nextConstraint.permits(cp);
 854                     return;
 855                 }
 856                 throw new CertPathValidatorException(
 857                         "Algorithm constraints check failed on keysize limits. " +
 858                         algorithm + " " + KeyUtil.getKeySize(key) + "bit key" +
 859                         extendedMsg(cp),
 860                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 861             }
 862         }
 863 
 864 
 865         // Check if key constraint disable the specified key
 866         // Uses old style permit()
 867         @Override
 868         public boolean permits(Key key) {
 869             // If we recursively find a constraint that permits us to use
 870             // this key, return true and skip any other constraint checks.
 871             if (nextConstraint != null && nextConstraint.permits(key)) {
 872                 return true;
 873             }
 874             if (debug != null) {
 875                 debug.println("KeySizeConstraints.permits(): " + algorithm);
 876             }
 877 
 878             return permitsImpl(key);
 879         }
 880 
 881         @Override
 882         public boolean permits(AlgorithmParameters parameters) {
 883             String paramAlg = parameters.getAlgorithm();
 884             if (!algorithm.equalsIgnoreCase(parameters.getAlgorithm())) {
 885                 // Consider the impact of the algorithm aliases.
 886                 Collection<String> aliases =
 887                         AlgorithmDecomposer.getAliases(algorithm);
 888                 if (!aliases.contains(paramAlg)) {
 889                     return true;
 890                 }
 891             }
 892 
 893             int keySize = KeyUtil.getKeySize(parameters);
 894             if (keySize == 0) {
 895                 return false;
 896             } else if (keySize > 0) {
 897                 return !((keySize < minSize) || (keySize > maxSize) ||
 898                     (prohibitedSize == keySize));
 899             }   // Otherwise, the key size is not accessible or determined.
 900                 // Conservatively, please don't disable such keys.
 901 
 902             return true;
 903         }
 904 
 905         private boolean permitsImpl(Key key) {
 906             // Verify this constraint is for this public key algorithm
 907             if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) {
 908                 return true;
 909             }
 910 
 911             size = KeyUtil.getKeySize(key);
 912             if (size == 0) {
 913                 return false;    // we don't allow any key of size 0.
 914             } else if (size > 0) {
 915                 return !((size < minSize) || (size > maxSize) ||
 916                     (prohibitedSize == size));
 917             }   // Otherwise, the key size is not accessible. Conservatively,
 918                 // please don't disable such keys.
 919 
 920             return true;
 921         }
 922     }
 923 
 924     /*
 925      * This constraint is used for the complete disabling of the algorithm.
 926      */
 927     private static class DisabledConstraint extends Constraint {
 928         DisabledConstraint(String algo) {
 929             algorithm = algo;
 930         }
 931 
 932         @Override
 933         public void permits(ConstraintsParameters cp)
 934                 throws CertPathValidatorException {
 935             throw new CertPathValidatorException(
 936                     "Algorithm constraints check failed on disabled " +
 937                             "algorithm: " + algorithm + extendedMsg(cp),
 938                     null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 939         }
 940 
 941         @Override
 942         public boolean permits(Key key) {
 943             return false;
 944         }
 945     }
 946 }
 947