1 /*
   2  * Copyright (c) 2010, 2016, 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 java.security.CryptoPrimitive;
  29 import java.security.AlgorithmParameters;
  30 import java.security.Key;
  31 import java.security.cert.CertPathValidatorException;
  32 import java.security.cert.CertPathValidatorException.BasicReason;
  33 import java.security.cert.X509Certificate;
  34 import java.text.SimpleDateFormat;
  35 import java.util.Calendar;
  36 import java.util.Date;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.Locale;
  40 import java.util.Map;
  41 import java.util.Set;
  42 import java.util.TimeZone;
  43 import java.util.regex.Pattern;
  44 import java.util.regex.Matcher;
  45 
  46 /**
  47  * Algorithm constraints for disabled algorithms property
  48  *
  49  * See the "jdk.certpath.disabledAlgorithms" specification in java.security
  50  * for the syntax of the disabled algorithm string.
  51  */
  52 public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints {
  53     private static final Debug debug = Debug.getInstance("certpath");
  54 
  55     // the known security property, jdk.certpath.disabledAlgorithms
  56     public static final String PROPERTY_CERTPATH_DISABLED_ALGS =
  57             "jdk.certpath.disabledAlgorithms";
  58 
  59     // the known security property, jdk.tls.disabledAlgorithms
  60     public static final String PROPERTY_TLS_DISABLED_ALGS =
  61             "jdk.tls.disabledAlgorithms";
  62 
  63     private final String[] disabledAlgorithms;
  64     private final Constraints algorithmConstraints;
  65 
  66     /**
  67      * Initialize algorithm constraints with the specified security property.
  68      *
  69      * @param propertyName the security property name that define the disabled
  70      *        algorithm constraints
  71      */
  72     public DisabledAlgorithmConstraints(String propertyName) {
  73         this(propertyName, new AlgorithmDecomposer());
  74     }
  75 
  76     public DisabledAlgorithmConstraints(String propertyName,
  77             AlgorithmDecomposer decomposer) {
  78         super(decomposer);
  79         disabledAlgorithms = getAlgorithms(propertyName);
  80         algorithmConstraints = new Constraints(disabledAlgorithms);
  81     }
  82 
  83     /*
  84      * This only checks if the algorithm has been completely disabled.  If
  85      * there are keysize or other limit, this method allow the algorithm.
  86      */
  87     @Override
  88     public final boolean permits(Set<CryptoPrimitive> primitives,
  89             String algorithm, AlgorithmParameters parameters) {
  90 
  91         if (primitives == null || primitives.isEmpty()) {
  92             throw new IllegalArgumentException(
  93                         "No cryptographic primitive specified");
  94         }
  95 
  96         return checkAlgorithm(disabledAlgorithms, algorithm, decomposer);
  97     }
  98 
  99     /*
 100      * Checks if the key algorithm has been disabled or constraints have been
 101      * placed on the key.
 102      */
 103     @Override
 104     public final boolean permits(Set<CryptoPrimitive> primitives, Key key) {
 105         return checkConstraints(primitives, "", key, null);
 106     }
 107 
 108     /*
 109      * Checks if the key algorithm has been disabled or if constraints have
 110      * been placed on the key.
 111      */
 112     @Override
 113     public final boolean permits(Set<CryptoPrimitive> primitives,
 114             String algorithm, Key key, AlgorithmParameters parameters) {
 115 
 116         if (algorithm == null || algorithm.length() == 0) {
 117             throw new IllegalArgumentException("No algorithm name specified");
 118         }
 119 
 120         return checkConstraints(primitives, algorithm, key, parameters);
 121     }
 122 
 123     /*
 124      * Check if a x509Certificate object is permitted.  Check if all
 125      * algorithms are allowed, certificate constraints, and the
 126      * public key against key constraints.
 127      *
 128      * Uses new style permit() which throws exceptions.
 129      */
 130     public final void permits(Set<CryptoPrimitive> primitives,
 131             CertConstraintParameters cp) throws CertPathValidatorException {
 132         checkConstraints(primitives, cp);
 133     }
 134 
 135     /*
 136      * Check if Certificate object is within the constraints.
 137      * Uses new style permit() which throws exceptions.
 138      */
 139     public final void permits(Set<CryptoPrimitive> primitives,
 140             X509Certificate cert) throws CertPathValidatorException {
 141         checkConstraints(primitives, new CertConstraintParameters(cert));
 142     }
 143 
 144     // Check if a string is contained inside the property
 145     public boolean checkProperty(String param) {
 146         param = param.toLowerCase(Locale.ENGLISH);
 147         for (String block : disabledAlgorithms) {
 148             if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) {
 149                 return true;
 150             }
 151         }
 152         return false;
 153     }
 154 
 155     // Check algorithm constraints with key and algorithm
 156     private boolean checkConstraints(Set<CryptoPrimitive> primitives,
 157             String algorithm, Key key, AlgorithmParameters parameters) {
 158 
 159         // check the key parameter, it cannot be null.
 160         if (key == null) {
 161             throw new IllegalArgumentException("The key cannot be null");
 162         }
 163 
 164         // check the signature algorithm
 165         if (algorithm != null && algorithm.length() != 0) {
 166             if (!permits(primitives, algorithm, parameters)) {
 167                 return false;
 168             }
 169         }
 170 
 171         // check the key algorithm
 172         if (!permits(primitives, key.getAlgorithm(), null)) {
 173             return false;
 174         }
 175 
 176         // check the key constraints
 177         return algorithmConstraints.permits(key);
 178     }
 179 
 180     /*
 181      * Check algorithm constraints with Certificate
 182      * Uses new style permit() which throws exceptions.
 183      */
 184     private void checkConstraints(Set<CryptoPrimitive> primitives,
 185             CertConstraintParameters cp) throws CertPathValidatorException {
 186 
 187         X509Certificate cert = cp.getCertificate();
 188         String algorithm = cert.getSigAlgName();
 189 
 190         // Check signature algorithm is not disabled
 191         if (!permits(primitives, algorithm, null)) {
 192             throw new CertPathValidatorException(
 193                     "Algorithm constraints check failed on disabled "+
 194                             "signature algorithm: " + algorithm,
 195                     null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 196         }
 197 
 198         // Check key algorithm is not disabled
 199         if (!permits(primitives, cert.getPublicKey().getAlgorithm(), null)) {
 200             throw new CertPathValidatorException(
 201                     "Algorithm constraints check failed on disabled "+
 202                             "public key algorithm: " + algorithm,
 203                     null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 204         }
 205 
 206         // Check the certificate and key constraints
 207         algorithmConstraints.permits(cp);
 208 
 209     }
 210 
 211     /**
 212      * Key and Certificate Constraints
 213      *
 214      * The complete disabling of an algorithm is not handled by Constraints or
 215      * Constraint classes.  That is addressed with
 216      *   permit(Set<CryptoPrimitive>, String, AlgorithmParameters)
 217      *
 218      * When passing a Key to permit(), the boolean return values follow the
 219      * same as the interface class AlgorithmConstraints.permit().  This is to
 220      * maintain compatibility:
 221      * 'true' means the operation is allowed.
 222      * 'false' means it failed the constraints and is disallowed.
 223      *
 224      * When passing CertConstraintParameters through permit(), an exception
 225      * will be thrown on a failure to better identify why the operation was
 226      * disallowed.
 227      */
 228 
 229     private static class Constraints {
 230         private Map<String, Set<Constraint>> constraintsMap = new HashMap<>();
 231         private static final Pattern keySizePattern = Pattern.compile(
 232                 "keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)");
 233         private static final Pattern denyAfterPattern = Pattern.compile(
 234                 "denyAfter\\s+(\\d{4})-(\\d{2})-(\\d{2})");
 235 
 236         public Constraints(String[] constraintArray) {
 237             for (String constraintEntry : constraintArray) {
 238                 if (constraintEntry == null || constraintEntry.isEmpty()) {
 239                     continue;
 240                 }
 241 
 242                 constraintEntry = constraintEntry.trim();
 243                 if (debug != null) {
 244                     debug.println("Constraints: " + constraintEntry);
 245                 }
 246 
 247                 // Check if constraint is a complete disabling of an
 248                 // algorithm or has conditions.
 249                 String algorithm;
 250                 String policy;
 251                 int space = constraintEntry.indexOf(' ');
 252                 if (space > 0) {
 253                     algorithm = AlgorithmDecomposer.hashName(
 254                             constraintEntry.substring(0, space).
 255                                     toUpperCase(Locale.ENGLISH));
 256                     policy = constraintEntry.substring(space + 1);
 257                 } else {
 258                     constraintsMap.computeIfAbsent(
 259                             constraintEntry.toUpperCase(Locale.ENGLISH),
 260                             k -> new HashSet<>());
 261                     continue;
 262                 }
 263 
 264                 // Convert constraint conditions into Constraint classes
 265                 Constraint c, lastConstraint = null;
 266                 // Allow only one jdkCA entry per constraint entry
 267                 boolean jdkCALimit = false;
 268                 // Allow only one denyAfter entry per constraint entry
 269                 boolean denyAfterLimit = false;
 270 
 271                 for (String entry : policy.split("&")) {
 272                     entry = entry.trim();
 273 
 274                     Matcher matcher = keySizePattern.matcher(entry);
 275                     if (matcher.matches()) {
 276                         if (debug != null) {
 277                             debug.println("Constraints set to keySize: " +
 278                                     entry);
 279                         }
 280                         c = new KeySizeConstraint(algorithm,
 281                                 KeySizeConstraint.Operator.of(matcher.group(1)),
 282                                 Integer.parseInt(matcher.group(2)));
 283 
 284                     } else if (entry.equalsIgnoreCase("jdkCA")) {
 285                         if (debug != null) {
 286                             debug.println("Constraints set to jdkCA.");
 287                         }
 288                         if (jdkCALimit) {
 289                             throw new IllegalArgumentException("Only one " +
 290                                     "jdkCA entry allowed in property. " +
 291                                     "Constraint: " + constraintEntry);
 292                         }
 293                         c = new jdkCAConstraint(algorithm);
 294                         jdkCALimit = true;
 295 
 296                     } else if(matcher.usePattern(denyAfterPattern).matches()) {
 297                         if (debug != null) {
 298                             debug.println("Constraints set to denyAfter");
 299                         }
 300                         if (denyAfterLimit) {
 301                             throw new IllegalArgumentException("Only one " +
 302                                     "denyAfter entry allowed in property. " +
 303                                     "Constraint: " + constraintEntry);
 304                         }
 305                         int year = Integer.parseInt(matcher.group(1));
 306                         int month = Integer.parseInt(matcher.group(2));
 307                         int day = Integer.parseInt(matcher.group(3));
 308                         c = new DenyAfterConstraint(algorithm, year, month,
 309                                 day);
 310                         denyAfterLimit = true;
 311                     } else {
 312                         throw new IllegalArgumentException("Error in security" +
 313                                 " property. Constraint unknown: " + entry);
 314                     }
 315 
 316                     // Link multiple conditions for a single constraint
 317                     // into a linked list.
 318                     if (lastConstraint == null) {
 319                         if (!constraintsMap.containsKey(algorithm)) {
 320                             constraintsMap.putIfAbsent(algorithm,
 321                                     new HashSet<>());
 322                         }
 323                         constraintsMap.get(algorithm).add(c);
 324                     } else {
 325                         lastConstraint.nextConstraint = c;
 326                     }
 327                     lastConstraint = c;
 328                 }
 329             }
 330         }
 331 
 332         // Get applicable constraints based off the signature algorithm
 333         private Set<Constraint> getConstraints(String algorithm) {
 334             return constraintsMap.get(algorithm);
 335         }
 336 
 337         // Check if KeySizeConstraints permit the specified key
 338         public boolean permits(Key key) {
 339             Set<Constraint> set = getConstraints(key.getAlgorithm());
 340             if (set == null) {
 341                 return true;
 342             }
 343             for (Constraint constraint : set) {
 344                 if (!constraint.permits(key)) {
 345                     if (debug != null) {
 346                         debug.println("keySizeConstraint: failed key " +
 347                                 "constraint check " + KeyUtil.getKeySize(key));
 348                     }
 349                     return false;
 350                 }
 351             }
 352             return true;
 353         }
 354 
 355         // Check if constraints permit this cert.
 356         public void permits(CertConstraintParameters cp)
 357                 throws CertPathValidatorException {
 358             X509Certificate cert = cp.getCertificate();
 359 
 360             if (debug != null) {
 361                 debug.println("Constraints.permits(): " + cert.getSigAlgName());
 362             }
 363 
 364             // Get all signature algorithms to check for constraints
 365             Set<String> algorithms =
 366                     AlgorithmDecomposer.decomposeOneHash(cert.getSigAlgName());
 367             if (algorithms == null || algorithms.isEmpty()) {
 368                 return;
 369             }
 370 
 371             // Attempt to add the public key algorithm to the set
 372             algorithms.add(cert.getPublicKey().getAlgorithm());
 373 
 374             // Check all applicable constraints
 375             for (String algorithm : algorithms) {
 376                 Set<Constraint> set = getConstraints(algorithm);
 377                 if (set == null) {
 378                     continue;
 379                 }
 380                 for (Constraint constraint : set) {
 381                     constraint.permits(cp);
 382                 }
 383             }
 384         }
 385     }
 386 
 387     /**
 388      * This abstract Constraint class for algorithm-based checking
 389      * may contain one or more constraints.  If the '&' on the {@Security}
 390      * property is used, multiple constraints have been grouped together
 391      * requiring all the constraints to fail for the check to be disallowed.
 392      *
 393      * If the class contains multiple constraints, the next constraint
 394      * is stored in {@code nextConstraint} in linked-list fashion.
 395      */
 396     private abstract static class Constraint {
 397         String algorithm;
 398         Constraint nextConstraint = null;
 399 
 400         // operator
 401         enum Operator {
 402             EQ,         // "=="
 403             NE,         // "!="
 404             LT,         // "<"
 405             LE,         // "<="
 406             GT,         // ">"
 407             GE;         // ">="
 408 
 409             static Operator of(String s) {
 410                 switch (s) {
 411                     case "==":
 412                         return EQ;
 413                     case "!=":
 414                         return NE;
 415                     case "<":
 416                         return LT;
 417                     case "<=":
 418                         return LE;
 419                     case ">":
 420                         return GT;
 421                     case ">=":
 422                         return GE;
 423                 }
 424 
 425                 throw new IllegalArgumentException("Error in security " +
 426                         "property. " + s + " is not a legal Operator");
 427             }
 428         }
 429 
 430         /**
 431          * Check if an algorithm constraint is permitted with a given key.
 432          *
 433          * If the check inside of {@code permit()} fails, it must call
 434          * {@code next()} with the same {@code Key} parameter passed if
 435          * multiple constraints need to be checked.
 436          *
 437          * @param key Public key
 438          * @return 'true' if constraint is allowed, 'false' if disallowed.
 439          */
 440         public boolean permits(Key key) {
 441             return true;
 442         }
 443 
 444         /**
 445          * Check if an algorithm constraint is permitted with a given
 446          * CertConstraintParameters.
 447          *
 448          * If the check inside of {@code permits()} fails, it must call
 449          * {@code next()} with the same {@code CertConstraintParameters}
 450          * parameter passed if multiple constraints need to be checked.
 451          *
 452          * @param cp CertConstraintParameter containing certificate info
 453          * @throws CertPathValidatorException if constraint disallows.
 454          *
 455          */
 456         public abstract void permits(CertConstraintParameters cp)
 457                 throws CertPathValidatorException;
 458 
 459         /**
 460          * Recursively check if the constraints are allowed.
 461          *
 462          * If {@code nextConstraint} is non-null, this method will
 463          * call {@code nextConstraint}'s {@code permits()} to check if the
 464          * constraint is allowed or denied.  If the constraint's
 465          * {@code permits()} is allowed, this method will exit this and any
 466          * recursive next() calls, returning 'true'.  If the constraints called
 467          * were disallowed, the last constraint will throw
 468          * {@code CertPathValidatorException}.
 469          *
 470          * @param cp CertConstraintParameters
 471          * @return 'true' if constraint allows the operation, 'false' if
 472          * we are at the end of the constraint list or,
 473          * {@code nextConstraint} is null.
 474          */
 475         boolean next(CertConstraintParameters cp)
 476                 throws CertPathValidatorException {
 477             if (nextConstraint != null) {
 478                 nextConstraint.permits(cp);
 479                 return true;
 480             }
 481             return false;
 482         }
 483 
 484         /**
 485          * Recursively check if this constraint is allowed,
 486          *
 487          * If {@code nextConstraint} is non-null, this method will
 488          * call {@code nextConstraint}'s {@code permit()} to check if the
 489          * constraint is allowed or denied.  If the constraint's
 490          * {@code permit()} is allowed, this method will exit this and any
 491          * recursive next() calls, returning 'true'.  If the constraints
 492          * called were disallowed the check will exit with 'false'.
 493          *
 494          * @param key Public key
 495          * @return 'true' if constraint allows the operation, 'false' if
 496          * the constraint denies the operation.
 497          */
 498         boolean next(Key key) {
 499             if (nextConstraint != null && nextConstraint.permits(key)) {
 500                 return true;
 501             }
 502             return false;
 503         }
 504     }
 505 
 506     /*
 507      * This class contains constraints dealing with the certificate chain
 508      * of the certificate.
 509      */
 510     private static class jdkCAConstraint extends Constraint {
 511         jdkCAConstraint(String algo) {
 512             algorithm = algo;
 513         }
 514 
 515         /*
 516          * Check if CertConstraintParameters has a trusted match, if it does
 517          * call next() for any following constraints. If it does not, exit
 518          * as this constraint(s) does not restrict the operation.
 519          */
 520         public void permits(CertConstraintParameters cp)
 521                 throws CertPathValidatorException {
 522             if (debug != null) {
 523                 debug.println("jdkCAConstraints.permits(): " + algorithm);
 524             }
 525 
 526             // Check chain has a trust anchor in cacerts
 527             if (cp.isTrustedMatch()) {
 528                 if (next(cp)) {
 529                     return;
 530                 }
 531                 throw new CertPathValidatorException(
 532                         "Algorithm constraints check failed on certificate " +
 533                                 "anchor limits. " + algorithm + " used with " +
 534                                 cp.getCertificate().getSubjectX500Principal(),
 535                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 536             }
 537         }
 538     }
 539 
 540     /*
 541      * This class handles the denyAfter constraint.  The date is in the UTC/GMT
 542      * timezone.
 543      */
 544      private static class DenyAfterConstraint extends Constraint {
 545          private Date denyAfterDate;
 546          private static final SimpleDateFormat dateFormat =
 547                  new SimpleDateFormat("EEE, MMM d HH:mm:ss z yyyy");
 548 
 549          DenyAfterConstraint(String algo, int year, int month, int day) {
 550              Calendar c;
 551 
 552              algorithm = algo;
 553 
 554              if (debug != null) {
 555                  debug.println("DenyAfterConstraint read in as:  year " +
 556                          year + ", month = " + month + ", day = " + day);
 557              }
 558 
 559              c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
 560                      .setDate(year, month - 1, day).build();
 561 
 562              if (year > c.getActualMaximum(Calendar.YEAR) ||
 563                      year < c.getActualMinimum(Calendar.YEAR)) {
 564                  throw new IllegalArgumentException(
 565                          "Invalid year given in constraint: " + year);
 566              }
 567              if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
 568                      (month - 1) < c.getActualMinimum(Calendar.MONTH)) {
 569                  throw new IllegalArgumentException(
 570                          "Invalid month given in constraint: " + month);
 571              }
 572              if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
 573                      day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
 574                  throw new IllegalArgumentException(
 575                          "Invalid Day of Month given in constraint: " + day);
 576              }
 577 
 578              denyAfterDate = c.getTime();
 579              if (debug != null) {
 580                  debug.println("DenyAfterConstraint date set to: " +
 581                          dateFormat.format(denyAfterDate));
 582              }
 583          }
 584 
 585          /*
 586           * Checking that the provided date is not beyond the constraint date.
 587           * The provided date can be the PKIXParameter date if given,
 588           * otherwise it is the current date.
 589           *
 590           * If the constraint disallows, call next() for any following
 591           * constraints. Throw an exception if this is the last constraint.
 592           */
 593          @Override
 594          public void permits(CertConstraintParameters cp)
 595                  throws CertPathValidatorException {
 596              Date currentDate;
 597              String errmsg;
 598 
 599              if (cp.getJARTimestamp() != null) {
 600                  currentDate = cp.getJARTimestamp().getTimestamp();
 601                  errmsg = "JAR Timestamp date: ";
 602              } else if (cp.getPKIXParamDate() != null) {
 603                  currentDate = cp.getPKIXParamDate();
 604                  errmsg = "PKIXParameter date: ";
 605              } else {
 606                  currentDate = new Date();
 607                  errmsg = "Certificate date: ";
 608              }
 609 
 610              if (!denyAfterDate.after(currentDate)) {
 611                  if (next(cp)) {
 612                      return;
 613                  }
 614                  throw new CertPathValidatorException(
 615                          "denyAfter constraint check failed: " + algorithm +
 616                                  " used with Constraint date: " +
 617                                  dateFormat.format(denyAfterDate) + "; "
 618                                  + errmsg + dateFormat.format(currentDate),
 619                          null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 620              }
 621          }
 622 
 623          /*
 624           * Return result if the constraint's date is beyond the current date
 625           * in UTC timezone.
 626           */
 627          public boolean permits(Key key) {
 628              if (next(key)) {
 629                  return true;
 630              }
 631              if (debug != null) {
 632                  debug.println("DenyAfterConstraints.permits(): " + algorithm);
 633              }
 634 
 635              return denyAfterDate.after(new Date());
 636          }
 637      }
 638 
 639     /*
 640      * This class contains constraints dealing with the key size
 641      * support limits per algorithm.   e.g.  "keySize <= 1024"
 642      */
 643     private static class KeySizeConstraint extends Constraint {
 644 
 645         private int minSize;            // the minimal available key size
 646         private int maxSize;            // the maximal available key size
 647         private int prohibitedSize = -1;    // unavailable key sizes
 648         private int size;
 649 
 650         public KeySizeConstraint(String algo, Operator operator, int length) {
 651             algorithm = algo;
 652             switch (operator) {
 653                 case EQ:      // an unavailable key size
 654                     this.minSize = 0;
 655                     this.maxSize = Integer.MAX_VALUE;
 656                     prohibitedSize = length;
 657                     break;
 658                 case NE:
 659                     this.minSize = length;
 660                     this.maxSize = length;
 661                     break;
 662                 case LT:
 663                     this.minSize = length;
 664                     this.maxSize = Integer.MAX_VALUE;
 665                     break;
 666                 case LE:
 667                     this.minSize = length + 1;
 668                     this.maxSize = Integer.MAX_VALUE;
 669                     break;
 670                 case GT:
 671                     this.minSize = 0;
 672                     this.maxSize = length;
 673                     break;
 674                 case GE:
 675                     this.minSize = 0;
 676                     this.maxSize = length > 1 ? (length - 1) : 0;
 677                     break;
 678                 default:
 679                     // unlikely to happen
 680                     this.minSize = Integer.MAX_VALUE;
 681                     this.maxSize = -1;
 682             }
 683         }
 684 
 685         /*
 686          * If we are passed a certificate, extract the public key and use it.
 687          *
 688          * Check if each constraint fails and check if there is a linked
 689          * constraint  Any permitted constraint will exit the linked list
 690          * to allow the operation.
 691          */
 692         public void permits(CertConstraintParameters cp)
 693                 throws CertPathValidatorException {
 694             if (!permitsImpl(cp.getCertificate().getPublicKey())) {
 695                 if (nextConstraint != null) {
 696                     nextConstraint.permits(cp);
 697                     return;
 698                 }
 699                 throw new CertPathValidatorException(
 700                         "Algorithm constraints check failed on keysize limits. "
 701                                 + algorithm + " " + size + "bit key used with "
 702                                 + cp.getCertificate().getSubjectX500Principal(),
 703                         null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
 704             }
 705         }
 706 
 707 
 708         // Check if key constraint disable the specified key
 709         // Uses old style permit()
 710         public boolean permits(Key key) {
 711             // If we recursively find a constraint that permits us to use
 712             // this key, return true and skip any other constraint checks.
 713             if (nextConstraint != null && nextConstraint.permits(key)) {
 714                 return true;
 715             }
 716             if (debug != null) {
 717                 debug.println("KeySizeConstraints.permits(): " + algorithm);
 718             }
 719 
 720             return permitsImpl(key);
 721         }
 722 
 723         private boolean permitsImpl(Key key) {
 724             // Verify this constraint is for this public key algorithm
 725             if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) {
 726                 return true;
 727             }
 728 
 729             size = KeyUtil.getKeySize(key);
 730             if (size == 0) {
 731                 return false;    // we don't allow any key of size 0.
 732             } else if (size > 0) {
 733                 return !((size < minSize) || (size > maxSize) ||
 734                     (prohibitedSize == size));
 735             }   // Otherwise, the key size is not accessible. Conservatively,
 736                 // please don't disable such keys.
 737 
 738             return true;
 739         }
 740     }
 741 }
 742