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 public final 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 static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = 80 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 81 82 private static final 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 static final 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 */ 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 */ 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 */ 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 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 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 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 198 public Set<String> getSupportedExtensions() { 199 return null; 200 } 201 202 @Override 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 // Check against DisabledAlgorithmConstraints certpath constraints. 259 // permits() will throw exception on failure. 260 certPathDefaultConstraints.permits(primitives, 261 new CertConstraintParameters((X509Certificate)cert, 262 trustedMatch)); 263 // new CertConstraintParameters(x509Cert, trustedMatch)); 264 // If there is no previous key, set one and exit 265 if (prevPubKey == null) { 266 prevPubKey = currPubKey; 267 return; 268 } 269 270 X509CertImpl x509Cert; 271 AlgorithmId algorithmId; 272 try { 273 x509Cert = X509CertImpl.toImpl((X509Certificate)cert); 274 algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); 275 } catch (CertificateException ce) { 276 throw new CertPathValidatorException(ce); 277 } 278 279 AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); 280 String currSigAlg = x509Cert.getSigAlgName(); 281 282 // If 'constraints' is not of DisabledAlgorithmConstraints, check all 283 // everything individually 284 if (!(constraints instanceof DisabledAlgorithmConstraints)) { 285 // Check the current signature algorithm 286 if (!constraints.permits( 287 SIGNATURE_PRIMITIVE_SET, 288 currSigAlg, currSigAlgParams)) { 289 throw new CertPathValidatorException( 290 "Algorithm constraints check failed on signature " + 291 "algorithm: " + currSigAlg, null, null, -1, 292 BasicReason.ALGORITHM_CONSTRAINED); 293 } 294 295 if (!constraints.permits(primitives, currPubKey)) { 296 throw new CertPathValidatorException( 297 "Algorithm constraints check failed on keysize: " + 298 sun.security.util.KeyUtil.getKeySize(currPubKey), 299 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 300 } 301 } 302 303 // Check with previous cert for signature algorithm and public key 304 if (prevPubKey != null) { 305 if (!constraints.permits( 306 SIGNATURE_PRIMITIVE_SET, 307 currSigAlg, prevPubKey, currSigAlgParams)) { 308 throw new CertPathValidatorException( 309 "Algorithm constraints check failed on " + 310 "signature algorithm: " + currSigAlg, 311 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 312 } 313 314 // Inherit key parameters from previous key 315 if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { 316 // Inherit DSA parameters from previous key 317 if (!(prevPubKey instanceof DSAPublicKey)) { 318 throw new CertPathValidatorException("Input key is not " + 319 "of a appropriate type for inheriting parameters"); 320 } 321 322 DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); 323 if (params == null) { 324 throw new CertPathValidatorException( 325 "Key parameters missing from public key."); 326 } 327 328 try { 329 BigInteger y = ((DSAPublicKey)currPubKey).getY(); 330 KeyFactory kf = KeyFactory.getInstance("DSA"); 331 DSAPublicKeySpec ks = new DSAPublicKeySpec(y, 332 params.getP(), 333 params.getQ(), 334 params.getG()); 335 currPubKey = kf.generatePublic(ks); 336 } catch (GeneralSecurityException e) { 337 throw new CertPathValidatorException("Unable to generate " + 338 "key with inherited parameters: " + e.getMessage(), e); 339 } 340 } 341 } 342 343 // reset the previous public key 344 prevPubKey = currPubKey; 345 346 // check the extended key usage, ignore the check now 347 // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage(); 348 349 // DO NOT remove any unresolved critical extensions 350 } 351 352 /** 353 * Try to set the trust anchor of the checker. 354 * <p> 355 * If there is no trust anchor specified and the checker has not started, 356 * set the trust anchor. 357 * 358 * @param anchor the trust anchor selected to validate the target 359 * certificate 360 */ 361 void trySetTrustAnchor(TrustAnchor anchor) { 362 // Don't bother if the check has started or trust anchor has already 363 // specified. 364 if (prevPubKey == null) { 365 if (anchor == null) { 366 throw new IllegalArgumentException( 367 "The trust anchor cannot be null"); 368 } 369 370 // Don't bother to change the trustedPubKey. 371 if (anchor.getTrustedCert() != null) { 372 prevPubKey = anchor.getTrustedCert().getPublicKey(); 373 // Check for anchor certificate restrictions 374 trustedMatch = checkFingerprint(anchor.getTrustedCert()); 375 if (trustedMatch && debug != null) { 376 debug.println("trustedMatch = true"); 377 } 378 } else { 379 prevPubKey = anchor.getCAPublicKey(); 380 } 381 } 382 } 383 384 /** 385 * Check the signature algorithm with the specified public key. 386 * 387 * @param key the public key to verify the CRL signature 388 * @param crl the target CRL 389 */ 390 static void check(PublicKey key, X509CRL crl) 391 throws CertPathValidatorException { 392 393 X509CRLImpl x509CRLImpl = null; 394 try { 395 x509CRLImpl = X509CRLImpl.toImpl(crl); 396 } catch (CRLException ce) { 397 throw new CertPathValidatorException(ce); 398 } 399 400 AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); 401 check(key, algorithmId); 402 } 403 404 /** 405 * Check the signature algorithm with the specified public key. 406 * 407 * @param key the public key to verify the CRL signature 408 * @param crl the target CRL 409 */ 410 static void check(PublicKey key, AlgorithmId algorithmId) 411 throws CertPathValidatorException { 412 String sigAlgName = algorithmId.getName(); 413 AlgorithmParameters sigAlgParams = algorithmId.getParameters(); 414 415 if (!certPathDefaultConstraints.permits( 416 SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { 417 throw new CertPathValidatorException( 418 "Algorithm constraints check failed on signature algorithm: " + 419 sigAlgName + " is disabled", 420 null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); 421 } 422 } 423 424 } 425