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