0N/A/*
4756N/A * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider.certpath;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.security.GeneralSecurityException;
0N/Aimport java.security.Principal;
0N/Aimport java.security.cert.CertificateException;
0N/Aimport java.security.cert.CertPathValidatorException;
0N/Aimport java.security.cert.CertStore;
0N/Aimport java.security.cert.CertStoreException;
0N/Aimport java.security.cert.PKIXBuilderParameters;
0N/Aimport java.security.cert.PKIXCertPathChecker;
0N/Aimport java.security.cert.PKIXParameters;
585N/Aimport java.security.cert.PKIXReason;
0N/Aimport java.security.cert.TrustAnchor;
585N/Aimport java.security.cert.X509Certificate;
0N/Aimport java.security.cert.X509CertSelector;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.Collection;
0N/Aimport java.util.Collections;
0N/Aimport java.util.Comparator;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.List;
0N/Aimport java.util.LinkedList;
0N/Aimport java.util.Set;
0N/A
0N/Aimport javax.security.auth.x500.X500Principal;
0N/A
0N/Aimport sun.security.util.Debug;
0N/Aimport sun.security.x509.Extension;
0N/Aimport sun.security.x509.PKIXExtensions;
0N/Aimport sun.security.x509.X500Name;
0N/Aimport sun.security.x509.X509CertImpl;
0N/Aimport sun.security.x509.PolicyMappingsExtension;
0N/A
0N/A/**
0N/A * This class represents a reverse builder, which is able to retrieve
0N/A * matching certificates from CertStores and verify a particular certificate
0N/A * against a ReverseState.
0N/A *
0N/A * @since 1.4
0N/A * @author Sean Mullan
0N/A * @author Yassir Elley
0N/A */
0N/A
0N/Aclass ReverseBuilder extends Builder {
0N/A
0N/A private Debug debug = Debug.getInstance("certpath");
0N/A
0N/A Set<String> initPolicies;
0N/A
0N/A /**
0N/A * Initialize the builder with the input parameters.
0N/A *
0N/A * @param params the parameter set used to build a certification path
0N/A */
0N/A ReverseBuilder(PKIXBuilderParameters buildParams,
0N/A X500Principal targetSubjectDN) {
0N/A
0N/A super(buildParams, targetSubjectDN);
0N/A
0N/A Set<String> initialPolicies = buildParams.getInitialPolicies();
0N/A initPolicies = new HashSet<String>();
0N/A if (initialPolicies.isEmpty()) {
0N/A // if no initialPolicies are specified by user, set
0N/A // initPolicies to be anyPolicy by default
0N/A initPolicies.add(PolicyChecker.ANY_POLICY);
0N/A } else {
0N/A for (String policy : initialPolicies) {
0N/A initPolicies.add(policy);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Retrieves all certs from the specified CertStores that satisfy the
0N/A * requirements specified in the parameters and the current
0N/A * PKIX state (name constraints, policy constraints, etc).
0N/A *
0N/A * @param currentState the current state.
0N/A * Must be an instance of <code>ReverseState</code>
0N/A * @param certStores list of CertStores
0N/A */
0N/A Collection<X509Certificate> getMatchingCerts
0N/A (State currState, List<CertStore> certStores)
0N/A throws CertStoreException, CertificateException, IOException
0N/A {
0N/A ReverseState currentState = (ReverseState) currState;
0N/A
0N/A if (debug != null)
0N/A debug.println("In ReverseBuilder.getMatchingCerts.");
0N/A
0N/A /*
0N/A * The last certificate could be an EE or a CA certificate
0N/A * (we may be building a partial certification path or
0N/A * establishing trust in a CA).
0N/A *
0N/A * Try the EE certs before the CA certs. It will be more
0N/A * common to build a path to an end entity.
0N/A */
0N/A Collection<X509Certificate> certs =
0N/A getMatchingEECerts(currentState, certStores);
0N/A certs.addAll(getMatchingCACerts(currentState, certStores));
0N/A
0N/A return certs;
0N/A }
0N/A
0N/A /*
0N/A * Retrieves all end-entity certificates which satisfy constraints
0N/A * and requirements specified in the parameters and PKIX state.
0N/A */
0N/A private Collection<X509Certificate> getMatchingEECerts
0N/A (ReverseState currentState, List<CertStore> certStores)
0N/A throws CertStoreException, CertificateException, IOException {
0N/A
0N/A /*
0N/A * Compose a CertSelector to filter out
0N/A * certs which do not satisfy requirements.
0N/A *
0N/A * First, retrieve clone of current target cert constraints,
0N/A * and then add more selection criteria based on current validation state.
0N/A */
0N/A X509CertSelector sel = (X509CertSelector) targetCertConstraints.clone();
0N/A
0N/A /*
0N/A * Match on issuer (subject of previous cert)
0N/A */
0N/A sel.setIssuer(currentState.subjectDN);
0N/A
0N/A /*
0N/A * Match on certificate validity date.
0N/A */
0N/A sel.setCertificateValid(date);
0N/A
0N/A /*
0N/A * Policy processing optimizations
0N/A */
0N/A if (currentState.explicitPolicy == 0)
0N/A sel.setPolicy(getMatchingPolicies());
0N/A
0N/A /*
0N/A * If previous cert has a subject key identifier extension,
0N/A * use it to match on authority key identifier extension.
0N/A */
0N/A /*if (currentState.subjKeyId != null) {
0N/A AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
0N/A (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
0N/A null, null);
0N/A sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
0N/A }*/
0N/A
0N/A /*
0N/A * Require EE certs
0N/A */
0N/A sel.setBasicConstraints(-2);
0N/A
0N/A /* Retrieve matching certs from CertStores */
0N/A HashSet<X509Certificate> eeCerts = new HashSet<X509Certificate>();
0N/A addMatchingCerts(sel, certStores, eeCerts, true);
0N/A
0N/A if (debug != null) {
0N/A debug.println("ReverseBuilder.getMatchingEECerts got " + eeCerts.size()
0N/A + " certs.");
0N/A }
0N/A return eeCerts;
0N/A }
0N/A
0N/A /*
0N/A * Retrieves all CA certificates which satisfy constraints
0N/A * and requirements specified in the parameters and PKIX state.
0N/A */
0N/A private Collection<X509Certificate> getMatchingCACerts
0N/A (ReverseState currentState, List<CertStore> certStores)
0N/A throws CertificateException, CertStoreException, IOException {
0N/A
0N/A /*
0N/A * Compose a CertSelector to filter out
0N/A * certs which do not satisfy requirements.
0N/A */
0N/A X509CertSelector sel = new X509CertSelector();
0N/A
0N/A /*
0N/A * Match on issuer (subject of previous cert)
0N/A */
0N/A sel.setIssuer(currentState.subjectDN);
0N/A
0N/A /*
0N/A * Match on certificate validity date.
0N/A */
0N/A sel.setCertificateValid(date);
0N/A
0N/A /*
0N/A * Match on target subject name (checks that current cert's
0N/A * name constraints permit it to certify target).
0N/A * (4 is the integer type for DIRECTORY name).
0N/A */
0N/A sel.addPathToName(4, targetCertConstraints.getSubjectAsBytes());
0N/A
0N/A /*
0N/A * Policy processing optimizations
0N/A */
0N/A if (currentState.explicitPolicy == 0)
0N/A sel.setPolicy(getMatchingPolicies());
0N/A
0N/A /*
0N/A * If previous cert has a subject key identifier extension,
0N/A * use it to match on authority key identifier extension.
0N/A */
0N/A /*if (currentState.subjKeyId != null) {
0N/A AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
0N/A (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
0N/A null, null);
0N/A sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
0N/A }*/
0N/A
0N/A /*
0N/A * Require CA certs
0N/A */
0N/A sel.setBasicConstraints(0);
0N/A
0N/A /* Retrieve matching certs from CertStores */
0N/A ArrayList<X509Certificate> reverseCerts =
0N/A new ArrayList<X509Certificate>();
0N/A addMatchingCerts(sel, certStores, reverseCerts, true);
0N/A
0N/A /* Sort remaining certs using name constraints */
0N/A Collections.sort(reverseCerts, new PKIXCertComparator());
0N/A
0N/A if (debug != null)
0N/A debug.println("ReverseBuilder.getMatchingCACerts got " +
0N/A reverseCerts.size() + " certs.");
0N/A return reverseCerts;
0N/A }
0N/A
0N/A /*
0N/A * This inner class compares 2 PKIX certificates according to which
0N/A * should be tried first when building a path to the target. For
0N/A * now, the algorithm is to look at name constraints in each cert and those
0N/A * which constrain the path closer to the target should be
0N/A * ranked higher. Later, we may want to consider other components,
0N/A * such as key identifiers.
0N/A */
0N/A class PKIXCertComparator implements Comparator<X509Certificate> {
0N/A
0N/A private Debug debug = Debug.getInstance("certpath");
0N/A
0N/A public int compare(X509Certificate cert1, X509Certificate cert2) {
0N/A
0N/A /*
0N/A * if either cert certifies the target, always
0N/A * put at head of list.
0N/A */
0N/A if (cert1.getSubjectX500Principal().equals(targetSubjectDN)) {
0N/A return -1;
0N/A }
0N/A if (cert2.getSubjectX500Principal().equals(targetSubjectDN)) {
0N/A return 1;
0N/A }
0N/A
0N/A int targetDist1;
0N/A int targetDist2;
0N/A try {
0N/A X500Name targetSubjectName = X500Name.asX500Name(targetSubjectDN);
0N/A targetDist1 = Builder.targetDistance(
0N/A null, cert1, targetSubjectName);
0N/A targetDist2 = Builder.targetDistance(
0N/A null, cert2, targetSubjectName);
0N/A } catch (IOException e) {
0N/A if (debug != null) {
0N/A debug.println("IOException in call to Builder.targetDistance");
0N/A e.printStackTrace();
0N/A }
0N/A throw new ClassCastException
0N/A ("Invalid target subject distinguished name");
0N/A }
0N/A
0N/A if (targetDist1 == targetDist2)
0N/A return 0;
0N/A
0N/A if (targetDist1 == -1)
0N/A return 1;
0N/A
0N/A if (targetDist1 < targetDist2)
0N/A return -1;
0N/A
0N/A return 1;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Verifies a matching certificate.
0N/A *
0N/A * This method executes any of the validation steps in the PKIX path validation
0N/A * algorithm which were not satisfied via filtering out non-compliant
0N/A * certificates with certificate matching rules.
0N/A *
0N/A * If the last certificate is being verified (the one whose subject
0N/A * matches the target subject, then the steps in Section 6.1.4 of the
0N/A * Certification Path Validation algorithm are NOT executed,
0N/A * regardless of whether or not the last cert is an end-entity
0N/A * cert or not. This allows callers to certify CA certs as
0N/A * well as EE certs.
0N/A *
0N/A * @param cert the certificate to be verified
0N/A * @param currentState the current state against which the cert is verified
0N/A * @param certPathList the certPathList generated thus far
0N/A */
0N/A void verifyCert(X509Certificate cert, State currState,
0N/A List<X509Certificate> certPathList)
0N/A throws GeneralSecurityException
0N/A {
0N/A if (debug != null) {
0N/A debug.println("ReverseBuilder.verifyCert(SN: "
0N/A + Debug.toHexString(cert.getSerialNumber())
0N/A + "\n Subject: " + cert.getSubjectX500Principal() + ")");
0N/A }
0N/A
0N/A ReverseState currentState = (ReverseState) currState;
0N/A
0N/A /* we don't perform any validation of the trusted cert */
0N/A if (currentState.isInitial()) {
0N/A return;
0N/A }
0N/A
4756N/A // Don't bother to verify untrusted certificate more.
4756N/A currentState.untrustedChecker.check(cert,
4756N/A Collections.<String>emptySet());
4756N/A
0N/A /*
0N/A * check for looping - abort a loop if
0N/A * ((we encounter the same certificate twice) AND
0N/A * ((policyMappingInhibited = true) OR (no policy mapping
0N/A * extensions can be found between the occurences of the same
0N/A * certificate)))
0N/A * in order to facilitate the check to see if there are
0N/A * any policy mapping extensions found between the occurences
0N/A * of the same certificate, we reverse the certpathlist first
0N/A */
0N/A if ((certPathList != null) && (!certPathList.isEmpty())) {
0N/A List<X509Certificate> reverseCertList =
0N/A new ArrayList<X509Certificate>();
0N/A for (X509Certificate c : certPathList) {
0N/A reverseCertList.add(0, c);
0N/A }
0N/A
0N/A boolean policyMappingFound = false;
0N/A for (X509Certificate cpListCert : reverseCertList) {
0N/A X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert);
0N/A PolicyMappingsExtension policyMappingsExt =
0N/A cpListCertImpl.getPolicyMappingsExtension();
0N/A if (policyMappingsExt != null) {
0N/A policyMappingFound = true;
0N/A }
0N/A if (debug != null)
0N/A debug.println("policyMappingFound = " + policyMappingFound);
0N/A if (cert.equals(cpListCert)){
0N/A if ((buildParams.isPolicyMappingInhibited()) ||
0N/A (!policyMappingFound)){
0N/A if (debug != null)
0N/A debug.println("loop detected!!");
0N/A throw new CertPathValidatorException("loop detected");
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /* check if target cert */
0N/A boolean finalCert = cert.getSubjectX500Principal().equals(targetSubjectDN);
0N/A
0N/A /* check if CA cert */
0N/A boolean caCert = (cert.getBasicConstraints() != -1 ? true : false);
0N/A
0N/A /* if there are more certs to follow, verify certain constraints */
0N/A if (!finalCert) {
0N/A
0N/A /* check if CA cert */
0N/A if (!caCert)
0N/A throw new CertPathValidatorException("cert is NOT a CA cert");
0N/A
0N/A /* If the certificate was not self-issued, verify that
0N/A * remainingCerts is greater than zero
0N/A */
0N/A if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) {
0N/A throw new CertPathValidatorException
585N/A ("pathLenConstraint violated, path too long", null,
585N/A null, -1, PKIXReason.PATH_TOO_LONG);
0N/A }
0N/A
0N/A /*
0N/A * Check keyUsage extension (only if CA cert and not final cert)
0N/A */
0N/A KeyChecker.verifyCAKeyUsage(cert);
0N/A
0N/A } else {
0N/A
0N/A /*
0N/A * If final cert, check that it satisfies specified target
0N/A * constraints
0N/A */
0N/A if (targetCertConstraints.match(cert) == false) {
0N/A throw new CertPathValidatorException("target certificate " +
0N/A "constraints check failed");
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Check revocation.
0N/A */
0N/A if (buildParams.isRevocationEnabled()) {
0N/A
0N/A currentState.crlChecker.check(cert,
0N/A currentState.pubKey,
0N/A currentState.crlSign);
0N/A }
0N/A
0N/A /* Check name constraints if this is not a self-issued cert */
0N/A if (finalCert || !X509CertImpl.isSelfIssued(cert)){
0N/A if (currentState.nc != null){
0N/A try {
0N/A if (!currentState.nc.verify(cert)){
0N/A throw new CertPathValidatorException
585N/A ("name constraints check failed", null, null, -1,
585N/A PKIXReason.INVALID_NAME);
0N/A }
0N/A } catch (IOException ioe){
0N/A throw new CertPathValidatorException(ioe);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Check policy
0N/A */
0N/A X509CertImpl certImpl = X509CertImpl.toImpl(cert);
0N/A currentState.rootNode = PolicyChecker.processPolicies
0N/A (currentState.certIndex, initPolicies,
0N/A currentState.explicitPolicy, currentState.policyMapping,
0N/A currentState.inhibitAnyPolicy,
0N/A buildParams.getPolicyQualifiersRejected(), currentState.rootNode,
0N/A certImpl, finalCert);
0N/A
0N/A /*
0N/A * Check CRITICAL private extensions
0N/A */
0N/A Set<String> unresolvedCritExts = cert.getCriticalExtensionOIDs();
0N/A if (unresolvedCritExts == null) {
0N/A unresolvedCritExts = Collections.<String>emptySet();
0N/A }
2999N/A
2999N/A /*
2999N/A * Check that the signature algorithm is not disabled.
2999N/A */
2999N/A currentState.algorithmChecker.check(cert, unresolvedCritExts);
2999N/A
0N/A for (PKIXCertPathChecker checker : currentState.userCheckers) {
0N/A checker.check(cert, unresolvedCritExts);
0N/A }
2999N/A
0N/A /*
0N/A * Look at the remaining extensions and remove any ones we have
0N/A * already checked. If there are any left, throw an exception!
0N/A */
0N/A if (!unresolvedCritExts.isEmpty()) {
0N/A unresolvedCritExts.remove(PKIXExtensions.BasicConstraints_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.NameConstraints_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.CertificatePolicies_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.PolicyMappings_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.PolicyConstraints_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.InhibitAnyPolicy_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.SubjectAlternativeName_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.KeyUsage_Id.toString());
0N/A unresolvedCritExts.remove(PKIXExtensions.ExtendedKeyUsage_Id.toString());
0N/A
0N/A if (!unresolvedCritExts.isEmpty())
585N/A throw new CertPathValidatorException
585N/A ("Unrecognized critical extension(s)", null, null, -1,
585N/A PKIXReason.UNRECOGNIZED_CRIT_EXT);
0N/A }
0N/A
0N/A /*
0N/A * Check signature.
0N/A */
0N/A if (buildParams.getSigProvider() != null) {
0N/A cert.verify(currentState.pubKey, buildParams.getSigProvider());
0N/A } else {
0N/A cert.verify(currentState.pubKey);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Verifies whether the input certificate completes the path.
0N/A * This checks whether the cert is the target certificate.
0N/A *
0N/A * @param cert the certificate to test
0N/A * @return a boolean value indicating whether the cert completes the path.
0N/A */
0N/A boolean isPathCompleted(X509Certificate cert) {
0N/A return cert.getSubjectX500Principal().equals(targetSubjectDN);
0N/A }
0N/A
0N/A /** Adds the certificate to the certPathList
0N/A *
0N/A * @param cert the certificate to be added
0N/A * @param certPathList the certification path list
0N/A */
0N/A void addCertToPath(X509Certificate cert,
0N/A LinkedList<X509Certificate> certPathList) {
0N/A certPathList.addLast(cert);
0N/A }
0N/A
0N/A /** Removes final certificate from the certPathList
0N/A *
0N/A * @param certPathList the certification path list
0N/A */
0N/A void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
0N/A certPathList.removeLast();
0N/A }
0N/A}