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 throw new IllegalArgumentException( 190 "The trust anchor cannot be null"); 191 } 192 193 if (anchor.getTrustedCert() != null) { 194 this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); 195 // Check for anchor certificate restrictions 196 trustedMatch = checkFingerprint(anchor.getTrustedCert()); 197 if (trustedMatch && debug != null) { 198 debug.println("trustedMatch = true"); 199 } 200 } else { 201 this.trustedPubKey = anchor.getCAPublicKey(); 202 } 203 204 this.prevPubKey = trustedPubKey; 205 this.constraints = constraints; 206 this.pkixdate = pkixdate; 207 this.jarTimestamp = null; 208 } 209 210 /** 211 * Create a new {@code AlgorithmChecker} with the 212 * given {@code TrustAnchor} and {@code PKIXParameter} date. 213 * 214 * @param anchor the trust anchor selected to validate the target 215 * certificate 216 * @param pkixdate Date the constraints are checked against. The value is 217 * either the PKIXParameter date or null for the current date. 218 * 219 * @throws IllegalArgumentException if the {@code anchor} is null 220 */ 221 public AlgorithmChecker(TrustAnchor anchor, Date pkixdate) { 222 this(anchor, certPathDefaultConstraints, pkixdate); 223 } 224 225 // Check this 'cert' for restrictions in the AnchorCertificates 226 // trusted certificates list 227 private static boolean checkFingerprint(X509Certificate cert) { 228 if (!publicCALimits) { 229 return false; 230 } 231 232 if (debug != null) { 233 debug.println("AlgorithmChecker.contains: " + cert.getSigAlgName()); 234 } 235 return AnchorCertificates.contains(cert); 236 } 237 238 Timestamp getJarTimestamp() { 239 return jarTimestamp; 240 } 241 242 @Override 243 public void init(boolean forward) throws CertPathValidatorException { 244 // Note that this class does not support forward mode. 245 if (!forward) { 246 if (trustedPubKey != null) { 247 prevPubKey = trustedPubKey; 248 } else { 249 prevPubKey = null; 250 } 251 } else { 252 throw new 253 CertPathValidatorException("forward checking not supported"); 254 } 255 } 256 257 @Override 258 public boolean isForwardCheckingSupported() { 259 // Note that as this class does not support forward mode, the method 260 // will always returns false. 261 return false; 262 } 263 264 @Override 265 public Set<String> getSupportedExtensions() { 266 return null; 267 } 268 269 @Override 270 public void check(Certificate cert, 271 Collection<String> unresolvedCritExts) 272 throws CertPathValidatorException { 273 274 if (!(cert instanceof X509Certificate) || constraints == null) { 275 // ignore the check for non-x.509 certificate or null constraints 276 return; 277 } 278 279 // check the key usage and key size 280 boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); 281 if (keyUsage != null && keyUsage.length < 9) { 282 throw new CertPathValidatorException( 283 "incorrect KeyUsage extension", 284 null, null, -1, PKIXReason.INVALID_KEY_USAGE); 285 } 286 287 // Assume all key usage bits are set if key usage is not present 288 Set<CryptoPrimitive> primitives = KU_PRIMITIVE_SET; 289 290 if (keyUsage != null) { 291 primitives = EnumSet.noneOf(CryptoPrimitive.class); 292 293 if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { 294 // keyUsage[0]: KeyUsage.digitalSignature 295 // keyUsage[1]: KeyUsage.nonRepudiation 296 // keyUsage[5]: KeyUsage.keyCertSign 297 // keyUsage[6]: KeyUsage.cRLSign 298 primitives.add(CryptoPrimitive.SIGNATURE); 299 } 300 301 if (keyUsage[2]) { // KeyUsage.keyEncipherment 302 primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); 303 } 304 305 if (keyUsage[3]) { // KeyUsage.dataEncipherment 306 primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); 307 } 308 309 if (keyUsage[4]) { // KeyUsage.keyAgreement 310 primitives.add(CryptoPrimitive.KEY_AGREEMENT); 311 } 312 313 // KeyUsage.encipherOnly and KeyUsage.decipherOnly are 314 // undefined in the absence of the keyAgreement bit. 315 316 if (primitives.isEmpty()) { 317 throw new CertPathValidatorException( 318 "incorrect KeyUsage extension bits", 319 null, null, -1, PKIXReason.INVALID_KEY_USAGE); 320 } 321 } 322 323 PublicKey currPubKey = cert.getPublicKey(); 324 325 // Check against DisabledAlgorithmConstraints certpath constraints. 326 // permits() will throw exception on failure. 327 certPathDefaultConstraints.permits(primitives, 328 new CertConstraintParameters((X509Certificate)cert, 329 trustedMatch, pkixdate, jarTimestamp)); 330 // If there is no previous key, set one and exit 331 if (prevPubKey == null) { 332 prevPubKey = currPubKey; 333 return; 334 } 335 336 X509CertImpl x509Cert; 337 AlgorithmId algorithmId; 338 try { 339 x509Cert = X509CertImpl.toImpl((X509Certificate)cert); 340 algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); 341 } catch (CertificateException ce) { 342 throw new CertPathValidatorException(ce); 343 } 344 345 AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); 346 String currSigAlg = x509Cert.getSigAlgName(); 347 348 // If 'constraints' is not of DisabledAlgorithmConstraints, check all 349 // everything individually 350 if (!(constraints instanceof DisabledAlgorithmConstraints)) { 351 // Check the current signature algorithm 352 if (!constraints.permits( 353 SIGNATURE_PRIMITIVE_SET, 354 currSigAlg, currSigAlgParams)) { 355 throw new CertPathValidatorException( 356 "Algorithm constraints check failed on signature " + 357 "algorithm: " + currSigAlg, null, null, -1, 358 BasicReason.ALGORITHM_CONSTRAINED); 359 } 360 361 if (!constraints.permits(primitives, currPubKey)) { 362 throw new CertPathValidatorException( 363 "Algorithm constraints check failed on keysize: " + 364 sun.security.util.KeyUtil.getKeySize(currPubKey), 365 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 366 } 367 } 368 369 // Check with previous cert for signature algorithm and public key 370 if (prevPubKey != null) { 371 if (!constraints.permits( 372 SIGNATURE_PRIMITIVE_SET, 373 currSigAlg, prevPubKey, currSigAlgParams)) { 374 throw new CertPathValidatorException( 375 "Algorithm constraints check failed on " + 376 "signature algorithm: " + currSigAlg, 377 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 378 } 379 380 // Inherit key parameters from previous key 381 if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { 382 // Inherit DSA parameters from previous key 383 if (!(prevPubKey instanceof DSAPublicKey)) { 384 throw new CertPathValidatorException("Input key is not " + 385 "of a appropriate type for inheriting parameters"); 386 } 387 388 DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); 389 if (params == null) { 390 throw new CertPathValidatorException( 391 "Key parameters missing from public key."); 392 } 393 394 try { 395 BigInteger y = ((DSAPublicKey)currPubKey).getY(); 396 KeyFactory kf = KeyFactory.getInstance("DSA"); 397 DSAPublicKeySpec ks = new DSAPublicKeySpec(y, 398 params.getP(), 399 params.getQ(), 400 params.getG()); 401 currPubKey = kf.generatePublic(ks); 402 } catch (GeneralSecurityException e) { 403 throw new CertPathValidatorException("Unable to generate " + 404 "key with inherited parameters: " + e.getMessage(), e); 405 } 406 } 407 } 408 409 // reset the previous public key 410 prevPubKey = currPubKey; 411 412 // check the extended key usage, ignore the check now 413 // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage(); 414 415 // DO NOT remove any unresolved critical extensions 416 } 417 418 /** 419 * Try to set the trust anchor of the checker. 420 * <p> 421 * If there is no trust anchor specified and the checker has not started, 422 * set the trust anchor. 423 * 424 * @param anchor the trust anchor selected to validate the target 425 * certificate 426 */ 427 void trySetTrustAnchor(TrustAnchor anchor) { 428 // Don't bother if the check has started or trust anchor has already 429 // specified. 430 if (prevPubKey == null) { 431 if (anchor == null) { 432 throw new IllegalArgumentException( 433 "The trust anchor cannot be null"); 434 } 435 436 // Don't bother to change the trustedPubKey. 437 if (anchor.getTrustedCert() != null) { 438 prevPubKey = anchor.getTrustedCert().getPublicKey(); 439 // Check for anchor certificate restrictions 440 trustedMatch = checkFingerprint(anchor.getTrustedCert()); 441 if (trustedMatch && debug != null) { 442 debug.println("trustedMatch = true"); 443 } 444 } else { 445 prevPubKey = anchor.getCAPublicKey(); 446 } 447 } 448 } 449 450 /** 451 * Check the signature algorithm with the specified public key. 452 * 453 * @param key the public key to verify the CRL signature 454 * @param crl the target CRL 455 */ 456 static void check(PublicKey key, X509CRL crl) 457 throws CertPathValidatorException { 458 459 X509CRLImpl x509CRLImpl = null; 460 try { 461 x509CRLImpl = X509CRLImpl.toImpl(crl); 462 } catch (CRLException ce) { 463 throw new CertPathValidatorException(ce); 464 } 465 466 AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); 467 check(key, algorithmId); 468 } 469 470 /** 471 * Check the signature algorithm with the specified public key. 472 * 473 * @param key the public key to verify the CRL signature 474 * @param algorithmId signature algorithm Algorithm ID 475 */ 476 static void check(PublicKey key, AlgorithmId algorithmId) 477 throws CertPathValidatorException { 478 String sigAlgName = algorithmId.getName(); 479 AlgorithmParameters sigAlgParams = algorithmId.getParameters(); 480 481 if (!certPathDefaultConstraints.permits( 482 SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { 483 throw new CertPathValidatorException( 484 "Algorithm constraints check failed on signature algorithm: " + 485 sigAlgName + " is disabled", 486 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 487 } 488 } 489 490 } 491