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