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