1 /* 2 * Copyright (c) 2009, 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.provider.certpath; 27 28 import java.security.AlgorithmConstraints; 29 import java.security.CryptoPrimitive; 30 import java.security.Timestamp; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.Date; 34 import java.util.Set; 35 import java.util.EnumSet; 36 import java.math.BigInteger; 37 import java.security.PublicKey; 38 import java.security.KeyFactory; 39 import java.security.AlgorithmParameters; 40 import java.security.GeneralSecurityException; 41 import java.security.cert.Certificate; 42 import java.security.cert.X509CRL; 43 import java.security.cert.X509Certificate; 44 import java.security.cert.PKIXCertPathChecker; 45 import java.security.cert.TrustAnchor; 46 import java.security.cert.CRLException; 47 import java.security.cert.CertificateException; 48 import java.security.cert.CertPathValidatorException; 49 import java.security.cert.CertPathValidatorException.BasicReason; 50 import java.security.cert.PKIXReason; 51 import java.security.interfaces.DSAParams; 52 import java.security.interfaces.DSAPublicKey; 53 import java.security.spec.DSAPublicKeySpec; 54 55 import sun.security.util.AnchorCertificates; 56 import sun.security.util.CertConstraintParameters; 57 import sun.security.util.Debug; 58 import sun.security.util.DisabledAlgorithmConstraints; 59 import sun.security.x509.X509CertImpl; 60 import sun.security.x509.X509CRLImpl; 61 import sun.security.x509.AlgorithmId; 62 63 /** 64 * A {@code PKIXCertPathChecker} implementation to check whether a 65 * specified certificate contains the required algorithm constraints. 66 * <p> 67 * Certificate fields such as the subject public key, the signature 68 * algorithm, key usage, extended key usage, etc. need to conform to 69 * the specified algorithm constraints. 70 * 71 * @see PKIXCertPathChecker 72 * @see PKIXParameters 73 */ 74 public final class AlgorithmChecker extends PKIXCertPathChecker { 75 private static final Debug debug = Debug.getInstance("certpath"); 76 77 private final AlgorithmConstraints constraints; 78 private final PublicKey trustedPubKey; 79 private final Date pkixdate; 80 private PublicKey prevPubKey; 81 private final Timestamp jarTimestamp; 82 83 private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = 84 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 85 86 private static final Set<CryptoPrimitive> KU_PRIMITIVE_SET = 87 Collections.unmodifiableSet(EnumSet.of( 88 CryptoPrimitive.SIGNATURE, 89 CryptoPrimitive.KEY_ENCAPSULATION, 90 CryptoPrimitive.PUBLIC_KEY_ENCRYPTION, 91 CryptoPrimitive.KEY_AGREEMENT)); 92 93 private static final DisabledAlgorithmConstraints 94 certPathDefaultConstraints = new DisabledAlgorithmConstraints( 95 DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); 96 97 // If there is no "cacerts" keyword, then disable anchor checking 98 private static final boolean publicCALimits = 99 certPathDefaultConstraints.checkProperty("jdkCA"); 100 101 // If anchor checking enabled, this will be true if the trust anchor 102 // has a match in the cacerts file 103 private boolean trustedMatch = false; 104 105 /** 106 * Create a new {@code AlgorithmChecker} with the algorithm 107 * constraints specified in security property 108 * "jdk.certpath.disabledAlgorithms". 109 * 110 * @param anchor the trust anchor selected to validate the target 111 * certificate 112 */ 113 public AlgorithmChecker(TrustAnchor anchor) { 114 this(anchor, certPathDefaultConstraints, null); 115 } 116 117 /** 118 * Create a new {@code AlgorithmChecker} with the 119 * given {@code TrustAnchor} and {@code AlgorithmConstraints}. 120 * 121 * @param anchor the trust anchor selected to validate the target 122 * certificate 123 * @param constraints the algorithm constraints (or null) 124 * 125 * @throws IllegalArgumentException if the {@code anchor} is null 126 */ 127 public AlgorithmChecker(TrustAnchor anchor, 128 AlgorithmConstraints constraints) { 129 this(anchor, constraints, null); 130 } 131 132 /** 133 * Create a new {@code AlgorithmChecker} with the 134 * given {@code AlgorithmConstraints}. 135 * <p> 136 * Note that this constructor will be used to check a certification 137 * path where the trust anchor is unknown, or a certificate list which may 138 * contain the trust anchor. This constructor is used by SunJSSE. 139 * 140 * @param constraints the algorithm constraints (or null) 141 */ 142 public AlgorithmChecker(AlgorithmConstraints constraints) { 143 this.prevPubKey = null; 144 this.trustedPubKey = null; 145 this.constraints = constraints; 146 this.pkixdate = null; 147 this.jarTimestamp = null; 148 } 149 150 /** 151 * Create a new {@code AlgorithmChecker} with the given 152 * {@code Timestamp}. 153 * <p> 154 * Note that this constructor will be used to check a certification 155 * path for signed JAR files that are timestamped. 156 * 157 * @param jarTimestamp Timestamp passed for JAR timestamp constraint 158 * checking. Set to null if not applicable. 159 */ 160 public AlgorithmChecker(Timestamp jarTimestamp) { 161 this.prevPubKey = null; 162 this.trustedPubKey = null; 163 this.constraints = certPathDefaultConstraints; 164 if (jarTimestamp == null) { 165 throw new IllegalArgumentException( 166 "Timestamp cannot be null"); 167 } 168 this.pkixdate = jarTimestamp.getTimestamp(); 169 this.jarTimestamp = jarTimestamp; 170 } 171 172 /** 173 * Create a new {@code AlgorithmChecker} with the 174 * given {@code TrustAnchor} and {@code AlgorithmConstraints}. 175 * 176 * @param anchor the trust anchor selected to validate the target 177 * certificate 178 * @param constraints the algorithm constraints (or null) 179 * @param pkixdate Date the constraints are checked against. The value is 180 * either the PKIXParameter date or null for the current date. 181 * 182 * @throws IllegalArgumentException if the {@code anchor} is null 183 */ 184 public AlgorithmChecker(TrustAnchor anchor, 185 AlgorithmConstraints constraints, 186 Date pkixdate) { 187 188 if (anchor != null) { 189 if (anchor.getTrustedCert() != null) { 190 this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); 191 // Check for anchor certificate restrictions 192 trustedMatch = checkFingerprint(anchor.getTrustedCert()); 193 if (trustedMatch && debug != null) { 194 debug.println("trustedMatch = true"); 195 } 196 } else { 197 this.trustedPubKey = anchor.getCAPublicKey(); 198 } 199 } else { 200 this.trustedPubKey = null; 201 if (debug != null) { 202 debug.println("TrustAnchor is null, trustedMatch is false."); 203 } 204 } 205 206 this.prevPubKey = trustedPubKey; 207 this.constraints = constraints; 208 this.pkixdate = pkixdate; 209 this.jarTimestamp = null; 210 } 211 212 /** 213 * Create a new {@code AlgorithmChecker} with the 214 * given {@code TrustAnchor} and {@code PKIXParameter} date. 215 * 216 * @param anchor the trust anchor selected to validate the target 217 * certificate 218 * @param pkixdate Date the constraints are checked against. The value is 219 * either the PKIXParameter date or null for the current date. 220 * 221 * @throws IllegalArgumentException if the {@code anchor} is null 222 */ 223 public AlgorithmChecker(TrustAnchor anchor, Date pkixdate) { 224 this(anchor, certPathDefaultConstraints, pkixdate); 225 } 226 227 // Check this 'cert' for restrictions in the AnchorCertificates 228 // trusted certificates list 229 private static boolean checkFingerprint(X509Certificate cert) { 230 if (!publicCALimits) { 231 return false; 232 } 233 234 if (debug != null) { 235 debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName()); 236 } 237 return AnchorCertificates.contains(cert); 238 } 239 240 Timestamp getJarTimestamp() { 241 return jarTimestamp; 242 } 243 244 @Override 245 public void init(boolean forward) throws CertPathValidatorException { 246 // Note that this class does not support forward mode. 247 if (!forward) { 248 if (trustedPubKey != null) { 249 prevPubKey = trustedPubKey; 250 } else { 251 prevPubKey = null; 252 } 253 } else { 254 throw new 255 CertPathValidatorException("forward checking not supported"); 256 } 257 } 258 259 @Override 260 public boolean isForwardCheckingSupported() { 261 // Note that as this class does not support forward mode, the method 262 // will always returns false. 263 return false; 264 } 265 266 @Override 267 public Set<String> getSupportedExtensions() { 268 return null; 269 } 270 271 @Override 272 public void check(Certificate cert, 273 Collection<String> unresolvedCritExts) 274 throws CertPathValidatorException { 275 276 if (!(cert instanceof X509Certificate) || constraints == null) { 277 // ignore the check for non-x.509 certificate or null constraints 278 return; 279 } 280 281 // check the key usage and key size 282 boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); 283 if (keyUsage != null && keyUsage.length < 9) { 284 throw new CertPathValidatorException( 285 "incorrect KeyUsage extension", 286 null, null, -1, PKIXReason.INVALID_KEY_USAGE); 287 } 288 289 // Assume all key usage bits are set if key usage is not present 290 Set<CryptoPrimitive> primitives = KU_PRIMITIVE_SET; 291 292 if (keyUsage != null) { 293 primitives = EnumSet.noneOf(CryptoPrimitive.class); 294 295 if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { 296 // keyUsage[0]: KeyUsage.digitalSignature 297 // keyUsage[1]: KeyUsage.nonRepudiation 298 // keyUsage[5]: KeyUsage.keyCertSign 299 // keyUsage[6]: KeyUsage.cRLSign 300 primitives.add(CryptoPrimitive.SIGNATURE); 301 } 302 303 if (keyUsage[2]) { // KeyUsage.keyEncipherment 304 primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); 305 } 306 307 if (keyUsage[3]) { // KeyUsage.dataEncipherment 308 primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); 309 } 310 311 if (keyUsage[4]) { // KeyUsage.keyAgreement 312 primitives.add(CryptoPrimitive.KEY_AGREEMENT); 313 } 314 315 // KeyUsage.encipherOnly and KeyUsage.decipherOnly are 316 // undefined in the absence of the keyAgreement bit. 317 318 if (primitives.isEmpty()) { 319 throw new CertPathValidatorException( 320 "incorrect KeyUsage extension bits", 321 null, null, -1, PKIXReason.INVALID_KEY_USAGE); 322 } 323 } 324 325 PublicKey currPubKey = cert.getPublicKey(); 326 327 // Check against DisabledAlgorithmConstraints certpath constraints. 328 // permits() will throw exception on failure. 329 certPathDefaultConstraints.permits(primitives, 330 new CertConstraintParameters((X509Certificate)cert, 331 trustedMatch, pkixdate, jarTimestamp)); 332 // If there is no previous key, set one and exit 333 if (prevPubKey == null) { 334 prevPubKey = currPubKey; 335 return; 336 } 337 338 X509CertImpl x509Cert; 339 AlgorithmId algorithmId; 340 try { 341 x509Cert = X509CertImpl.toImpl((X509Certificate)cert); 342 algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); 343 } catch (CertificateException ce) { 344 throw new CertPathValidatorException(ce); 345 } 346 347 AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); 348 String currSigAlg = x509Cert.getSigAlgName(); 349 350 // If 'constraints' is not of DisabledAlgorithmConstraints, check all 351 // everything individually 352 if (!(constraints instanceof DisabledAlgorithmConstraints)) { 353 // Check the current signature algorithm 354 if (!constraints.permits( 355 SIGNATURE_PRIMITIVE_SET, 356 currSigAlg, currSigAlgParams)) { 357 throw new CertPathValidatorException( 358 "Algorithm constraints check failed on signature " + 359 "algorithm: " + currSigAlg, null, null, -1, 360 BasicReason.ALGORITHM_CONSTRAINED); 361 } 362 363 if (!constraints.permits(primitives, currPubKey)) { 364 throw new CertPathValidatorException( 365 "Algorithm constraints check failed on keysize: " + 366 sun.security.util.KeyUtil.getKeySize(currPubKey), 367 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 368 } 369 } 370 371 // Check with previous cert for signature algorithm and public key 372 if (prevPubKey != null) { 373 if (!constraints.permits( 374 SIGNATURE_PRIMITIVE_SET, 375 currSigAlg, prevPubKey, currSigAlgParams)) { 376 throw new CertPathValidatorException( 377 "Algorithm constraints check failed on " + 378 "signature algorithm: " + currSigAlg, 379 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 380 } 381 382 // Inherit key parameters from previous key 383 if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { 384 // Inherit DSA parameters from previous key 385 if (!(prevPubKey instanceof DSAPublicKey)) { 386 throw new CertPathValidatorException("Input key is not " + 387 "of a appropriate type for inheriting parameters"); 388 } 389 390 DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); 391 if (params == null) { 392 throw new CertPathValidatorException( 393 "Key parameters missing from public key."); 394 } 395 396 try { 397 BigInteger y = ((DSAPublicKey)currPubKey).getY(); 398 KeyFactory kf = KeyFactory.getInstance("DSA"); 399 DSAPublicKeySpec ks = new DSAPublicKeySpec(y, 400 params.getP(), 401 params.getQ(), 402 params.getG()); 403 currPubKey = kf.generatePublic(ks); 404 } catch (GeneralSecurityException e) { 405 throw new CertPathValidatorException("Unable to generate " + 406 "key with inherited parameters: " + e.getMessage(), e); 407 } 408 } 409 } 410 411 // reset the previous public key 412 prevPubKey = currPubKey; 413 414 // check the extended key usage, ignore the check now 415 // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage(); 416 417 // DO NOT remove any unresolved critical extensions 418 } 419 420 /** 421 * Try to set the trust anchor of the checker. 422 * <p> 423 * If there is no trust anchor specified and the checker has not started, 424 * set the trust anchor. 425 * 426 * @param anchor the trust anchor selected to validate the target 427 * certificate 428 */ 429 void trySetTrustAnchor(TrustAnchor anchor) { 430 // Don't bother if the check has started or trust anchor has already 431 // specified. 432 if (prevPubKey == null) { 433 if (anchor == null) { 434 throw new IllegalArgumentException( 435 "The trust anchor cannot be null"); 436 } 437 438 // Don't bother to change the trustedPubKey. 439 if (anchor.getTrustedCert() != null) { 440 prevPubKey = anchor.getTrustedCert().getPublicKey(); 441 // Check for anchor certificate restrictions 442 trustedMatch = checkFingerprint(anchor.getTrustedCert()); 443 if (trustedMatch && debug != null) { 444 debug.println("trustedMatch = true"); 445 } 446 } else { 447 prevPubKey = anchor.getCAPublicKey(); 448 } 449 } 450 } 451 452 /** 453 * Check the signature algorithm with the specified public key. 454 * 455 * @param key the public key to verify the CRL signature 456 * @param crl the target CRL 457 */ 458 static void check(PublicKey key, X509CRL crl) 459 throws CertPathValidatorException { 460 461 X509CRLImpl x509CRLImpl = null; 462 try { 463 x509CRLImpl = X509CRLImpl.toImpl(crl); 464 } catch (CRLException ce) { 465 throw new CertPathValidatorException(ce); 466 } 467 468 AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); 469 check(key, algorithmId); 470 } 471 472 /** 473 * Check the signature algorithm with the specified public key. 474 * 475 * @param key the public key to verify the CRL signature 476 * @param algorithmId signature algorithm Algorithm ID 477 */ 478 static void check(PublicKey key, AlgorithmId algorithmId) 479 throws CertPathValidatorException { 480 String sigAlgName = algorithmId.getName(); 481 AlgorithmParameters sigAlgParams = algorithmId.getParameters(); 482 483 if (!certPathDefaultConstraints.permits( 484 SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { 485 throw new CertPathValidatorException( 486 "Algorithm constraints check failed on signature algorithm: " + 487 sigAlgName + " is disabled", 488 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 489 } 490 } 491 492 } 493