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                                 toUpperCase(Locale.ENGLISH)));
 251                 List<Constraint> constraintList =
 252                         constraintsMap.getOrDefault(algorithm,
 253                                 new ArrayList<>(1));
 254 
 255                 // Consider the impact of algorithm aliases.
 256                 for (String alias : AlgorithmDecomposer.getAliases(algorithm)) {
 257                     constraintsMap.putIfAbsent(alias, constraintList);
 258                 }
 259 
 260                 if (space <= 0) {
 261                     constraintList.add(new DisabledConstraint(algorithm));
 262                     continue;
 263                 }
 264 
 265                 String policy = constraintEntry.substring(space + 1);
 266 
 267                 // Convert constraint conditions into Constraint classes
 268                 Constraint c, lastConstraint = null;
 269                 // Allow only one jdkCA entry per constraint entry
 270                 boolean jdkCALimit = false;
 271                 // Allow only one denyAfter entry per constraint entry
 272                 boolean denyAfterLimit = false;
 273 
 274                 for (String entry : policy.split("&")) {
 275                     entry = entry.trim();
 276 
 277                     Matcher matcher;
 278                     if (entry.startsWith("keySize")) {
 279                         if (debug != null) {
 280                             debug.println("Constraints set to keySize: " +
 281                                     entry);
 282                         }
 283                         StringTokenizer tokens = new StringTokenizer(entry);
 284                         if (!"keySize".equals(tokens.nextToken())) {
 285                             throw new IllegalArgumentException("Error in " +
 286                                     "security property. Constraint unknown: " +
 287                                     entry);
 288                         }
 289                         c = new KeySizeConstraint(algorithm,
 290                                 KeySizeConstraint.Operator.of(tokens.nextToken()),
 291                                 Integer.parseInt(tokens.nextToken()));
 292 
 293                     } else if (entry.equalsIgnoreCase("jdkCA")) {
 294                         if (debug != null) {
 295                             debug.println("Constraints set to jdkCA.");
 296                         }
 297                         if (jdkCALimit) {
 298                             throw new IllegalArgumentException("Only one " +
 299                                     "jdkCA entry allowed in property. " +
 300                                     "Constraint: " + constraintEntry);
 301                         }
 302                         c = new jdkCAConstraint(algorithm);
 303                         jdkCALimit = true;
 304 
 305                     } else if (entry.startsWith("denyAfter") &&
 306                             (matcher = Holder.DENY_AFTER_PATTERN.matcher(entry))
 307                                     .matches()) {
 308                         if (debug != null) {
 309                             debug.println("Constraints set to denyAfter");
 310                         }
 311                         if (denyAfterLimit) {
 312                             throw new IllegalArgumentException("Only one " +
 313                                     "denyAfter entry allowed in property. " +
 314                                     "Constraint: " + constraintEntry);
 315                         }
 316                         int year = Integer.parseInt(matcher.group(1));
 317                         int month = Integer.parseInt(matcher.group(2));
 318                         int day = Integer.parseInt(matcher.group(3));
 319                         c = new DenyAfterConstraint(algorithm, year, month,
 320                                 day);
 321                         denyAfterLimit = true;
 322                     } else if (entry.startsWith("usage")) {
 323                         String s[] = (entry.substring(5)).trim().split(" ");
 324                         c = new UsageConstraint(algorithm, s);
 325                         if (debug != null) {
 326                             debug.println("Constraints usage length is " + s.length);
 327                         }
 328                     } else {
 329                         throw new IllegalArgumentException("Error in security" +
 330                                 " property. Constraint unknown: " + entry);
 331                     }
 332 
 333                     // Link multiple conditions for a single constraint
 334                     // into a linked list.
 335                     if (lastConstraint == null) {
 336                         constraintList.add(c);
 337                     } else {
 338                         lastConstraint.nextConstraint = c;
 339                     }
 340                     lastConstraint = c;
 341                 }
 342             }
 343         }
 344 
 345         // Get applicable constraints based off the signature algorithm
 346         private List<Constraint> getConstraints(String algorithm) {
 347             return constraintsMap.get(algorithm);
 348         }
 349 
 350         // Check if KeySizeConstraints permit the specified key
 351         public boolean permits(Key key) {
 352             List<Constraint> list = getConstraints(key.getAlgorithm());
 353             if (list == null) {
 354                 return true;
 355             }
 356             for (Constraint constraint : list) {
 357                 if (!constraint.permits(key)) {
 358                     if (debug != null) {
 359                         debug.println("keySizeConstraint: failed key " +
 360                                 "constraint check " + KeyUtil.getKeySize(key));
 361                     }
 362                     return false;
 363                 }
 364             }
 365             return true;
 366         }
 367 
 368         // Check if constraints permit this AlgorithmParameters.
 369         public boolean permits(String algorithm, AlgorithmParameters aps) {
 370             List<Constraint> list = getConstraints(algorithm);
 371             if (list == null) {
 372                 return true;
 373             }
 374 
 375             for (Constraint constraint : list) {
 376                 if (!constraint.permits(aps)) {
 377                     if (debug != null) {
 378                         debug.println("keySizeConstraint: failed algorithm " +
 379                                 "parameters constraint check " + aps);
 380                     }
 381 
 382                     return false;
 383                 }
 384             }
 385 
 386             return true;
 387         }
 388 
 389         // Check if constraints permit this cert.
 390         public void permits(String algorithm, ConstraintsParameters cp)
 391                 throws CertPathValidatorException {
 392             X509Certificate cert = cp.getCertificate();
 393 
 394             if (debug != null) {
 395                 debug.println("Constraints.permits(): " + algorithm +
 396                         " Variant: " + cp.getVariant());
 397             }
 398 
 399             // Get all signature algorithms to check for constraints
 400             Set<String> algorithms = new HashSet<>();
 401             if (algorithm != null) {
 402                 algorithms.addAll(AlgorithmDecomposer.decomposeOneHash(algorithm));
 403             }
 404 
 405             // Attempt to add the public key algorithm if cert provided
 406             if (cert != null) {
 407                 algorithms.add(cert.getPublicKey().getAlgorithm());
 408             }
 409             if (cp.getPublicKey() != null) {
 410                 algorithms.add(cp.getPublicKey().getAlgorithm());
 411             }
 412             // Check all applicable constraints
 413             for (String alg : algorithms) {
 414                 List<Constraint> list = getConstraints(alg);
 415                 if (list == null) {
 416                     continue;
 417                 }
 418                 for (Constraint constraint : list) {
 419                     constraint.permits(cp);
 420                 }
 421             }
 422         }
 423     }
 424 
 425     /**
 426      * This abstract Constraint class for algorithm-based checking
 427      * may contain one or more constraints.  If the '&' on the {@Security}
 428      * property is used, multiple constraints have been grouped together
 429      * requiring all the constraints to fail for the check to be disallowed.
 430      *
 431      * If the class contains multiple constraints, the next constraint
 432      * is stored in {@code nextConstraint} in linked-list fashion.
 433      */
 434     private abstract static class Constraint {
 435         String algorithm;
 436         Constraint nextConstraint = null;
 437 
 438         // operator
 439         enum Operator {
 440             EQ,         // "=="
 441             NE,         // "!="
 442             LT,         // "<"
 443             LE,         // "<="
 444             GT,         // ">"
 445             GE;         // ">="
 446 
 447             static Operator of(String s) {
 448                 switch (s) {
 449                     case "==":
 450                         return EQ;
 451                     case "!=":
 452                         return NE;
 453                     case "<":
 454                         return LT;
 455                     case "<=":
 456                         return LE;
 457                     case ">":
 458                         return GT;
 459                     case ">=":
 460                         return GE;
 461                 }
 462 
 463                 throw new IllegalArgumentException("Error in security " +
 464                         "property. " + s + " is not a legal Operator");
 465             }
 466         }
 467 
 468         /**
 469          * Check if an algorithm constraint is permitted with a given key.
 470          *
 471          * If the check inside of {@code permit()} fails, it must call
 472          * {@code next()} with the same {@code Key} parameter passed if
 473          * multiple constraints need to be checked.
 474          *
 475          * @param key Public key
 476          * @return 'true' if constraint is allowed, 'false' if disallowed.
 477          */
 478         public boolean permits(Key key) {
 479             return true;
 480         }
 481 
 482         /**
 483          * Check if the algorithm constraint permits a given cryptographic
 484          * parameters.
 485          *
 486          * @param parameters the cryptographic parameters
 487          * @return 'true' if the cryptographic parameters is allowed,
 488          *         'false' ortherwise.
 489          */
 490         public boolean permits(AlgorithmParameters parameters) {
 491             return true;
 492         }
 493 
 494         /**
 495          * Check if an algorithm constraint is permitted with a given
 496          * ConstraintsParameters.
 497          *
 498          * If the check inside of {@code permits()} fails, it must call
 499          * {@code next()} with the same {@code ConstraintsParameters}
 500          * parameter passed if multiple constraints need to be checked.
 501          *
 502          * @param cp CertConstraintParameter containing certificate info
 503          * @throws CertPathValidatorException if constraint disallows.
 504          *
 505          */
 506         public abstract void permits(ConstraintsParameters cp)
 507                 throws CertPathValidatorException;
 508 
 509         /**
 510          * Recursively check if the constraints are allowed.
 511          *
 512          * If {@code nextConstraint} is non-null, this method will
 513          * call {@code nextConstraint}'s {@code permits()} to check if the
 514          * constraint is allowed or denied.  If the constraint's
 515          * {@code permits()} is allowed, this method will exit this and any
 516          * recursive next() calls, returning 'true'.  If the constraints called
 517          * were disallowed, the last constraint will throw
 518          * {@code CertPathValidatorException}.
 519          *
 520          * @param cp ConstraintsParameters
 521          * @return 'true' if constraint allows the operation, 'false' if
 522          * we are at the end of the constraint list or,
 523          * {@code nextConstraint} is null.
 524          */
 525         boolean next(ConstraintsParameters cp)
 526                 throws CertPathValidatorException {
 527             if (nextConstraint != null) {
 528                 nextConstraint.permits(cp);
 529                 return true;
 530             }
 531             return false;
 532         }
 533 
 534         /**
 535          * Recursively check if this constraint is allowed,
 536          *
 537          * If {@code nextConstraint} is non-null, this method will
 538          * call {@code nextConstraint}'s {@code permit()} to check if the
 539          * constraint is allowed or denied.  If the constraint's
 540          * {@code permit()} is allowed, this method will exit this and any
 541          * recursive next() calls, returning 'true'.  If the constraints
 542          * called were disallowed the check will exit with 'false'.
 543          *
 544          * @param key Public key
 545          * @return 'true' if constraint allows the operation, 'false' if
 546          * the constraint denies the operation.
 547          */
 548         boolean next(Key key) {
 549             if (nextConstraint != null && nextConstraint.permits(key)) {
 550                 return true;
 551             }
 552             return false;
 553         }
 554 
 555         String extendedMsg(ConstraintsParameters cp) {
 556             return (cp.getCertificate() == null ? "." :
 557                     " used with certificate: " +
 558                             cp.getCertificate().getSubjectX500Principal() +
 559                     (cp.getVariant() != Validator.VAR_GENERIC ?
 560                             ".  Usage was " + cp.getVariant() : "."));
 561         }
 562     }
 563 
 564     /*
 565      * This class contains constraints dealing with the certificate chain
 566      * of the certificate.
 567      */
 568     private static class jdkCAConstraint extends Constraint {
 569         jdkCAConstraint(String algo) {
 570             algorithm = algo;
 571         }
 572 
 573         /*
 574          * Check if ConstraintsParameters has a trusted match, if it does
 575          * call next() for any following constraints. If it does not, exit
 576          * as this constraint(s) does not restrict the operation.
 577          */
 578         @Override
 579         public void permits(ConstraintsParameters cp)
 580                 throws CertPathValidatorException {
 581             if (debug != null) {
 582                 debug.println("jdkCAConstraints.permits(): " + algorithm);
 583             }
 584 
 585             // Check chain has a trust anchor in cacerts
 586             if (cp.isTrustedMatch()) {
 587                 if (next(cp)) {
 588                     return;
 589                 }
 590                 throw new CertPathValidatorException(
 591                         "Algorithm constraints check failed on certificate " +
 592                         "anchor limits. " + algorithm + extendedMsg(cp),
 593                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 594             }
 595         }
 596     }
 597 
 598     /*
 599      * This class handles the denyAfter constraint.  The date is in the UTC/GMT
 600      * timezone.
 601      */
 602     private static class DenyAfterConstraint extends Constraint {
 603         private Date denyAfterDate;
 604         private static final SimpleDateFormat dateFormat =
 605                 new SimpleDateFormat("EEE, MMM d HH:mm:ss z yyyy");
 606 
 607         DenyAfterConstraint(String algo, int year, int month, int day) {
 608             Calendar c;
 609 
 610             algorithm = algo;
 611 
 612             if (debug != null) {
 613                 debug.println("DenyAfterConstraint read in as:  year " +
 614                         year + ", month = " + month + ", day = " + day);
 615             }
 616 
 617             c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
 618                     .setDate(year, month - 1, day).build();
 619 
 620             if (year > c.getActualMaximum(Calendar.YEAR) ||
 621                     year < c.getActualMinimum(Calendar.YEAR)) {
 622                 throw new IllegalArgumentException(
 623                         "Invalid year given in constraint: " + year);
 624             }
 625             if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
 626                     (month - 1) < c.getActualMinimum(Calendar.MONTH)) {
 627                 throw new IllegalArgumentException(
 628                         "Invalid month given in constraint: " + month);
 629             }
 630             if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
 631                     day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
 632                 throw new IllegalArgumentException(
 633                         "Invalid Day of Month given in constraint: " + day);
 634             }
 635 
 636             denyAfterDate = c.getTime();
 637             if (debug != null) {
 638                 debug.println("DenyAfterConstraint date set to: " +
 639                         dateFormat.format(denyAfterDate));
 640             }
 641         }
 642 
 643         /*
 644          * Checking that the provided date is not beyond the constraint date.
 645          * The provided date can be the PKIXParameter date if given,
 646          * otherwise it is the current date.
 647          *
 648          * If the constraint disallows, call next() for any following
 649          * constraints. Throw an exception if this is the last constraint.
 650          */
 651         @Override
 652         public void permits(ConstraintsParameters cp)
 653                 throws CertPathValidatorException {
 654             Date currentDate;
 655             String errmsg;
 656 
 657             if (cp.getJARTimestamp() != null) {
 658                 currentDate = cp.getJARTimestamp().getTimestamp();
 659                 errmsg = "JAR Timestamp date: ";
 660             } else if (cp.getPKIXParamDate() != null) {
 661                 currentDate = cp.getPKIXParamDate();
 662                 errmsg = "PKIXParameter date: ";
 663             } else {
 664                 currentDate = new Date();
 665                 errmsg = "Current date: ";
 666             }
 667 
 668             if (!denyAfterDate.after(currentDate)) {
 669                 if (next(cp)) {
 670                     return;
 671                 }
 672                 throw new CertPathValidatorException(
 673                         "denyAfter constraint check failed: " + algorithm +
 674                         " used with Constraint date: " +
 675                         dateFormat.format(denyAfterDate) + "; " + errmsg +
 676                         dateFormat.format(currentDate) + extendedMsg(cp),
 677                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 678             }
 679         }
 680 
 681         /*
 682          * Return result if the constraint's date is beyond the current date
 683          * in UTC timezone.
 684          */
 685         @Override
 686         public boolean permits(Key key) {
 687             if (next(key)) {
 688                 return true;
 689             }
 690             if (debug != null) {
 691                 debug.println("DenyAfterConstraints.permits(): " + algorithm);
 692             }
 693 
 694             return denyAfterDate.after(new Date());
 695         }
 696     }
 697 
 698     /*
 699      * The usage constraint is for the "usage" keyword.  It checks against the
 700      * variant value in ConstraintsParameters.
 701      */
 702     private static class UsageConstraint extends Constraint {
 703         String[] usages;
 704 
 705         UsageConstraint(String algorithm, String[] usages) {
 706             this.algorithm = algorithm;
 707             this.usages = usages;
 708         }
 709 
 710         @Override
 711         public void permits(ConstraintsParameters cp)
 712                 throws CertPathValidatorException {
 713             for (String usage : usages) {
 714 
 715                 String v = null;
 716                 if (usage.compareToIgnoreCase("TLSServer") == 0) {
 717                     v = Validator.VAR_TLS_SERVER;
 718                 } else if (usage.compareToIgnoreCase("TLSClient") == 0) {
 719                     v = Validator.VAR_TLS_CLIENT;
 720                 } else if (usage.compareToIgnoreCase("SignedJAR") == 0) {
 721                     v = Validator.VAR_PLUGIN_CODE_SIGNING;
 722                 }
 723 
 724                 if (debug != null) {
 725                     debug.println("Checking if usage constraint \"" + v +
 726                             "\" matches \"" + cp.getVariant() + "\"");
 727                     if (Debug.isVerbose()) {
 728                         // Because usage checking can come from many places
 729                         // a stack trace is very helpful.
 730                         (new Exception()).printStackTrace(debug.getPrintStream());
 731                     }
 732                 }
 733                 if (cp.getVariant().compareTo(v) == 0) {
 734                     if (next(cp)) {
 735                         return;
 736                     }
 737                     throw new CertPathValidatorException("Usage constraint " +
 738                             usage + " check failed: " + algorithm +
 739                             extendedMsg(cp),
 740                             null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 741                 }
 742             }
 743         }
 744     }
 745 
 746     /*
 747      * This class contains constraints dealing with the key size
 748      * support limits per algorithm.   e.g.  "keySize <= 1024"
 749      */
 750     private static class KeySizeConstraint extends Constraint {
 751 
 752         private int minSize;            // the minimal available key size
 753         private int maxSize;            // the maximal available key size
 754         private int prohibitedSize = -1;    // unavailable key sizes
 755 
 756         public KeySizeConstraint(String algo, Operator operator, int length) {
 757             algorithm = algo;
 758             switch (operator) {
 759                 case EQ:      // an unavailable key size
 760                     this.minSize = 0;
 761                     this.maxSize = Integer.MAX_VALUE;
 762                     prohibitedSize = length;
 763                     break;
 764                 case NE:
 765                     this.minSize = length;
 766                     this.maxSize = length;
 767                     break;
 768                 case LT:
 769                     this.minSize = length;
 770                     this.maxSize = Integer.MAX_VALUE;
 771                     break;
 772                 case LE:
 773                     this.minSize = length + 1;
 774                     this.maxSize = Integer.MAX_VALUE;
 775                     break;
 776                 case GT:
 777                     this.minSize = 0;
 778                     this.maxSize = length;
 779                     break;
 780                 case GE:
 781                     this.minSize = 0;
 782                     this.maxSize = length > 1 ? (length - 1) : 0;
 783                     break;
 784                 default:
 785                     // unlikely to happen
 786                     this.minSize = Integer.MAX_VALUE;
 787                     this.maxSize = -1;
 788             }
 789         }
 790 
 791         /*
 792          * If we are passed a certificate, extract the public key and use it.
 793          *
 794          * Check if each constraint fails and check if there is a linked
 795          * constraint  Any permitted constraint will exit the linked list
 796          * to allow the operation.
 797          */
 798         @Override
 799         public void permits(ConstraintsParameters cp)
 800                 throws CertPathValidatorException {
 801             Key key = null;
 802             if (cp.getPublicKey() != null) {
 803                 key = cp.getPublicKey();
 804             } else if (cp.getCertificate() != null) {
 805                 key = cp.getCertificate().getPublicKey();
 806             }
 807             if (key != null && !permitsImpl(key)) {
 808                 if (nextConstraint != null) {
 809                     nextConstraint.permits(cp);
 810                     return;
 811                 }
 812                 throw new CertPathValidatorException(
 813                         "Algorithm constraints check failed on keysize limits. " +
 814                         algorithm + " " + KeyUtil.getKeySize(key) + "bit key" +
 815                         extendedMsg(cp),
 816                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 817             }
 818         }
 819 
 820 
 821         // Check if key constraint disable the specified key
 822         // Uses old style permit()
 823         @Override
 824         public boolean permits(Key key) {
 825             // If we recursively find a constraint that permits us to use
 826             // this key, return true and skip any other constraint checks.
 827             if (nextConstraint != null && nextConstraint.permits(key)) {
 828                 return true;
 829             }
 830             if (debug != null) {
 831                 debug.println("KeySizeConstraints.permits(): " + algorithm);
 832             }
 833 
 834             return permitsImpl(key);
 835         }
 836 
 837         @Override
 838         public boolean permits(AlgorithmParameters parameters) {
 839             String paramAlg = parameters.getAlgorithm();
 840             if (!algorithm.equalsIgnoreCase(parameters.getAlgorithm())) {
 841                 // Consider the impact of the algorithm aliases.
 842                 Collection<String> aliases =
 843                         AlgorithmDecomposer.getAliases(algorithm);
 844                 if (!aliases.contains(paramAlg)) {
 845                     return true;
 846                 }
 847             }
 848 
 849             int keySize = KeyUtil.getKeySize(parameters);
 850             if (keySize == 0) {
 851                 return false;
 852             } else if (keySize > 0) {
 853                 return !((keySize < minSize) || (keySize > maxSize) ||
 854                     (prohibitedSize == keySize));
 855             }   // Otherwise, the key size is not accessible or determined.
 856                 // Conservatively, please don't disable such keys.
 857 
 858             return true;
 859         }
 860 
 861         private boolean permitsImpl(Key key) {
 862             // Verify this constraint is for this public key algorithm
 863             if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) {
 864                 return true;
 865             }
 866 
 867             int size = KeyUtil.getKeySize(key);
 868             if (size == 0) {
 869                 return false;    // we don't allow any key of size 0.
 870             } else if (size > 0) {
 871                 return !((size < minSize) || (size > maxSize) ||
 872                     (prohibitedSize == size));
 873             }   // Otherwise, the key size is not accessible. Conservatively,
 874                 // please don't disable such keys.
 875 
 876             return true;
 877         }
 878     }
 879 
 880     /*
 881      * This constraint is used for the complete disabling of the algorithm.
 882      */
 883     private static class DisabledConstraint extends Constraint {
 884         DisabledConstraint(String algo) {
 885             algorithm = algo;
 886         }
 887 
 888         @Override
 889         public void permits(ConstraintsParameters cp)
 890                 throws CertPathValidatorException {
 891             throw new CertPathValidatorException(
 892                     "Algorithm constraints check failed on disabled " +
 893                             "algorithm: " + algorithm + extendedMsg(cp),
 894                     null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 895         }
 896 
 897         @Override
 898         public boolean permits(Key key) {
 899             return false;
 900         }
 901     }
 902 }
 903