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