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