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