| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.provider.certpath;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.security.InvalidAlgorithmParameterException;  | 
 | 
import java.security.PublicKey;  | 
 | 
import java.security.cert.*;  | 
 | 
import java.security.cert.CertPathValidatorException.BasicReason;  | 
 | 
import java.security.cert.PKIXReason;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Collection;  | 
 | 
import java.util.Collections;  | 
 | 
import java.util.List;  | 
 | 
import java.util.LinkedList;  | 
 | 
import java.util.Set;  | 
 | 
import javax.security.auth.x500.X500Principal;  | 
 | 
 | 
 | 
import sun.security.provider.certpath.PKIX.BuilderParams;  | 
 | 
import static sun.security.x509.PKIXExtensions.*;  | 
 | 
import sun.security.util.Debug;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public final class SunCertPathBuilder extends CertPathBuilderSpi { | 
 | 
 | 
 | 
    private static final Debug debug = Debug.getInstance("certpath"); | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private BuilderParams buildParams;  | 
 | 
    private CertificateFactory cf;  | 
 | 
    private boolean pathCompleted = false;  | 
 | 
    private PolicyNode policyTreeResult;  | 
 | 
    private TrustAnchor trustAnchor;  | 
 | 
    private PublicKey finalPublicKey;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public SunCertPathBuilder() throws CertPathBuilderException { | 
 | 
        try { | 
 | 
            cf = CertificateFactory.getInstance("X.509"); | 
 | 
        } catch (CertificateException e) { | 
 | 
            throw new CertPathBuilderException(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public CertPathChecker engineGetRevocationChecker() { | 
 | 
        return new RevocationChecker();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public CertPathBuilderResult engineBuild(CertPathParameters params)  | 
 | 
        throws CertPathBuilderException, InvalidAlgorithmParameterException { | 
 | 
 | 
 | 
        if (debug != null) { | 
 | 
            debug.println("SunCertPathBuilder.engineBuild(" + params + ")"); | 
 | 
        }  | 
 | 
 | 
 | 
        buildParams = PKIX.checkBuilderParams(params);  | 
 | 
        return build();  | 
 | 
    }  | 
 | 
 | 
 | 
    private PKIXCertPathBuilderResult build() throws CertPathBuilderException { | 
 | 
        List<List<Vertex>> adjList = new ArrayList<>();  | 
 | 
        PKIXCertPathBuilderResult result = buildCertPath(false, adjList);  | 
 | 
        if (result == null) { | 
 | 
            if (debug != null) { | 
 | 
                debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " + | 
 | 
                              "try building again searching all certstores");  | 
 | 
            }  | 
 | 
              | 
 | 
            adjList.clear();  | 
 | 
            result = buildCertPath(true, adjList);  | 
 | 
            if (result == null) { | 
 | 
                throw new SunCertPathBuilderException("unable to find valid " | 
 | 
                    + "certification path to requested target",  | 
 | 
                    new AdjacencyList(adjList));  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
    private PKIXCertPathBuilderResult buildCertPath(boolean searchAllCertStores,  | 
 | 
                                                    List<List<Vertex>> adjList)  | 
 | 
        throws CertPathBuilderException  | 
 | 
    { | 
 | 
          | 
 | 
        pathCompleted = false;  | 
 | 
        trustAnchor = null;  | 
 | 
        finalPublicKey = null;  | 
 | 
        policyTreeResult = null;  | 
 | 
        LinkedList<X509Certificate> certPathList = new LinkedList<>();  | 
 | 
        try { | 
 | 
            buildForward(adjList, certPathList, searchAllCertStores);  | 
 | 
        } catch (GeneralSecurityException | IOException e) { | 
 | 
            if (debug != null) { | 
 | 
                debug.println("SunCertPathBuilder.engineBuild() exception in " | 
 | 
                    + "build");  | 
 | 
                e.printStackTrace();  | 
 | 
            }  | 
 | 
            throw new SunCertPathBuilderException("unable to find valid " | 
 | 
                + "certification path to requested target", e,  | 
 | 
                new AdjacencyList(adjList));  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        try { | 
 | 
            if (pathCompleted) { | 
 | 
                if (debug != null)  | 
 | 
                    debug.println("SunCertPathBuilder.engineBuild() " | 
 | 
                                  + "pathCompleted");  | 
 | 
 | 
 | 
                // we must return a certpath which has the target  | 
 | 
                // as the first cert in the certpath - i.e. reverse  | 
 | 
                  | 
 | 
                Collections.reverse(certPathList);  | 
 | 
 | 
 | 
                return new SunCertPathBuilderResult(  | 
 | 
                    cf.generateCertPath(certPathList), trustAnchor,  | 
 | 
                    policyTreeResult, finalPublicKey,  | 
 | 
                    new AdjacencyList(adjList));  | 
 | 
            }  | 
 | 
        } catch (CertificateException e) { | 
 | 
            if (debug != null) { | 
 | 
                debug.println("SunCertPathBuilder.engineBuild() exception " | 
 | 
                              + "in wrap-up");  | 
 | 
                e.printStackTrace();  | 
 | 
            }  | 
 | 
            throw new SunCertPathBuilderException("unable to find valid " | 
 | 
                + "certification path to requested target", e,  | 
 | 
                new AdjacencyList(adjList));  | 
 | 
        }  | 
 | 
 | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private void buildForward(List<List<Vertex>> adjacencyList,  | 
 | 
                              LinkedList<X509Certificate> certPathList,  | 
 | 
                              boolean searchAllCertStores)  | 
 | 
        throws GeneralSecurityException, IOException  | 
 | 
    { | 
 | 
        if (debug != null) { | 
 | 
            debug.println("SunCertPathBuilder.buildForward()..."); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        ForwardState currentState = new ForwardState();  | 
 | 
        currentState.initState(buildParams.certPathCheckers());  | 
 | 
 | 
 | 
          | 
 | 
        adjacencyList.clear();  | 
 | 
        adjacencyList.add(new LinkedList<Vertex>());  | 
 | 
 | 
 | 
        currentState.untrustedChecker = new UntrustedChecker();  | 
 | 
 | 
 | 
        depthFirstSearchForward(buildParams.targetSubject(), currentState,  | 
 | 
                                new ForwardBuilder(buildParams,  | 
 | 
                                                   searchAllCertStores),  | 
 | 
                                adjacencyList, certPathList);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private void depthFirstSearchForward(X500Principal dN,  | 
 | 
                                         ForwardState currentState,  | 
 | 
                                         ForwardBuilder builder,  | 
 | 
                                         List<List<Vertex>> adjList,  | 
 | 
                                         LinkedList<X509Certificate> cpList)  | 
 | 
        throws GeneralSecurityException, IOException  | 
 | 
    { | 
 | 
        if (debug != null) { | 
 | 
            debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN | 
 | 
                          + ", " + currentState.toString() + ")");  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        Collection<X509Certificate> certs =  | 
 | 
            builder.getMatchingCerts(currentState, buildParams.certStores());  | 
 | 
        List<Vertex> vertices = addVertices(certs, adjList);  | 
 | 
        if (debug != null) { | 
 | 
            debug.println("SunCertPathBuilder.depthFirstSearchForward(): " | 
 | 
                          + "certs.size=" + vertices.size());  | 
 | 
        }  | 
 | 
 | 
 | 
        /*  | 
 | 
         * For each cert in the collection, verify anything  | 
 | 
         * that hasn't been checked yet (signature, revocation, etc)  | 
 | 
         * and check for loops. Call depthFirstSearchForward()  | 
 | 
         * recursively for each good cert.  | 
 | 
         */  | 
 | 
 | 
 | 
               vertices:  | 
 | 
        for (Vertex vertex : vertices) { | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            ForwardState nextState = (ForwardState) currentState.clone();  | 
 | 
            X509Certificate cert = vertex.getCertificate();  | 
 | 
 | 
 | 
            try { | 
 | 
                builder.verifyCert(cert, nextState, cpList);  | 
 | 
            } catch (GeneralSecurityException gse) { | 
 | 
                if (debug != null) { | 
 | 
                    debug.println("SunCertPathBuilder.depthFirstSearchForward()" | 
 | 
                                  + ": validation failed: " + gse);  | 
 | 
                    gse.printStackTrace();  | 
 | 
                }  | 
 | 
                vertex.setThrowable(gse);  | 
 | 
                continue;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            if (builder.isPathCompleted(cert)) { | 
 | 
 | 
 | 
                if (debug != null)  | 
 | 
                    debug.println("SunCertPathBuilder.depthFirstSearchForward()" | 
 | 
                                  + ": commencing final verification");  | 
 | 
 | 
 | 
                List<X509Certificate> appendedCerts = new ArrayList<>(cpList);  | 
 | 
 | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (builder.trustAnchor.getTrustedCert() == null) { | 
 | 
                    appendedCerts.add(0, cert);  | 
 | 
                }  | 
 | 
 | 
 | 
                Set<String> initExpPolSet =  | 
 | 
                    Collections.singleton(PolicyChecker.ANY_POLICY);  | 
 | 
 | 
 | 
                PolicyNodeImpl rootNode = new PolicyNodeImpl(null,  | 
 | 
                    PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);  | 
 | 
 | 
 | 
                List<PKIXCertPathChecker> checkers = new ArrayList<>();  | 
 | 
                PolicyChecker policyChecker  | 
 | 
                    = new PolicyChecker(buildParams.initialPolicies(),  | 
 | 
                                        appendedCerts.size(),  | 
 | 
                                        buildParams.explicitPolicyRequired(),  | 
 | 
                                        buildParams.policyMappingInhibited(),  | 
 | 
                                        buildParams.anyPolicyInhibited(),  | 
 | 
                                        buildParams.policyQualifiersRejected(),  | 
 | 
                                        rootNode);  | 
 | 
                checkers.add(policyChecker);  | 
 | 
 | 
 | 
                  | 
 | 
                checkers.add(new AlgorithmChecker(builder.trustAnchor,  | 
 | 
                        buildParams.date(), buildParams.variant()));  | 
 | 
 | 
 | 
                BasicChecker basicChecker = null;  | 
 | 
                if (nextState.keyParamsNeeded()) { | 
 | 
                    PublicKey rootKey = cert.getPublicKey();  | 
 | 
                    if (builder.trustAnchor.getTrustedCert() == null) { | 
 | 
                        rootKey = builder.trustAnchor.getCAPublicKey();  | 
 | 
                        if (debug != null)  | 
 | 
                            debug.println(  | 
 | 
                                "SunCertPathBuilder.depthFirstSearchForward " +  | 
 | 
                                "using buildParams public key: " +  | 
 | 
                                rootKey.toString());  | 
 | 
                    }  | 
 | 
                    TrustAnchor anchor = new TrustAnchor  | 
 | 
                        (cert.getSubjectX500Principal(), rootKey, null);  | 
 | 
 | 
 | 
                      | 
 | 
                    basicChecker = new BasicChecker(anchor, buildParams.date(),  | 
 | 
                                                    buildParams.sigProvider(),  | 
 | 
                                                    true);  | 
 | 
                    checkers.add(basicChecker);  | 
 | 
                }  | 
 | 
 | 
 | 
                buildParams.setCertPath(cf.generateCertPath(appendedCerts));  | 
 | 
 | 
 | 
                boolean revCheckerAdded = false;  | 
 | 
                List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers();  | 
 | 
                for (PKIXCertPathChecker ckr : ckrs) { | 
 | 
                    if (ckr instanceof PKIXRevocationChecker) { | 
 | 
                        if (revCheckerAdded) { | 
 | 
                            throw new CertPathValidatorException(  | 
 | 
                                "Only one PKIXRevocationChecker can be specified");  | 
 | 
                        }  | 
 | 
                        revCheckerAdded = true;  | 
 | 
                          | 
 | 
                        if (ckr instanceof RevocationChecker) { | 
 | 
                            ((RevocationChecker)ckr).init(builder.trustAnchor,  | 
 | 
                                                          buildParams);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                // only add a RevocationChecker if revocation is enabled and  | 
 | 
                  | 
 | 
                if (buildParams.revocationEnabled() && !revCheckerAdded) { | 
 | 
                    checkers.add(new RevocationChecker(builder.trustAnchor,  | 
 | 
                                                       buildParams));  | 
 | 
                }  | 
 | 
 | 
 | 
                checkers.addAll(ckrs);  | 
 | 
 | 
 | 
                // Why we don't need BasicChecker and RevocationChecker  | 
 | 
                // if nextState.keyParamsNeeded() is false?  | 
 | 
 | 
 | 
                for (int i = 0; i < appendedCerts.size(); i++) { | 
 | 
                    X509Certificate currCert = appendedCerts.get(i);  | 
 | 
                    if (debug != null)  | 
 | 
                        debug.println("current subject = " | 
 | 
                                      + currCert.getSubjectX500Principal());  | 
 | 
                    Set<String> unresCritExts =  | 
 | 
                        currCert.getCriticalExtensionOIDs();  | 
 | 
                    if (unresCritExts == null) { | 
 | 
                        unresCritExts = Collections.<String>emptySet();  | 
 | 
                    }  | 
 | 
 | 
 | 
                    for (PKIXCertPathChecker currChecker : checkers) { | 
 | 
                        if (!currChecker.isForwardCheckingSupported()) { | 
 | 
                            if (i == 0) { | 
 | 
                                currChecker.init(false);  | 
 | 
 | 
 | 
                                // The user specified  | 
 | 
                                // AlgorithmChecker may not be  | 
 | 
                                  | 
 | 
                                if (currChecker instanceof AlgorithmChecker) { | 
 | 
                                    ((AlgorithmChecker)currChecker).  | 
 | 
                                        trySetTrustAnchor(builder.trustAnchor);  | 
 | 
                                }  | 
 | 
                            }  | 
 | 
 | 
 | 
                            try { | 
 | 
                                currChecker.check(currCert, unresCritExts);  | 
 | 
                            } catch (CertPathValidatorException cpve) { | 
 | 
                                if (debug != null)  | 
 | 
                                    debug.println  | 
 | 
                                    ("SunCertPathBuilder.depthFirstSearchForward(): " + | 
 | 
                                    "final verification failed: " + cpve);  | 
 | 
                                // If the target cert itself is revoked, we  | 
 | 
                                  | 
 | 
                                if (buildParams.targetCertConstraints().match(currCert)  | 
 | 
                                        && cpve.getReason() == BasicReason.REVOKED) { | 
 | 
                                    throw cpve;  | 
 | 
                                }  | 
 | 
                                vertex.setThrowable(cpve);  | 
 | 
                                continue vertices;  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
 | 
 | 
                      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                     */  | 
 | 
                    for (PKIXCertPathChecker checker :  | 
 | 
                         buildParams.certPathCheckers())  | 
 | 
                    { | 
 | 
                        if (checker.isForwardCheckingSupported()) { | 
 | 
                            Set<String> suppExts =  | 
 | 
                                checker.getSupportedExtensions();  | 
 | 
                            if (suppExts != null) { | 
 | 
                                unresCritExts.removeAll(suppExts);  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
 | 
 | 
                    if (!unresCritExts.isEmpty()) { | 
 | 
                        unresCritExts.remove(BasicConstraints_Id.toString());  | 
 | 
                        unresCritExts.remove(NameConstraints_Id.toString());  | 
 | 
                        unresCritExts.remove(CertificatePolicies_Id.toString());  | 
 | 
                        unresCritExts.remove(PolicyMappings_Id.toString());  | 
 | 
                        unresCritExts.remove(PolicyConstraints_Id.toString());  | 
 | 
                        unresCritExts.remove(InhibitAnyPolicy_Id.toString());  | 
 | 
                        unresCritExts.remove(  | 
 | 
                            SubjectAlternativeName_Id.toString());  | 
 | 
                        unresCritExts.remove(KeyUsage_Id.toString());  | 
 | 
                        unresCritExts.remove(ExtendedKeyUsage_Id.toString());  | 
 | 
 | 
 | 
                        if (!unresCritExts.isEmpty()) { | 
 | 
                            throw new CertPathValidatorException  | 
 | 
                                ("unrecognized critical extension(s)", null, | 
 | 
                                 null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                if (debug != null)  | 
 | 
                    debug.println("SunCertPathBuilder.depthFirstSearchForward()" | 
 | 
                        + ": final verification succeeded - path completed!");  | 
 | 
                pathCompleted = true;  | 
 | 
 | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (builder.trustAnchor.getTrustedCert() == null)  | 
 | 
                    builder.addCertToPath(cert, cpList);  | 
 | 
                  | 
 | 
                this.trustAnchor = builder.trustAnchor;  | 
 | 
 | 
 | 
                  | 
 | 
 | 
 | 
                 */  | 
 | 
                if (basicChecker != null) { | 
 | 
                    finalPublicKey = basicChecker.getPublicKey();  | 
 | 
                } else { | 
 | 
                    Certificate finalCert;  | 
 | 
                    if (cpList.isEmpty()) { | 
 | 
                        finalCert = builder.trustAnchor.getTrustedCert();  | 
 | 
                    } else { | 
 | 
                        finalCert = cpList.getLast();  | 
 | 
                    }  | 
 | 
                    finalPublicKey = finalCert.getPublicKey();  | 
 | 
                }  | 
 | 
 | 
 | 
                policyTreeResult = policyChecker.getPolicyTree();  | 
 | 
                return;  | 
 | 
            } else { | 
 | 
                builder.addCertToPath(cert, cpList);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            nextState.updateState(cert);  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            adjList.add(new LinkedList<Vertex>());  | 
 | 
            vertex.setIndex(adjList.size() - 1);  | 
 | 
 | 
 | 
              | 
 | 
            depthFirstSearchForward(cert.getIssuerX500Principal(), nextState,  | 
 | 
                                    builder, adjList, cpList);  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
             */  | 
 | 
            if (pathCompleted) { | 
 | 
                return;  | 
 | 
            } else { | 
 | 
                  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                 */  | 
 | 
                if (debug != null)  | 
 | 
                    debug.println("SunCertPathBuilder.depthFirstSearchForward()" | 
 | 
                                  + ": backtracking");  | 
 | 
                builder.removeFinalCertFromPath(cpList);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static List<Vertex> addVertices(Collection<X509Certificate> certs,  | 
 | 
                                            List<List<Vertex>> adjList)  | 
 | 
    { | 
 | 
        List<Vertex> l = adjList.get(adjList.size() - 1);  | 
 | 
 | 
 | 
        for (X509Certificate cert : certs) { | 
 | 
            Vertex v = new Vertex(cert);  | 
 | 
            l.add(v);  | 
 | 
        }  | 
 | 
 | 
 | 
        return l;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static boolean anchorIsTarget(TrustAnchor anchor,  | 
 | 
                                          CertSelector sel)  | 
 | 
    { | 
 | 
        X509Certificate anchorCert = anchor.getTrustedCert();  | 
 | 
        if (anchorCert != null) { | 
 | 
            return sel.match(anchorCert);  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
}  |