Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/Builder.java
41161 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider.certpath;2627import java.io.IOException;28import java.security.AccessController;29import java.security.GeneralSecurityException;30import java.security.cert.*;31import java.util.*;3233import sun.security.action.GetBooleanAction;34import sun.security.provider.certpath.PKIX.BuilderParams;35import sun.security.util.Debug;36import sun.security.x509.GeneralNames;37import sun.security.x509.GeneralNameInterface;38import sun.security.x509.GeneralSubtrees;39import sun.security.x509.NameConstraintsExtension;40import sun.security.x509.SubjectAlternativeNameExtension;41import sun.security.x509.X500Name;42import sun.security.x509.X509CertImpl;4344/**45* Abstract class representing a builder, which is able to retrieve46* matching certificates and is able to verify a particular certificate.47*48* @since 1.449* @author Sean Mullan50* @author Yassir Elley51*/5253public abstract class Builder {5455private static final Debug debug = Debug.getInstance("certpath");56private Set<String> matchingPolicies;57final BuilderParams buildParams;58final X509CertSelector targetCertConstraints;5960/**61* Flag indicating whether support for the caIssuers field of the62* Authority Information Access extension shall be enabled. Currently63* disabled by default for compatibility reasons.64*/65static final boolean USE_AIA = GetBooleanAction66.privilegedGetProperty("com.sun.security.enableAIAcaIssuers");6768/**69* Initialize the builder with the input parameters.70*71* @param buildParams the parameter set used to build a certification path72*/73Builder(BuilderParams buildParams) {74this.buildParams = buildParams;75this.targetCertConstraints =76(X509CertSelector)buildParams.targetCertConstraints();77}7879/**80* Retrieves certificates from the list of certStores using the buildParams81* and the currentState as a filter82*83* @param currentState the current State84* @param certStores list of CertStores85*/86abstract Collection<X509Certificate> getMatchingCerts87(State currentState, List<CertStore> certStores)88throws CertStoreException, CertificateException, IOException;8990/**91* Verifies the cert against the currentState, using the certPathList92* generated thus far to help with loop detection93*94* @param cert the certificate to be verified95* @param currentState the current state against which the cert is verified96* @param certPathList the certPathList generated thus far97*/98abstract void verifyCert(X509Certificate cert, State currentState,99List<X509Certificate> certPathList)100throws GeneralSecurityException;101102/**103* Verifies whether the input certificate completes the path.104* When building in the forward direction, a trust anchor will105* complete the path.106*107* @param cert the certificate to test108* @return a boolean value indicating whether the cert completes the path.109*/110abstract boolean isPathCompleted(X509Certificate cert);111112/**113* Adds the certificate to the certPathList114*115* @param cert the certificate to be added116* @param certPathList the certification path list117*/118abstract void addCertToPath(X509Certificate cert,119LinkedList<X509Certificate> certPathList);120121/**122* Removes final certificate from the certPathList123*124* @param certPathList the certification path list125*/126abstract void removeFinalCertFromPath127(LinkedList<X509Certificate> certPathList);128129/**130* get distance of one GeneralName from another131*132* @param base GeneralName at base of subtree133* @param test GeneralName to be tested against base134* @param incomparable the value to return if the names are135* incomparable136* @return distance of test name from base, where 0137* means exact match, 1 means test is an immediate138* child of base, 2 means test is a grandchild, etc.139* -1 means test is a parent of base, -2 means test140* is a grandparent, etc.141*/142static int distance(GeneralNameInterface base,143GeneralNameInterface test, int incomparable)144{145switch (base.constrains(test)) {146case GeneralNameInterface.NAME_DIFF_TYPE:147if (debug != null) {148debug.println("Builder.distance(): Names are different types");149}150return incomparable;151case GeneralNameInterface.NAME_SAME_TYPE:152if (debug != null) {153debug.println("Builder.distance(): Names are same type but " +154"in different subtrees");155}156return incomparable;157case GeneralNameInterface.NAME_MATCH:158return 0;159case GeneralNameInterface.NAME_WIDENS:160break;161case GeneralNameInterface.NAME_NARROWS:162break;163default: // should never occur164return incomparable;165}166167/* names are in same subtree */168return test.subtreeDepth() - base.subtreeDepth();169}170171/**172* get hop distance of one GeneralName from another in links where173* the names need not have an ancestor/descendant relationship.174* For example, the hop distance from ou=D,ou=C,o=B,c=US to175* ou=F,ou=E,ou=C,o=B,c=US is 3: D->C, C->E, E->F. The hop distance176* from ou=C,o=B,c=US to ou=D,ou=C,o=B,c=US is -1: C->D177*178* @param base GeneralName179* @param test GeneralName to be tested against base180* @param incomparable the value to return if the names are181* incomparable182* @return distance of test name from base measured in hops in the183* namespace hierarchy, where 0 means exact match. Result184* is positive if path is some number of up hops followed by185* some number of down hops; result is negative if path is186* some number of down hops.187*/188static int hops(GeneralNameInterface base, GeneralNameInterface test,189int incomparable)190{191int baseRtest = base.constrains(test);192switch (baseRtest) {193case GeneralNameInterface.NAME_DIFF_TYPE:194if (debug != null) {195debug.println("Builder.hops(): Names are different types");196}197return incomparable;198case GeneralNameInterface.NAME_SAME_TYPE:199/* base and test are in different subtrees */200break;201case GeneralNameInterface.NAME_MATCH:202/* base matches test */203return 0;204case GeneralNameInterface.NAME_WIDENS:205/* base is ancestor of test */206return (test.subtreeDepth()-base.subtreeDepth());207case GeneralNameInterface.NAME_NARROWS:208/* base is descendant of test */209return (test.subtreeDepth()-base.subtreeDepth());210default: // should never occur211return incomparable;212}213214/* names are in different subtrees */215if (base.getType() != GeneralNameInterface.NAME_DIRECTORY) {216if (debug != null) {217debug.println("Builder.hops(): hopDistance not implemented " +218"for this name type");219}220return incomparable;221}222X500Name baseName = (X500Name)base;223X500Name testName = (X500Name)test;224X500Name commonName = baseName.commonAncestor(testName);225if (commonName == null) {226if (debug != null) {227debug.println("Builder.hops(): Names are in different " +228"namespaces");229}230return incomparable;231} else {232int commonDistance = commonName.subtreeDepth();233int baseDistance = baseName.subtreeDepth();234int testDistance = testName.subtreeDepth();235return (baseDistance + testDistance - (2 * commonDistance));236}237}238239/**240* Determine how close a given certificate gets you toward241* a given target.242*243* @param constraints Current NameConstraints; if null,244* then caller must verify NameConstraints245* independently, realizing that this certificate246* may not actually lead to the target at all.247* @param cert Candidate certificate for chain248* @param target GeneralNameInterface name of target249* @return distance from this certificate to target:250* <ul>251* <li>-1 means certificate could be CA for target, but252* there are no NameConstraints limiting how close253* <li> 0 means certificate subject or subjectAltName254* matches target255* <li> 1 means certificate is permitted to be CA for256* target.257* <li> 2 means certificate is permitted to be CA for258* parent of target.259* <li>>0 in general, means certificate is permitted260* to be a CA for this distance higher in the naming261* hierarchy than the target, plus 1.262* </ul>263* <p>Note that the subject and/or subjectAltName of the264* candidate cert does not have to be an ancestor of the265* target in order to be a CA that can issue a certificate to266* the target. In these cases, the target distance is calculated267* by inspecting the NameConstraints extension in the candidate268* certificate. For example, suppose the target is an X.500 DN with269* a value of "CN=mullan,OU=ireland,O=sun,C=us" and the270* NameConstraints extension in the candidate certificate271* includes a permitted component of "O=sun,C=us", which implies272* that the candidate certificate is allowed to issue certs in273* the "O=sun,C=us" namespace. The target distance is 3274* ((distance of permitted NC from target) + 1).275* The (+1) is added to distinguish the result from the case276* which returns (0).277* @throws IOException if certificate does not get closer278*/279static int targetDistance(NameConstraintsExtension constraints,280X509Certificate cert, GeneralNameInterface target)281throws IOException282{283/* ensure that certificate satisfies existing name constraints */284if (constraints != null && !constraints.verify(cert)) {285throw new IOException("certificate does not satisfy existing name "286+ "constraints");287}288289X509CertImpl certImpl;290try {291certImpl = X509CertImpl.toImpl(cert);292} catch (CertificateException e) {293throw new IOException("Invalid certificate", e);294}295/* see if certificate subject matches target */296X500Name subject = X500Name.asX500Name(certImpl.getSubjectX500Principal());297if (subject.equals(target)) {298/* match! */299return 0;300}301302SubjectAlternativeNameExtension altNameExt =303certImpl.getSubjectAlternativeNameExtension();304if (altNameExt != null) {305GeneralNames altNames = altNameExt.get(306SubjectAlternativeNameExtension.SUBJECT_NAME);307/* see if any alternative name matches target */308if (altNames != null) {309for (int j = 0, n = altNames.size(); j < n; j++) {310GeneralNameInterface altName = altNames.get(j).getName();311if (altName.equals(target)) {312return 0;313}314}315}316}317318319/* no exact match; see if certificate can get us to target */320321/* first, get NameConstraints out of certificate */322NameConstraintsExtension ncExt = certImpl.getNameConstraintsExtension();323if (ncExt == null) {324return -1;325}326327/* merge certificate's NameConstraints with current NameConstraints */328if (constraints != null) {329constraints.merge(ncExt);330} else {331// Make sure we do a clone here, because we're probably332// going to modify this object later and we don't want to333// be sharing it with a Certificate object!334constraints = (NameConstraintsExtension) ncExt.clone();335}336337if (debug != null) {338debug.println("Builder.targetDistance() merged constraints: "339+ String.valueOf(constraints));340}341/* reduce permitted by excluded */342GeneralSubtrees permitted =343constraints.get(NameConstraintsExtension.PERMITTED_SUBTREES);344GeneralSubtrees excluded =345constraints.get(NameConstraintsExtension.EXCLUDED_SUBTREES);346if (permitted != null) {347permitted.reduce(excluded);348}349if (debug != null) {350debug.println("Builder.targetDistance() reduced constraints: "351+ permitted);352}353/* see if new merged constraints allow target */354if (!constraints.verify(target)) {355throw new IOException("New certificate not allowed to sign "356+ "certificate for target");357}358/* find distance to target, if any, in permitted */359if (permitted == null) {360/* certificate is unconstrained; could sign for anything */361return -1;362}363for (int i = 0, n = permitted.size(); i < n; i++) {364GeneralNameInterface perName = permitted.get(i).getName().getName();365int distance = distance(perName, target, -1);366if (distance >= 0) {367return (distance + 1);368}369}370/* no matching type in permitted; cert holder could certify target */371return -1;372}373374/**375* This method can be used as an optimization to filter out376* certificates that do not have policies which are valid.377* It returns the set of policies (String OIDs) that should exist in378* the certificate policies extension of the certificate that is379* needed by the builder. The logic applied is as follows:380* <p>381* 1) If some initial policies have been set *and* policy mappings are382* inhibited, then acceptable certificates are those that include383* the ANY_POLICY OID or with policies that intersect with the384* initial policies.385* 2) If no initial policies have been set *or* policy mappings are386* not inhibited then we don't have much to work with. All we know is387* that a certificate must have *some* policy because if it didn't388* have any policy then the policy tree would become null (and validation389* would fail).390*391* @return the Set of policies any of which must exist in a392* cert's certificate policies extension in order for a cert to be selected.393*/394Set<String> getMatchingPolicies() {395if (matchingPolicies != null) {396Set<String> initialPolicies = buildParams.initialPolicies();397if ((!initialPolicies.isEmpty()) &&398(!initialPolicies.contains(PolicyChecker.ANY_POLICY)) &&399(buildParams.policyMappingInhibited()))400{401matchingPolicies = new HashSet<>(initialPolicies);402matchingPolicies.add(PolicyChecker.ANY_POLICY);403} else {404// we just return an empty set to make sure that there is405// at least a certificate policies extension in the cert406matchingPolicies = Collections.<String>emptySet();407}408}409return matchingPolicies;410}411412/**413* Search the specified CertStores and add all certificates matching414* selector to resultCerts. Self-signed certs are not useful here415* and therefore ignored.416*417* If the targetCert criterion of the selector is set, only that cert418* is examined and the CertStores are not searched.419*420* If checkAll is true, all CertStores are searched for matching certs.421* If false, the method returns as soon as the first CertStore returns422* a matching cert(s).423*424* Returns true iff resultCerts changed (a cert was added to the collection)425*/426boolean addMatchingCerts(X509CertSelector selector,427Collection<CertStore> certStores,428Collection<X509Certificate> resultCerts,429boolean checkAll)430{431X509Certificate targetCert = selector.getCertificate();432if (targetCert != null) {433// no need to search CertStores434if (selector.match(targetCert) && !X509CertImpl.isSelfSigned435(targetCert, buildParams.sigProvider())) {436if (debug != null) {437debug.println("Builder.addMatchingCerts: " +438"adding target cert" +439"\n SN: " + Debug.toHexString(440targetCert.getSerialNumber()) +441"\n Subject: " + targetCert.getSubjectX500Principal() +442"\n Issuer: " + targetCert.getIssuerX500Principal());443}444return resultCerts.add(targetCert);445}446return false;447}448boolean add = false;449for (CertStore store : certStores) {450try {451Collection<? extends Certificate> certs =452store.getCertificates(selector);453for (Certificate cert : certs) {454if (!X509CertImpl.isSelfSigned455((X509Certificate)cert, buildParams.sigProvider())) {456if (resultCerts.add((X509Certificate)cert)) {457add = true;458}459}460}461if (!checkAll && add) {462return true;463}464} catch (CertStoreException cse) {465// if getCertificates throws a CertStoreException, we ignore466// it and move on to the next CertStore467if (debug != null) {468debug.println("Builder.addMatchingCerts, non-fatal " +469"exception retrieving certs: " + cse);470cse.printStackTrace();471}472}473}474return add;475}476}477478479