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