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