Path: blob/master/src/java.base/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java
41161 views
/*1* Copyright (c) 2000, 2017, 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.GeneralSecurityException;29import java.security.InvalidAlgorithmParameterException;30import java.security.PublicKey;31import java.security.cert.*;32import java.security.cert.CertPathValidatorException.BasicReason;33import java.security.cert.PKIXReason;34import java.util.ArrayList;35import java.util.Collection;36import java.util.Collections;37import java.util.List;38import java.util.LinkedList;39import java.util.Set;40import javax.security.auth.x500.X500Principal;4142import sun.security.provider.certpath.PKIX.BuilderParams;43import static sun.security.x509.PKIXExtensions.*;44import sun.security.util.Debug;4546/**47* This class builds certification paths in the forward direction.48*49* <p> If successful, it returns a certification path which has successfully50* satisfied all the constraints and requirements specified in the51* PKIXBuilderParameters object and has been validated according to the PKIX52* path validation algorithm defined in RFC 5280.53*54* <p> This implementation uses a depth-first search approach to finding55* certification paths. If it comes to a point in which it cannot find56* any more certificates leading to the target OR the path length is too long57* it backtracks to previous paths until the target has been found or58* all possible paths have been exhausted.59*60* <p> This implementation is not thread-safe.61*62* @since 1.463* @author Sean Mullan64* @author Yassir Elley65*/66public final class SunCertPathBuilder extends CertPathBuilderSpi {6768private static final Debug debug = Debug.getInstance("certpath");6970/*71* private objects shared by methods72*/73private BuilderParams buildParams;74private CertificateFactory cf;75private boolean pathCompleted = false;76private PolicyNode policyTreeResult;77private TrustAnchor trustAnchor;78private PublicKey finalPublicKey;7980/**81* Create an instance of <code>SunCertPathBuilder</code>.82*83* @throws CertPathBuilderException if an error occurs84*/85public SunCertPathBuilder() throws CertPathBuilderException {86try {87cf = CertificateFactory.getInstance("X.509");88} catch (CertificateException e) {89throw new CertPathBuilderException(e);90}91}9293@Override94public CertPathChecker engineGetRevocationChecker() {95return new RevocationChecker();96}9798/**99* Attempts to build a certification path using the Sun build100* algorithm from a trusted anchor(s) to a target subject, which must both101* be specified in the input parameter set. This method will102* attempt to build in the forward direction: from the target to the CA.103*104* <p>The certification path that is constructed is validated105* according to the PKIX specification.106*107* @param params the parameter set for building a path. Must be an instance108* of <code>PKIXBuilderParameters</code>.109* @return a certification path builder result.110* @exception CertPathBuilderException Exception thrown if builder is111* unable to build a complete certification path from the trusted anchor(s)112* to the target subject.113* @throws InvalidAlgorithmParameterException if the given parameters are114* inappropriate for this certification path builder.115*/116@Override117public CertPathBuilderResult engineBuild(CertPathParameters params)118throws CertPathBuilderException, InvalidAlgorithmParameterException {119120if (debug != null) {121debug.println("SunCertPathBuilder.engineBuild(" + params + ")");122}123124buildParams = PKIX.checkBuilderParams(params);125return build();126}127128private PKIXCertPathBuilderResult build() throws CertPathBuilderException {129List<List<Vertex>> adjList = new ArrayList<>();130PKIXCertPathBuilderResult result = buildCertPath(false, adjList);131if (result == null) {132if (debug != null) {133debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " +134"try building again searching all certstores");135}136// try again137adjList.clear();138result = buildCertPath(true, adjList);139if (result == null) {140throw new SunCertPathBuilderException("unable to find valid "141+ "certification path to requested target",142new AdjacencyList(adjList));143}144}145return result;146}147148private PKIXCertPathBuilderResult buildCertPath(boolean searchAllCertStores,149List<List<Vertex>> adjList)150throws CertPathBuilderException151{152// Init shared variables and build certification path153pathCompleted = false;154trustAnchor = null;155finalPublicKey = null;156policyTreeResult = null;157LinkedList<X509Certificate> certPathList = new LinkedList<>();158try {159buildForward(adjList, certPathList, searchAllCertStores);160} catch (GeneralSecurityException | IOException e) {161if (debug != null) {162debug.println("SunCertPathBuilder.engineBuild() exception in "163+ "build");164e.printStackTrace();165}166throw new SunCertPathBuilderException("unable to find valid "167+ "certification path to requested target", e,168new AdjacencyList(adjList));169}170171// construct SunCertPathBuilderResult172try {173if (pathCompleted) {174if (debug != null)175debug.println("SunCertPathBuilder.engineBuild() "176+ "pathCompleted");177178// we must return a certpath which has the target179// as the first cert in the certpath - i.e. reverse180// the certPathList181Collections.reverse(certPathList);182183return new SunCertPathBuilderResult(184cf.generateCertPath(certPathList), trustAnchor,185policyTreeResult, finalPublicKey,186new AdjacencyList(adjList));187}188} catch (CertificateException e) {189if (debug != null) {190debug.println("SunCertPathBuilder.engineBuild() exception "191+ "in wrap-up");192e.printStackTrace();193}194throw new SunCertPathBuilderException("unable to find valid "195+ "certification path to requested target", e,196new AdjacencyList(adjList));197}198199return null;200}201202/*203* Private build forward method.204*/205private void buildForward(List<List<Vertex>> adjacencyList,206LinkedList<X509Certificate> certPathList,207boolean searchAllCertStores)208throws GeneralSecurityException, IOException209{210if (debug != null) {211debug.println("SunCertPathBuilder.buildForward()...");212}213214/* Initialize current state */215ForwardState currentState = new ForwardState();216currentState.initState(buildParams.certPathCheckers());217218/* Initialize adjacency list */219adjacencyList.clear();220adjacencyList.add(new LinkedList<Vertex>());221222currentState.untrustedChecker = new UntrustedChecker();223224depthFirstSearchForward(buildParams.targetSubject(), currentState,225new ForwardBuilder(buildParams,226searchAllCertStores),227adjacencyList, certPathList);228}229230/*231* This method performs a depth first search for a certification232* path while building forward which meets the requirements set in233* the parameters object.234* It uses an adjacency list to store all certificates which were235* tried (i.e. at one time added to the path - they may not end up in236* the final path if backtracking occurs). This information can237* be used later to debug or demo the build.238*239* See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"240* for an explanation of the DFS algorithm.241*242* @param dN the distinguished name being currently searched for certs243* @param currentState the current PKIX validation state244*/245private void depthFirstSearchForward(X500Principal dN,246ForwardState currentState,247ForwardBuilder builder,248List<List<Vertex>> adjList,249LinkedList<X509Certificate> cpList)250throws GeneralSecurityException, IOException251{252if (debug != null) {253debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN254+ ", " + currentState.toString() + ")");255}256257/*258* Find all the certificates issued to dN which259* satisfy the PKIX certification path constraints.260*/261Collection<X509Certificate> certs =262builder.getMatchingCerts(currentState, buildParams.certStores());263List<Vertex> vertices = addVertices(certs, adjList);264if (debug != null) {265debug.println("SunCertPathBuilder.depthFirstSearchForward(): "266+ "certs.size=" + vertices.size());267}268269/*270* For each cert in the collection, verify anything271* that hasn't been checked yet (signature, revocation, etc)272* and check for loops. Call depthFirstSearchForward()273* recursively for each good cert.274*/275276vertices:277for (Vertex vertex : vertices) {278/**279* Restore state to currentState each time through the loop.280* This is important because some of the user-defined281* checkers modify the state, which MUST be restored if282* the cert eventually fails to lead to the target and283* the next matching cert is tried.284*/285ForwardState nextState = (ForwardState) currentState.clone();286X509Certificate cert = vertex.getCertificate();287288try {289builder.verifyCert(cert, nextState, cpList);290} catch (GeneralSecurityException gse) {291if (debug != null) {292debug.println("SunCertPathBuilder.depthFirstSearchForward()"293+ ": validation failed: " + gse);294gse.printStackTrace();295}296vertex.setThrowable(gse);297continue;298}299300/*301* Certificate is good.302* If cert completes the path,303* process userCheckers that don't support forward checking304* and process policies over whole path305* and backtrack appropriately if there is a failure306* else if cert does not complete the path,307* add it to the path308*/309if (builder.isPathCompleted(cert)) {310311if (debug != null)312debug.println("SunCertPathBuilder.depthFirstSearchForward()"313+ ": commencing final verification");314315List<X509Certificate> appendedCerts = new ArrayList<>(cpList);316317/*318* if the trust anchor selected is specified as a trusted319* public key rather than a trusted cert, then verify this320* cert (which is signed by the trusted public key), but321* don't add it yet to the cpList322*/323if (builder.trustAnchor.getTrustedCert() == null) {324appendedCerts.add(0, cert);325}326327Set<String> initExpPolSet =328Collections.singleton(PolicyChecker.ANY_POLICY);329330PolicyNodeImpl rootNode = new PolicyNodeImpl(null,331PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);332333List<PKIXCertPathChecker> checkers = new ArrayList<>();334PolicyChecker policyChecker335= new PolicyChecker(buildParams.initialPolicies(),336appendedCerts.size(),337buildParams.explicitPolicyRequired(),338buildParams.policyMappingInhibited(),339buildParams.anyPolicyInhibited(),340buildParams.policyQualifiersRejected(),341rootNode);342checkers.add(policyChecker);343344// add the algorithm checker345checkers.add(new AlgorithmChecker(builder.trustAnchor,346buildParams.date(), buildParams.variant()));347348BasicChecker basicChecker = null;349if (nextState.keyParamsNeeded()) {350PublicKey rootKey = cert.getPublicKey();351if (builder.trustAnchor.getTrustedCert() == null) {352rootKey = builder.trustAnchor.getCAPublicKey();353if (debug != null)354debug.println(355"SunCertPathBuilder.depthFirstSearchForward " +356"using buildParams public key: " +357rootKey.toString());358}359TrustAnchor anchor = new TrustAnchor360(cert.getSubjectX500Principal(), rootKey, null);361362// add the basic checker363basicChecker = new BasicChecker(anchor, buildParams.date(),364buildParams.sigProvider(),365true);366checkers.add(basicChecker);367}368369buildParams.setCertPath(cf.generateCertPath(appendedCerts));370371boolean revCheckerAdded = false;372List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers();373for (PKIXCertPathChecker ckr : ckrs) {374if (ckr instanceof PKIXRevocationChecker) {375if (revCheckerAdded) {376throw new CertPathValidatorException(377"Only one PKIXRevocationChecker can be specified");378}379revCheckerAdded = true;380// if it's our own, initialize it381if (ckr instanceof RevocationChecker) {382((RevocationChecker)ckr).init(builder.trustAnchor,383buildParams);384}385}386}387// only add a RevocationChecker if revocation is enabled and388// a PKIXRevocationChecker has not already been added389if (buildParams.revocationEnabled() && !revCheckerAdded) {390checkers.add(new RevocationChecker(builder.trustAnchor,391buildParams));392}393394checkers.addAll(ckrs);395396// Why we don't need BasicChecker and RevocationChecker397// if nextState.keyParamsNeeded() is false?398399for (int i = 0; i < appendedCerts.size(); i++) {400X509Certificate currCert = appendedCerts.get(i);401if (debug != null)402debug.println("current subject = "403+ currCert.getSubjectX500Principal());404Set<String> unresCritExts =405currCert.getCriticalExtensionOIDs();406if (unresCritExts == null) {407unresCritExts = Collections.<String>emptySet();408}409410for (PKIXCertPathChecker currChecker : checkers) {411if (!currChecker.isForwardCheckingSupported()) {412if (i == 0) {413currChecker.init(false);414415// The user specified416// AlgorithmChecker may not be417// able to set the trust anchor until now.418if (currChecker instanceof AlgorithmChecker) {419((AlgorithmChecker)currChecker).420trySetTrustAnchor(builder.trustAnchor);421}422}423424try {425currChecker.check(currCert, unresCritExts);426} catch (CertPathValidatorException cpve) {427if (debug != null)428debug.println429("SunCertPathBuilder.depthFirstSearchForward(): " +430"final verification failed: " + cpve);431// If the target cert itself is revoked, we432// cannot trust it. We can bail out here.433if (buildParams.targetCertConstraints().match(currCert)434&& cpve.getReason() == BasicReason.REVOKED) {435throw cpve;436}437vertex.setThrowable(cpve);438continue vertices;439}440}441}442443/*444* Remove extensions from user checkers that support445* forward checking. After this step, we will have446* removed all extensions that all user checkers447* are capable of processing.448*/449for (PKIXCertPathChecker checker :450buildParams.certPathCheckers())451{452if (checker.isForwardCheckingSupported()) {453Set<String> suppExts =454checker.getSupportedExtensions();455if (suppExts != null) {456unresCritExts.removeAll(suppExts);457}458}459}460461if (!unresCritExts.isEmpty()) {462unresCritExts.remove(BasicConstraints_Id.toString());463unresCritExts.remove(NameConstraints_Id.toString());464unresCritExts.remove(CertificatePolicies_Id.toString());465unresCritExts.remove(PolicyMappings_Id.toString());466unresCritExts.remove(PolicyConstraints_Id.toString());467unresCritExts.remove(InhibitAnyPolicy_Id.toString());468unresCritExts.remove(469SubjectAlternativeName_Id.toString());470unresCritExts.remove(KeyUsage_Id.toString());471unresCritExts.remove(ExtendedKeyUsage_Id.toString());472473if (!unresCritExts.isEmpty()) {474throw new CertPathValidatorException475("unrecognized critical extension(s)", null,476null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT);477}478}479}480if (debug != null)481debug.println("SunCertPathBuilder.depthFirstSearchForward()"482+ ": final verification succeeded - path completed!");483pathCompleted = true;484485/*486* if the user specified a trusted public key rather than487* trusted certs, then add this cert (which is signed by488* the trusted public key) to the cpList489*/490if (builder.trustAnchor.getTrustedCert() == null)491builder.addCertToPath(cert, cpList);492// Save the trust anchor493this.trustAnchor = builder.trustAnchor;494495/*496* Extract and save the final target public key497*/498if (basicChecker != null) {499finalPublicKey = basicChecker.getPublicKey();500} else {501Certificate finalCert;502if (cpList.isEmpty()) {503finalCert = builder.trustAnchor.getTrustedCert();504} else {505finalCert = cpList.getLast();506}507finalPublicKey = finalCert.getPublicKey();508}509510policyTreeResult = policyChecker.getPolicyTree();511return;512} else {513builder.addCertToPath(cert, cpList);514}515516/* Update the PKIX state */517nextState.updateState(cert);518519/*520* Append an entry for cert in adjacency list and521* set index for current vertex.522*/523adjList.add(new LinkedList<Vertex>());524vertex.setIndex(adjList.size() - 1);525526/* recursively search for matching certs at next dN */527depthFirstSearchForward(cert.getIssuerX500Principal(), nextState,528builder, adjList, cpList);529530/*531* If path has been completed, return ASAP!532*/533if (pathCompleted) {534return;535} else {536/*537* If we get here, it means we have searched all possible538* certs issued by the dN w/o finding any matching certs.539* This means we have to backtrack to the previous cert in540* the path and try some other paths.541*/542if (debug != null)543debug.println("SunCertPathBuilder.depthFirstSearchForward()"544+ ": backtracking");545builder.removeFinalCertFromPath(cpList);546}547}548}549550/*551* Adds a collection of matching certificates to the552* adjacency list.553*/554private static List<Vertex> addVertices(Collection<X509Certificate> certs,555List<List<Vertex>> adjList)556{557List<Vertex> l = adjList.get(adjList.size() - 1);558559for (X509Certificate cert : certs) {560Vertex v = new Vertex(cert);561l.add(v);562}563564return l;565}566567/**568* Returns true if trust anchor certificate matches specified569* certificate constraints.570*/571private static boolean anchorIsTarget(TrustAnchor anchor,572CertSelector sel)573{574X509Certificate anchorCert = anchor.getTrustedCert();575if (anchorCert != null) {576return sel.match(anchorCert);577}578return false;579}580}581582583