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