1 /* 2 * Copyright (c) 2000, 2015, 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.io.IOException; 29 import java.security.AccessController; 30 import java.security.GeneralSecurityException; 31 import java.security.cert.*; 32 import java.util.*; 33 34 import sun.security.action.GetBooleanAction; 35 import sun.security.provider.certpath.PKIX.BuilderParams; 36 import sun.security.util.Debug; 37 import sun.security.x509.GeneralNames; 38 import sun.security.x509.GeneralNameInterface; 39 import sun.security.x509.GeneralSubtrees; 40 import sun.security.x509.NameConstraintsExtension; 41 import sun.security.x509.SubjectAlternativeNameExtension; 42 import sun.security.x509.X500Name; 43 import sun.security.x509.X509CertImpl; 44 45 /** 46 * Abstract class representing a builder, which is able to retrieve 47 * matching certificates and is able to verify a particular certificate. 48 * 49 * @since 1.4 50 * @author Sean Mullan 51 * @author Yassir Elley 52 */ 53 54 public abstract class Builder { 55 56 private static final Debug debug = Debug.getInstance("certpath"); 57 private Set<String> matchingPolicies; 58 final BuilderParams buildParams; 59 final X509CertSelector targetCertConstraints; 60 61 /** 62 * Flag indicating whether support for the caIssuers field of the 63 * Authority Information Access extension shall be enabled. Currently 64 * disabled by default for compatibility reasons. 65 */ 66 final static boolean USE_AIA = AccessController.doPrivileged 67 (new GetBooleanAction("com.sun.security.enableAIAcaIssuers")); 68 69 /** 70 * Initialize the builder with the input parameters. 71 * 72 * @param params the parameter set used to build a certification path 73 */ Builder(BuilderParams buildParams)74 Builder(BuilderParams buildParams) { 75 this.buildParams = buildParams; 76 this.targetCertConstraints = 77 (X509CertSelector)buildParams.targetCertConstraints(); 78 } 79 80 /** 81 * Retrieves certificates from the list of certStores using the buildParams 82 * and the currentState as a filter 83 * 84 * @param currentState the current State 85 * @param certStores list of CertStores 86 */ getMatchingCerts(State currentState, List<CertStore> certStores)87 abstract Collection<X509Certificate> getMatchingCerts 88 (State currentState, List<CertStore> certStores) 89 throws CertStoreException, CertificateException, IOException; 90 91 /** 92 * Verifies the cert against the currentState, using the certPathList 93 * generated thus far to help with loop detection 94 * 95 * @param cert the certificate to be verified 96 * @param currentState the current state against which the cert is verified 97 * @param certPathList the certPathList generated thus far 98 */ verifyCert(X509Certificate cert, State currentState, List<X509Certificate> certPathList)99 abstract void verifyCert(X509Certificate cert, State currentState, 100 List<X509Certificate> certPathList) 101 throws GeneralSecurityException; 102 103 /** 104 * Verifies whether the input certificate completes the path. 105 * When building in the forward direction, a trust anchor will 106 * complete the path. 107 * 108 * @param cert the certificate to test 109 * @return a boolean value indicating whether the cert completes the path. 110 */ isPathCompleted(X509Certificate cert)111 abstract boolean isPathCompleted(X509Certificate cert); 112 113 /** 114 * Adds the certificate to the certPathList 115 * 116 * @param cert the certificate to be added 117 * @param certPathList the certification path list 118 */ addCertToPath(X509Certificate cert, LinkedList<X509Certificate> certPathList)119 abstract void addCertToPath(X509Certificate cert, 120 LinkedList<X509Certificate> certPathList); 121 122 /** 123 * Removes final certificate from the certPathList 124 * 125 * @param certPathList the certification path list 126 */ removeFinalCertFromPath(LinkedList<X509Certificate> certPathList)127 abstract void removeFinalCertFromPath 128 (LinkedList<X509Certificate> certPathList); 129 130 /** 131 * get distance of one GeneralName from another 132 * 133 * @param base GeneralName at base of subtree 134 * @param test GeneralName to be tested against base 135 * @param incomparable the value to return if the names are 136 * incomparable 137 * @return distance of test name from base, where 0 138 * means exact match, 1 means test is an immediate 139 * child of base, 2 means test is a grandchild, etc. 140 * -1 means test is a parent of base, -2 means test 141 * is a grandparent, etc. 142 */ distance(GeneralNameInterface base, GeneralNameInterface test, int incomparable)143 static int distance(GeneralNameInterface base, 144 GeneralNameInterface test, int incomparable) 145 { 146 switch (base.constrains(test)) { 147 case GeneralNameInterface.NAME_DIFF_TYPE: 148 if (debug != null) { 149 debug.println("Builder.distance(): Names are different types"); 150 } 151 return incomparable; 152 case GeneralNameInterface.NAME_SAME_TYPE: 153 if (debug != null) { 154 debug.println("Builder.distance(): Names are same type but " + 155 "in different subtrees"); 156 } 157 return incomparable; 158 case GeneralNameInterface.NAME_MATCH: 159 return 0; 160 case GeneralNameInterface.NAME_WIDENS: 161 break; 162 case GeneralNameInterface.NAME_NARROWS: 163 break; 164 default: // should never occur 165 return incomparable; 166 } 167 168 /* names are in same subtree */ 169 return test.subtreeDepth() - base.subtreeDepth(); 170 } 171 172 /** 173 * get hop distance of one GeneralName from another in links where 174 * the names need not have an ancestor/descendant relationship. 175 * For example, the hop distance from ou=D,ou=C,o=B,c=US to 176 * ou=F,ou=E,ou=C,o=B,c=US is 3: D->C, C->E, E->F. The hop distance 177 * from ou=C,o=B,c=US to ou=D,ou=C,o=B,c=US is -1: C->D 178 * 179 * @param base GeneralName 180 * @param test GeneralName to be tested against base 181 * @param incomparable the value to return if the names are 182 * incomparable 183 * @return distance of test name from base measured in hops in the 184 * namespace hierarchy, where 0 means exact match. Result 185 * is positive if path is some number of up hops followed by 186 * some number of down hops; result is negative if path is 187 * some number of down hops. 188 */ hops(GeneralNameInterface base, GeneralNameInterface test, int incomparable)189 static int hops(GeneralNameInterface base, GeneralNameInterface test, 190 int incomparable) 191 { 192 int baseRtest = base.constrains(test); 193 switch (baseRtest) { 194 case GeneralNameInterface.NAME_DIFF_TYPE: 195 if (debug != null) { 196 debug.println("Builder.hops(): Names are different types"); 197 } 198 return incomparable; 199 case GeneralNameInterface.NAME_SAME_TYPE: 200 /* base and test are in different subtrees */ 201 break; 202 case GeneralNameInterface.NAME_MATCH: 203 /* base matches test */ 204 return 0; 205 case GeneralNameInterface.NAME_WIDENS: 206 /* base is ancestor of test */ 207 return (test.subtreeDepth()-base.subtreeDepth()); 208 case GeneralNameInterface.NAME_NARROWS: 209 /* base is descendant of test */ 210 return (test.subtreeDepth()-base.subtreeDepth()); 211 default: // should never occur 212 return incomparable; 213 } 214 215 /* names are in different subtrees */ 216 if (base.getType() != GeneralNameInterface.NAME_DIRECTORY) { 217 if (debug != null) { 218 debug.println("Builder.hops(): hopDistance not implemented " + 219 "for this name type"); 220 } 221 return incomparable; 222 } 223 X500Name baseName = (X500Name)base; 224 X500Name testName = (X500Name)test; 225 X500Name commonName = baseName.commonAncestor(testName); 226 if (commonName == null) { 227 if (debug != null) { 228 debug.println("Builder.hops(): Names are in different " + 229 "namespaces"); 230 } 231 return incomparable; 232 } else { 233 int commonDistance = commonName.subtreeDepth(); 234 int baseDistance = baseName.subtreeDepth(); 235 int testDistance = testName.subtreeDepth(); 236 return (baseDistance + testDistance - (2 * commonDistance)); 237 } 238 } 239 240 /** 241 * Determine how close a given certificate gets you toward 242 * a given target. 243 * 244 * @param constraints Current NameConstraints; if null, 245 * then caller must verify NameConstraints 246 * independently, realizing that this certificate 247 * may not actually lead to the target at all. 248 * @param cert Candidate certificate for chain 249 * @param target GeneralNameInterface name of target 250 * @return distance from this certificate to target: 251 * <ul> 252 * <li>-1 means certificate could be CA for target, but 253 * there are no NameConstraints limiting how close 254 * <li> 0 means certificate subject or subjectAltName 255 * matches target 256 * <li> 1 means certificate is permitted to be CA for 257 * target. 258 * <li> 2 means certificate is permitted to be CA for 259 * parent of target. 260 * <li>>0 in general, means certificate is permitted 261 * to be a CA for this distance higher in the naming 262 * hierarchy than the target, plus 1. 263 * </ul> 264 * <p>Note that the subject and/or subjectAltName of the 265 * candidate cert does not have to be an ancestor of the 266 * target in order to be a CA that can issue a certificate to 267 * the target. In these cases, the target distance is calculated 268 * by inspecting the NameConstraints extension in the candidate 269 * certificate. For example, suppose the target is an X.500 DN with 270 * a value of "CN=mullan,OU=ireland,O=sun,C=us" and the 271 * NameConstraints extension in the candidate certificate 272 * includes a permitted component of "O=sun,C=us", which implies 273 * that the candidate certificate is allowed to issue certs in 274 * the "O=sun,C=us" namespace. The target distance is 3 275 * ((distance of permitted NC from target) + 1). 276 * The (+1) is added to distinguish the result from the case 277 * which returns (0). 278 * @throws IOException if certificate does not get closer 279 */ targetDistance(NameConstraintsExtension constraints, X509Certificate cert, GeneralNameInterface target)280 static int targetDistance(NameConstraintsExtension constraints, 281 X509Certificate cert, GeneralNameInterface target) 282 throws IOException 283 { 284 /* ensure that certificate satisfies existing name constraints */ 285 if (constraints != null && !constraints.verify(cert)) { 286 throw new IOException("certificate does not satisfy existing name " 287 + "constraints"); 288 } 289 290 X509CertImpl certImpl; 291 try { 292 certImpl = X509CertImpl.toImpl(cert); 293 } catch (CertificateException e) { 294 throw new IOException("Invalid certificate", e); 295 } 296 /* see if certificate subject matches target */ 297 X500Name subject = X500Name.asX500Name(certImpl.getSubjectX500Principal()); 298 if (subject.equals(target)) { 299 /* match! */ 300 return 0; 301 } 302 303 SubjectAlternativeNameExtension altNameExt = 304 certImpl.getSubjectAlternativeNameExtension(); 305 if (altNameExt != null) { 306 GeneralNames altNames = altNameExt.get( 307 SubjectAlternativeNameExtension.SUBJECT_NAME); 308 /* see if any alternative name matches target */ 309 if (altNames != null) { 310 for (int j = 0, n = altNames.size(); j < n; j++) { 311 GeneralNameInterface altName = altNames.get(j).getName(); 312 if (altName.equals(target)) { 313 return 0; 314 } 315 } 316 } 317 } 318 319 320 /* no exact match; see if certificate can get us to target */ 321 322 /* first, get NameConstraints out of certificate */ 323 NameConstraintsExtension ncExt = certImpl.getNameConstraintsExtension(); 324 if (ncExt == null) { 325 return -1; 326 } 327 328 /* merge certificate's NameConstraints with current NameConstraints */ 329 if (constraints != null) { 330 constraints.merge(ncExt); 331 } else { 332 // Make sure we do a clone here, because we're probably 333 // going to modify this object later and we don't want to 334 // be sharing it with a Certificate object! 335 constraints = (NameConstraintsExtension) ncExt.clone(); 336 } 337 338 if (debug != null) { 339 debug.println("Builder.targetDistance() merged constraints: " 340 + String.valueOf(constraints)); 341 } 342 /* reduce permitted by excluded */ 343 GeneralSubtrees permitted = 344 constraints.get(NameConstraintsExtension.PERMITTED_SUBTREES); 345 GeneralSubtrees excluded = 346 constraints.get(NameConstraintsExtension.EXCLUDED_SUBTREES); 347 if (permitted != null) { 348 permitted.reduce(excluded); 349 } 350 if (debug != null) { 351 debug.println("Builder.targetDistance() reduced constraints: " 352 + permitted); 353 } 354 /* see if new merged constraints allow target */ 355 if (!constraints.verify(target)) { 356 throw new IOException("New certificate not allowed to sign " 357 + "certificate for target"); 358 } 359 /* find distance to target, if any, in permitted */ 360 if (permitted == null) { 361 /* certificate is unconstrained; could sign for anything */ 362 return -1; 363 } 364 for (int i = 0, n = permitted.size(); i < n; i++) { 365 GeneralNameInterface perName = permitted.get(i).getName().getName(); 366 int distance = distance(perName, target, -1); 367 if (distance >= 0) { 368 return (distance + 1); 369 } 370 } 371 /* no matching type in permitted; cert holder could certify target */ 372 return -1; 373 } 374 375 /** 376 * This method can be used as an optimization to filter out 377 * certificates that do not have policies which are valid. 378 * It returns the set of policies (String OIDs) that should exist in 379 * the certificate policies extension of the certificate that is 380 * needed by the builder. The logic applied is as follows: 381 * <p> 382 * 1) If some initial policies have been set *and* policy mappings are 383 * inhibited, then acceptable certificates are those that include 384 * the ANY_POLICY OID or with policies that intersect with the 385 * initial policies. 386 * 2) If no initial policies have been set *or* policy mappings are 387 * not inhibited then we don't have much to work with. All we know is 388 * that a certificate must have *some* policy because if it didn't 389 * have any policy then the policy tree would become null (and validation 390 * would fail). 391 * 392 * @return the Set of policies any of which must exist in a 393 * cert's certificate policies extension in order for a cert to be selected. 394 */ getMatchingPolicies()395 Set<String> getMatchingPolicies() { 396 if (matchingPolicies != null) { 397 Set<String> initialPolicies = buildParams.initialPolicies(); 398 if ((!initialPolicies.isEmpty()) && 399 (!initialPolicies.contains(PolicyChecker.ANY_POLICY)) && 400 (buildParams.policyMappingInhibited())) 401 { 402 matchingPolicies = new HashSet<>(initialPolicies); 403 matchingPolicies.add(PolicyChecker.ANY_POLICY); 404 } else { 405 // we just return an empty set to make sure that there is 406 // at least a certificate policies extension in the cert 407 matchingPolicies = Collections.<String>emptySet(); 408 } 409 } 410 return matchingPolicies; 411 } 412 413 /** 414 * Search the specified CertStores and add all certificates matching 415 * selector to resultCerts. Self-signed certs are not useful here 416 * and therefore ignored. 417 * 418 * If the targetCert criterion of the selector is set, only that cert 419 * is examined and the CertStores are not searched. 420 * 421 * If checkAll is true, all CertStores are searched for matching certs. 422 * If false, the method returns as soon as the first CertStore returns 423 * a matching cert(s). 424 * 425 * Returns true iff resultCerts changed (a cert was added to the collection) 426 */ addMatchingCerts(X509CertSelector selector, Collection<CertStore> certStores, Collection<X509Certificate> resultCerts, boolean checkAll)427 boolean addMatchingCerts(X509CertSelector selector, 428 Collection<CertStore> certStores, 429 Collection<X509Certificate> resultCerts, 430 boolean checkAll) 431 { 432 X509Certificate targetCert = selector.getCertificate(); 433 if (targetCert != null) { 434 // no need to search CertStores 435 if (selector.match(targetCert) && !X509CertImpl.isSelfSigned 436 (targetCert, buildParams.sigProvider())) { 437 if (debug != null) { 438 debug.println("Builder.addMatchingCerts: " + 439 "adding target cert" + 440 "\n SN: " + Debug.toHexString( 441 targetCert.getSerialNumber()) + 442 "\n Subject: " + targetCert.getSubjectX500Principal() + 443 "\n Issuer: " + targetCert.getIssuerX500Principal()); 444 } 445 return resultCerts.add(targetCert); 446 } 447 return false; 448 } 449 boolean add = false; 450 for (CertStore store : certStores) { 451 try { 452 Collection<? extends Certificate> certs = 453 store.getCertificates(selector); 454 for (Certificate cert : certs) { 455 if (!X509CertImpl.isSelfSigned 456 ((X509Certificate)cert, buildParams.sigProvider())) { 457 if (resultCerts.add((X509Certificate)cert)) { 458 add = true; 459 } 460 } 461 } 462 if (!checkAll && add) { 463 return true; 464 } 465 } catch (CertStoreException cse) { 466 // if getCertificates throws a CertStoreException, we ignore 467 // it and move on to the next CertStore 468 if (debug != null) { 469 debug.println("Builder.addMatchingCerts, non-fatal " + 470 "exception retrieving certs: " + cse); 471 cse.printStackTrace(); 472 } 473 } 474 } 475 return add; 476 } 477 } 478