1 /* 2 * Copyright (c) 2010, 2015, 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.util.HashMap; 35 import java.util.HashSet; 36 import java.util.Locale; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.regex.Pattern; 40 import java.util.regex.Matcher; 41 42 /** 43 * Algorithm constraints for disabled algorithms property 44 * 45 * See the "jdk.certpath.disabledAlgorithms" specification in java.security 46 * for the syntax of the disabled algorithm string. 47 */ 48 public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { 49 private static final Debug debug = Debug.getInstance("certpath"); 50 51 // the known security property, jdk.certpath.disabledAlgorithms 52 public final static String PROPERTY_CERTPATH_DISABLED_ALGS = 53 "jdk.certpath.disabledAlgorithms"; 54 55 // the known security property, jdk.tls.disabledAlgorithms 56 public final static String PROPERTY_TLS_DISABLED_ALGS = 57 "jdk.tls.disabledAlgorithms"; 58 59 // the known security property, jdk.jar.disabledAlgorithms 60 public static final String PROPERTY_JAR_DISABLED_ALGS = 61 "jdk.jar.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 */ DisabledAlgorithmConstraints(String propertyName)72 public DisabledAlgorithmConstraints(String propertyName) { 73 this(propertyName, new AlgorithmDecomposer()); 74 } 75 76 /** 77 * Initialize algorithm constraints with the specified security property 78 * for a specific usage type. 79 * 80 * @param propertyName the security property name that define the disabled 81 * algorithm constraints 82 * @param decomposer an alternate AlgorithmDecomposer. 83 */ DisabledAlgorithmConstraints(String propertyName, AlgorithmDecomposer decomposer)84 public DisabledAlgorithmConstraints(String propertyName, 85 AlgorithmDecomposer decomposer) { 86 super(decomposer); 87 disabledAlgorithms = getAlgorithms(propertyName); 88 algorithmConstraints = new Constraints(disabledAlgorithms); 89 } 90 91 /* 92 * This only checks if the algorithm has been completely disabled. If 93 * there are keysize or other limit, this method allow the algorithm. 94 */ 95 @Override permits(Set<CryptoPrimitive> primitives, String algorithm, AlgorithmParameters parameters)96 final public boolean permits(Set<CryptoPrimitive> primitives, 97 String algorithm, AlgorithmParameters parameters) { 98 if (primitives == null || primitives.isEmpty()) { 99 throw new IllegalArgumentException( 100 "No cryptographic primitive specified"); 101 } 102 103 return checkAlgorithm(disabledAlgorithms, algorithm, decomposer); 104 } 105 106 /* 107 * Checks if the key algorithm has been disabled or constraints have been 108 * placed on the key. 109 */ 110 @Override permits(Set<CryptoPrimitive> primitives, Key key)111 final public boolean permits(Set<CryptoPrimitive> primitives, Key key) { 112 return checkConstraints(primitives, "", key, null); 113 } 114 115 /* 116 * Checks if the key algorithm has been disabled or if constraints have 117 * been placed on the key. 118 */ 119 @Override permits(Set<CryptoPrimitive> primitives, String algorithm, Key key, AlgorithmParameters parameters)120 final public boolean permits(Set<CryptoPrimitive> primitives, 121 String algorithm, Key key, AlgorithmParameters parameters) { 122 123 if (algorithm == null || algorithm.length() == 0) { 124 throw new IllegalArgumentException("No algorithm name specified"); 125 } 126 127 return checkConstraints(primitives, algorithm, key, parameters); 128 } 129 130 /* 131 * Check if a x509Certificate object is permitted. Check if all 132 * algorithms are allowed, certificate constraints, and the 133 * public key against key constraints. 134 * 135 * Uses new style permit() which throws exceptions. 136 */ permits(Set<CryptoPrimitive> primitives, CertConstraintParameters cp)137 public final void permits(Set<CryptoPrimitive> primitives, 138 CertConstraintParameters cp) throws CertPathValidatorException { 139 checkConstraints(primitives, cp); 140 } 141 142 /* 143 * Check if Certificate object is within the constraints. 144 * Uses new style permit() which throws exceptions. 145 */ permits(Set<CryptoPrimitive> primitives, X509Certificate cert)146 public final void permits(Set<CryptoPrimitive> primitives, 147 X509Certificate cert) throws CertPathValidatorException { 148 checkConstraints(primitives, new CertConstraintParameters(cert)); 149 } 150 151 // Check if a string is contained inside the property checkProperty(String param)152 public boolean checkProperty(String param) { 153 param = param.toLowerCase(Locale.ENGLISH); 154 for (String block : disabledAlgorithms) { 155 if (block.toLowerCase(Locale.ENGLISH).indexOf(param) >= 0) { 156 return true; 157 } 158 } 159 return false; 160 } 161 162 // Check algorithm constraints with key and algorithm checkConstraints(Set<CryptoPrimitive> primitives, String algorithm, Key key, AlgorithmParameters parameters)163 private boolean checkConstraints(Set<CryptoPrimitive> primitives, 164 String algorithm, Key key, AlgorithmParameters parameters) { 165 166 // check the key parameter, it cannot be null. 167 if (key == null) { 168 throw new IllegalArgumentException("The key cannot be null"); 169 } 170 171 // check the signature algorithm 172 if (algorithm != null && algorithm.length() != 0) { 173 if (!permits(primitives, algorithm, parameters)) { 174 return false; 175 } 176 } 177 178 // check the key algorithm 179 if (!permits(primitives, key.getAlgorithm(), null)) { 180 return false; 181 } 182 183 // check the key constraints 184 return algorithmConstraints.permits(key); 185 } 186 187 /* 188 * Check algorithm constraints with Certificate 189 * Uses new style permit() which throws exceptions. 190 */ checkConstraints(Set<CryptoPrimitive> primitives, CertConstraintParameters cp)191 private void checkConstraints(Set<CryptoPrimitive> primitives, 192 CertConstraintParameters cp) throws CertPathValidatorException { 193 194 X509Certificate cert = cp.getCertificate(); 195 String algorithm = cert.getSigAlgName(); 196 197 // Check signature algorithm is not disabled 198 if (!permits(primitives, algorithm, null)) { 199 throw new CertPathValidatorException( 200 "Algorithm constraints check failed on disabled "+ 201 "signature algorithm: " + algorithm, 202 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 203 } 204 205 // Check key algorithm is not disabled 206 if (!permits(primitives, cert.getPublicKey().getAlgorithm(), null)) { 207 throw new CertPathValidatorException( 208 "Algorithm constraints check failed on disabled "+ 209 "public key algorithm: " + algorithm, 210 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 211 } 212 213 // Check the certificate and key constraints 214 algorithmConstraints.permits(cp); 215 216 } 217 218 /** 219 * Key and Certificate Constraints 220 * 221 * The complete disabling of an algorithm is not handled by Constraints or 222 * Constraint classes. That is addressed with 223 * permit(Set<CryptoPrimitive>, String, AlgorithmParameters) 224 * 225 * When passing a Key to permit(), the boolean return values follow the 226 * same as the interface class AlgorithmConstraints.permit(). This is to 227 * maintain compatibility: 228 * 'true' means the operation is allowed. 229 * 'false' means it failed the constraints and is disallowed. 230 * 231 * When passing CertConstraintParameters through permit(), an exception 232 * will be thrown on a failure to better identify why the operation was 233 * disallowed. 234 */ 235 236 private static class Constraints { 237 private Map<String, Set<Constraint>> constraintsMap = new HashMap<>(); 238 private static final Pattern keySizePattern = Pattern.compile( 239 "keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); 240 Constraints(String[] constraintArray)241 public Constraints(String[] constraintArray) { 242 for (String constraintEntry : constraintArray) { 243 if (constraintEntry == null || constraintEntry.isEmpty()) { 244 continue; 245 } 246 247 constraintEntry = constraintEntry.trim(); 248 if (debug != null) { 249 debug.println("Constraints: " + constraintEntry); 250 } 251 252 // Check if constraint is a complete disabling of an 253 // algorithm or has conditions. 254 String algorithm; 255 String policy; 256 int space = constraintEntry.indexOf(' '); 257 if (space > 0) { 258 algorithm = AlgorithmDecomposer.hashName( 259 constraintEntry.substring(0, space). 260 toUpperCase(Locale.ENGLISH)); 261 policy = constraintEntry.substring(space + 1); 262 } else { 263 constraintsMap.putIfAbsent( 264 constraintEntry.toUpperCase(Locale.ENGLISH), 265 new HashSet<>()); 266 continue; 267 } 268 269 // Convert constraint conditions into Constraint classes 270 Constraint c = null; 271 Constraint lastConstraint = null; 272 // Allow only one jdkCA entry per constraint entry 273 boolean jdkCALimit = false; 274 275 for (String entry : policy.split("&")) { 276 entry = entry.trim(); 277 278 Matcher matcher = keySizePattern.matcher(entry); 279 if (matcher.matches()) { 280 if (debug != null) { 281 debug.println("Constraints set to keySize: " + 282 entry); 283 } 284 c = new KeySizeConstraint(algorithm, 285 KeySizeConstraint.Operator.of(matcher.group(1)), 286 Integer.parseInt(matcher.group(2))); 287 288 } else if (entry.equalsIgnoreCase("jdkCA")) { 289 if (debug != null) { 290 debug.println("Constraints set to jdkCA."); 291 } 292 if (jdkCALimit) { 293 throw new IllegalArgumentException("Only one " + 294 "jdkCA entry allowed in property. " + 295 "Constraint: " + constraintEntry); 296 } 297 c = new jdkCAConstraint(algorithm); 298 jdkCALimit = true; 299 } 300 // Link multiple conditions for a single constraint 301 // into a linked list. 302 if (lastConstraint == null) { 303 if (!constraintsMap.containsKey(algorithm)) { 304 constraintsMap.putIfAbsent(algorithm, 305 new HashSet<>()); 306 } 307 if (c != null) { 308 constraintsMap.get(algorithm).add(c); 309 } 310 } else { 311 lastConstraint.nextConstraint = c; 312 } 313 lastConstraint = c; 314 } 315 } 316 } 317 318 // Get applicable constraints based off the signature algorithm getConstraints(String algorithm)319 private Set<Constraint> getConstraints(String algorithm) { 320 return constraintsMap.get(algorithm); 321 } 322 323 // Check if KeySizeConstraints permit the specified key permits(Key key)324 public boolean permits(Key key) { 325 Set<Constraint> set = getConstraints(key.getAlgorithm()); 326 if (set == null) { 327 return true; 328 } 329 for (Constraint constraint : set) { 330 if (!constraint.permits(key)) { 331 if (debug != null) { 332 debug.println("keySizeConstraint: failed key " + 333 "constraint check " + KeyUtil.getKeySize(key)); 334 } 335 return false; 336 } 337 } 338 return true; 339 } 340 341 // Check if constraints permit this cert. permits(CertConstraintParameters cp)342 public void permits(CertConstraintParameters cp) 343 throws CertPathValidatorException { 344 X509Certificate cert = cp.getCertificate(); 345 346 if (debug != null) { 347 debug.println("Constraints.permits(): " + cert.getSigAlgName()); 348 } 349 350 // Get all signature algorithms to check for constraints 351 Set<String> algorithms = 352 AlgorithmDecomposer.decomposeOneHash(cert.getSigAlgName()); 353 if (algorithms == null || algorithms.isEmpty()) { 354 return; 355 } 356 357 // Attempt to add the public key algorithm to the set 358 algorithms.add(cert.getPublicKey().getAlgorithm()); 359 360 // Check all applicable constraints 361 for (String algorithm : algorithms) { 362 Set<Constraint> set = getConstraints(algorithm); 363 if (set == null) { 364 continue; 365 } 366 for (Constraint constraint : set) { 367 constraint.permits(cp); 368 } 369 } 370 } 371 } 372 373 // Abstract class for algorithm constraint checking 374 private abstract static class Constraint { 375 String algorithm; 376 Constraint nextConstraint = null; 377 378 // operator 379 enum Operator { 380 EQ, // "==" 381 NE, // "!=" 382 LT, // "<" 383 LE, // "<=" 384 GT, // ">" 385 GE; // ">=" 386 of(String s)387 static Operator of(String s) { 388 switch (s) { 389 case "==": 390 return EQ; 391 case "!=": 392 return NE; 393 case "<": 394 return LT; 395 case "<=": 396 return LE; 397 case ">": 398 return GT; 399 case ">=": 400 return GE; 401 } 402 403 throw new IllegalArgumentException("Error in security " + 404 "property. " + s + " is not a legal Operator"); 405 } 406 } 407 408 /** 409 * Check if an algorithm constraint permit this key to be used. 410 * @param key Public key 411 * @return true if constraints do not match 412 */ permits(Key key)413 public boolean permits(Key key) { 414 return true; 415 } 416 417 /** 418 * Check if an algorithm constraint is permit this certificate to 419 * be used. 420 * @param cp CertificateParameter containing certificate and state info 421 * @return true if constraints do not match 422 */ permits(CertConstraintParameters cp)423 public abstract void permits(CertConstraintParameters cp) 424 throws CertPathValidatorException; 425 } 426 427 /* 428 * This class contains constraints dealing with the certificate chain 429 * of the certificate. 430 */ 431 private static class jdkCAConstraint extends Constraint { jdkCAConstraint(String algo)432 jdkCAConstraint(String algo) { 433 algorithm = algo; 434 } 435 436 /* 437 * Check if each constraint fails and check if there is a linked 438 * constraint Any permitted constraint will exit the linked list 439 * to allow the operation. 440 */ permits(CertConstraintParameters cp)441 public void permits(CertConstraintParameters cp) 442 throws CertPathValidatorException { 443 if (debug != null) { 444 debug.println("jdkCAConstraints.permits(): " + algorithm); 445 } 446 447 // Return false if the chain has a trust anchor in cacerts 448 if (cp.isTrustedMatch()) { 449 if (nextConstraint != null) { 450 nextConstraint.permits(cp); 451 return; 452 } 453 throw new CertPathValidatorException( 454 "Algorithm constraints check failed on certificate " + 455 "anchor limits", 456 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 457 } 458 } 459 } 460 461 462 /* 463 * This class contains constraints dealing with the key size 464 * support limits per algorithm. e.g. "keySize <= 1024" 465 */ 466 private static class KeySizeConstraint extends Constraint { 467 468 private int minSize; // the minimal available key size 469 private int maxSize; // the maximal available key size 470 private int prohibitedSize = -1; // unavailable key sizes 471 KeySizeConstraint(String algo, Operator operator, int length)472 public KeySizeConstraint(String algo, Operator operator, int length) { 473 algorithm = algo; 474 switch (operator) { 475 case EQ: // an unavailable key size 476 this.minSize = 0; 477 this.maxSize = Integer.MAX_VALUE; 478 prohibitedSize = length; 479 break; 480 case NE: 481 this.minSize = length; 482 this.maxSize = length; 483 break; 484 case LT: 485 this.minSize = length; 486 this.maxSize = Integer.MAX_VALUE; 487 break; 488 case LE: 489 this.minSize = length + 1; 490 this.maxSize = Integer.MAX_VALUE; 491 break; 492 case GT: 493 this.minSize = 0; 494 this.maxSize = length; 495 break; 496 case GE: 497 this.minSize = 0; 498 this.maxSize = length > 1 ? (length - 1) : 0; 499 break; 500 default: 501 // unlikely to happen 502 this.minSize = Integer.MAX_VALUE; 503 this.maxSize = -1; 504 } 505 } 506 507 /* 508 * If we are passed a certificate, extract the public key and use it. 509 * 510 * Check if each constraint fails and check if there is a linked 511 * constraint Any permitted constraint will exit the linked list 512 * to allow the operation. 513 */ permits(CertConstraintParameters cp)514 public void permits(CertConstraintParameters cp) 515 throws CertPathValidatorException { 516 if (!permitsImpl(cp.getCertificate().getPublicKey())) { 517 if (nextConstraint != null) { 518 nextConstraint.permits(cp); 519 return; 520 } 521 throw new CertPathValidatorException( 522 "Algorithm constraints check failed on keysize limits", 523 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 524 } 525 } 526 527 528 // Check if key constraint disable the specified key 529 // Uses old style permit() permits(Key key)530 public boolean permits(Key key) { 531 // If we recursively find a constraint that permits us to use 532 // this key, return true and skip any other constraint checks. 533 if (nextConstraint != null && nextConstraint.permits(key)) { 534 return true; 535 } 536 if (debug != null) { 537 debug.println("KeySizeConstraints.permits(): " + algorithm); 538 } 539 540 return permitsImpl(key); 541 } 542 permitsImpl(Key key)543 private boolean permitsImpl(Key key) { 544 // Verify this constraint is for this public key algorithm 545 if (algorithm.compareToIgnoreCase(key.getAlgorithm()) != 0) { 546 return true; 547 } 548 549 int size = KeyUtil.getKeySize(key); 550 if (size == 0) { 551 return false; // we don't allow any key of size 0. 552 } else if (size > 0) { 553 return !((size < minSize) || (size > maxSize) || 554 (prohibitedSize == size)); 555 } // Otherwise, the key size is not accessible. Conservatively, 556 // please don't disable such keys. 557 558 return true; 559 } 560 } 561 } 562