feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 2011, 2015, 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.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.X509CertSelector;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Date;
import sun.security.util.Debug;
import sun.security.util.DerInputStream;
import sun.security.x509.SerialNumber;
import sun.security.x509.AuthorityKeyIdentifierExtension;
/**
* An adaptable X509 certificate selector for forward certification path
* building. This selector overrides the default X509CertSelector matching
* rules for the subjectKeyIdentifier and serialNumber criteria, and adds
* additional rules for certificate validity.
*
* @since 1.7
*/
class AdaptableX509CertSelector extends X509CertSelector {
private static final Debug debug = Debug.getInstance("certpath");
// The start date of a validity period.
private Date startDate;
// The end date of a validity period.
private Date endDate;
// The subject key identifier
private byte[] ski;
// The serial number
private BigInteger serial;
/**
* Sets the criterion of the X509Certificate validity period.
*
* Normally, we may not have to check that a certificate validity period
* must fall within its issuer's certificate validity period. However,
* when we face root CA key updates for version 1 certificates, according
* to scheme of RFC 4210 or 2510, the validity periods should be checked
* to determine the right issuer's certificate.
*
* Conservatively, we will only check the validity periods for version
* 1 and version 2 certificates. For version 3 certificates, we can
* determine the right issuer by authority and subject key identifier
* extensions.
*
* @param startDate the start date of a validity period that must fall
* within the certificate validity period for the X509Certificate
* @param endDate the end date of a validity period that must fall
* within the certificate validity period for the X509Certificate
*/
void setValidityPeriod(Date startDate, Date endDate) {
this.startDate = startDate;
this.endDate = endDate;
}
/**
* This selector overrides the subjectKeyIdentifier matching rules of
* X509CertSelector, so it throws IllegalArgumentException if this method
* is ever called.
*/
@Override
public void setSubjectKeyIdentifier(byte[] subjectKeyID) {
throw new IllegalArgumentException();
}
/**
* This selector overrides the serialNumber matching rules of
* X509CertSelector, so it throws IllegalArgumentException if this method
* is ever called.
*/
@Override
public void setSerialNumber(BigInteger serial) {
throw new IllegalArgumentException();
}
/**
* Sets the subjectKeyIdentifier and serialNumber criteria from the
* authority key identifier extension.
*
* The subjectKeyIdentifier criterion is set to the keyIdentifier field
* of the extension, or null if it is empty. The serialNumber criterion
* is set to the authorityCertSerialNumber field, or null if it is empty.
*
* Note that we do not set the subject criterion to the
* authorityCertIssuer field of the extension. The caller MUST set
* the subject criterion before calling match().
*
* @param ext the authorityKeyIdentifier extension
* @throws IOException if there is an error parsing the extension
*/
void setSkiAndSerialNumber(AuthorityKeyIdentifierExtension ext)
throws IOException {
ski = null;
serial = null;
if (ext != null) {
ski = ext.getEncodedKeyIdentifier();
SerialNumber asn = (SerialNumber)ext.get(
AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
if (asn != null) {
serial = asn.getNumber();
}
// the subject criterion should be set by the caller
}
}
/**
* Decides whether a <code>Certificate</code> should be selected.
*
* This method overrides the matching rules for the subjectKeyIdentifier
* and serialNumber criteria and adds additional rules for certificate
* validity.
*
* For the purpose of compatibility, when a certificate is of
* version 1 and version 2, or the certificate does not include
* a subject key identifier extension, the selection criterion
* of subjectKeyIdentifier will be disabled.
*/
@Override
public boolean match(Certificate cert) {
X509Certificate xcert = (X509Certificate)cert;
// match subject key identifier
if (!matchSubjectKeyID(xcert)) {
return false;
}
// In practice, a CA may replace its root certificate and require that
// the existing certificate is still valid, even if the AKID extension
// does not match the replacement root certificate fields.
//
// Conservatively, we only support the replacement for version 1 and
// version 2 certificate. As for version 3, the certificate extension
// may contain sensitive information (for example, policies), the
// AKID need to be respected to seek the exact certificate in case
// of key or certificate abuse.
int version = xcert.getVersion();
if (serial != null && version > 2) {
if (!serial.equals(xcert.getSerialNumber())) {
return false;
}
}
// Check the validity period for version 1 and 2 certificate.
if (version < 3) {
if (startDate != null) {
try {
xcert.checkValidity(startDate);
} catch (CertificateException ce) {
return false;
}
}
if (endDate != null) {
try {
xcert.checkValidity(endDate);
} catch (CertificateException ce) {
return false;
}
}
}
if (!super.match(cert)) {
return false;
}
return true;
}
/*
* Match on subject key identifier extension value. These matching rules
* are identical to X509CertSelector except that if the certificate does
* not have a subject key identifier extension, it returns true.
*/
private boolean matchSubjectKeyID(X509Certificate xcert) {
if (ski == null) {
return true;
}
try {
byte[] extVal = xcert.getExtensionValue("2.5.29.14");
if (extVal == null) {
if (debug != null) {
debug.println("AdaptableX509CertSelector.match: "
+ "no subject key ID extension. Subject: "
+ xcert.getSubjectX500Principal());
}
return true;
}
DerInputStream in = new DerInputStream(extVal);
byte[] certSubjectKeyID = in.getOctetString();
if (certSubjectKeyID == null ||
!Arrays.equals(ski, certSubjectKeyID)) {
if (debug != null) {
debug.println("AdaptableX509CertSelector.match: "
+ "subject key IDs don't match. "
+ "Expected: " + Arrays.toString(ski) + " "
+ "Cert's: " + Arrays.toString(certSubjectKeyID));
}
return false;
}
} catch (IOException ex) {
if (debug != null) {
debug.println("AdaptableX509CertSelector.match: "
+ "exception in subject key ID check");
}
return false;
}
return true;
}
@Override
public Object clone() {
AdaptableX509CertSelector copy =
(AdaptableX509CertSelector)super.clone();
if (startDate != null) {
copy.startDate = (Date)startDate.clone();
}
if (endDate != null) {
copy.endDate = (Date)endDate.clone();
}
if (ski != null) {
copy.ski = ski.clone();
}
return copy;
}
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright (c) 2000, 2023, 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.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* An AdjacencyList is used to store the history of certification paths
* attempted in constructing a path from an initiator to a target. The
* AdjacencyList is initialized with a <code>List</code> of
* <code>List</code>s, where each sub-<code>List</code> contains objects of
* type <code>Vertex</code>. A <code>Vertex</code> describes one possible or
* actual step in the chain building process, and the associated
* <code>Certificate</code>. Specifically, a <code>Vertex</code> object
* contains a <code>Certificate</code> and an index value referencing the
* next sub-list in the process. If the index value is -1 then this
* <code>Vertex</code> doesn't continue the attempted build path.
* <p>
* Example:
* <p>
* Attempted Paths:<ul>
* <li>C1-&gt;C2-&gt;C3
* <li>C1-&gt;C4-&gt;C5
* <li>C1-&gt;C4-&gt;C6
* <li>C1-&gt;C4-&gt;C7
* <li>C1-&gt;C8-&gt;C9
* <li>C1-&gt;C10-&gt;C11
* </ul>
* <p>
* AdjacencyList structure:<ul>
* <li>AL[0] = C1,1
* <li>AL[1] = C2,2 =&gt;C4,3 =&gt;C8,4 =&gt;C10,5
* <li>AL[2] = C3,-1
* <li>AL[3] = C5,-1 =&gt;C6,-1 =&gt;C7,-1
* <li>AL[4] = C9,-1
* <li>AL[5] = C11,-1
* </ul>
* <p>
* The iterator method returns objects of type <code>BuildStep</code>, not
* objects of type <code>Vertex</code>.
* A <code>BuildStep</code> contains a <code>Vertex</code> and a result code,
* accessible via getResult method. There are five result values.
* <code>POSSIBLE</code> denotes that the current step represents a
* <code>Certificate</code> that the builder is considering at this point in
* the build. <code>FOLLOW</code> denotes a <code>Certificate</code> (one of
* those noted as <code>POSSIBLE</code>) that the builder is using to try
* extending the chain. <code>BACK</code> represents that a
* <code>FOLLOW</code> was incorrect, and is being removed from the chain.
* There is exactly one <code>FOLLOW</code> for each <code>BACK</code>. The
* values <code>SUCCEED</code> and <code>FAIL</code> mean that we've come to
* the end of the build process, and there will not be any more entries in
* the list.
* <p>
* @see sun.security.provider.certpath.BuildStep
* @see sun.security.provider.certpath.Vertex
* <p>
* @author seth proctor
* @since 1.4
*/
public class AdjacencyList {
// the actual set of steps the AdjacencyList represents
private ArrayList<BuildStep> mStepList;
// the original list
private List<List<Vertex>> mOrigList;
/**
* Constructs a new <code>AdjacencyList</code> based on the specified
* <code>List</code>. See the example above.
*
* @param list a <code>List</code> of <code>List</code>s of
* <code>Vertex</code> objects
*/
public AdjacencyList(List<List<Vertex>> list) {
mStepList = new ArrayList<BuildStep>();
mOrigList = list;
buildList(list, 0, null);
}
/**
* Gets an <code>Iterator</code> to iterate over the set of
* <code>BuildStep</code>s in build-order. Any attempts to change
* the list through the remove method will fail.
*
* @return an <code>Iterator</code> over the <code>BuildStep</code>s
*/
public Iterator<BuildStep> iterator() {
return Collections.unmodifiableList(mStepList).iterator();
}
/**
* Returns the number of attempted paths (useful for debugging).
*/
public int numAttemptedPaths() {
return mOrigList.size();
}
/**
* Recursive, private method which actually builds the step list from
* the given adjacency list. <code>Follow</code> is the parent BuildStep
* that we followed to get here, and if it's null, it means that we're
* at the start.
*/
private boolean buildList(List<List<Vertex>> theList, int index,
BuildStep follow) {
// Each time this method is called, we're examining a new list
// from the global list. So, we have to start by getting the list
// that contains the set of Vertexes we're considering.
List<Vertex> l = theList.get(index);
// we're interested in the case where all indexes are -1...
boolean allNegOne = true;
// ...and in the case where every entry has a Throwable
boolean allXcps = true;
for (Vertex v : l) {
if (v.getIndex() != -1) {
// count an empty list the same as an index of -1...this
// is to patch a bug somewhere in the builder
if (theList.get(v.getIndex()).size() != 0)
allNegOne = false;
} else {
if (v.getThrowable() == null)
allXcps = false;
}
// every entry, regardless of the final use for it, is always
// entered as a possible step before we take any actions
mStepList.add(new BuildStep(v, BuildStep.POSSIBLE));
}
if (allNegOne) {
// There are two cases that we could be looking at here. We
// may need to back up, or the build may have succeeded at
// this point. This is based on whether or not any
// exceptions were found in the list.
if (allXcps) {
// we need to go back...see if this is the last one
if (follow == null)
mStepList.add(new BuildStep(null, BuildStep.FAIL));
else
mStepList.add(new BuildStep(follow.getVertex(),
BuildStep.BACK));
return false;
} else {
// we succeeded...now the only question is which is the
// successful step? If there's only one entry without
// a throwable, then that's the successful step. Otherwise,
// we'll have to make some guesses...
List<Vertex> possibles = new ArrayList<>();
for (Vertex v : l) {
if (v.getThrowable() == null)
possibles.add(v);
}
if (possibles.size() == 1) {
// real easy...we've found the final Vertex
mStepList.add(new BuildStep(possibles.get(0),
BuildStep.SUCCEED));
} else {
// ok...at this point, there is more than one Cert
// which might be the succeed step...how do we know
// which it is? I'm going to assume that our builder
// algorithm is good enough to know which is the
// correct one, and put it first...but a FIXME goes
// here anyway, and we should be comparing to the
// target/initiator Cert...
mStepList.add(new BuildStep(possibles.get(0),
BuildStep.SUCCEED));
}
return true;
}
} else {
// There's at least one thing that we can try before we give
// up and go back. Run through the list now, and enter a new
// BuildStep for each path that we try to follow. If none of
// the paths we try produce a successful end, we're going to
// have to back out ourselves.
boolean success = false;
for (Vertex v : l) {
// Note that we'll only find a SUCCEED case when we're
// looking at the last possible path, so we don't need to
// consider success in the while loop
if (v.getIndex() != -1) {
if (theList.get(v.getIndex()).size() != 0) {
// If the entry we're looking at doesn't have an
// index of -1, and doesn't lead to an empty list,
// then it's something we follow!
BuildStep bs = new BuildStep(v, BuildStep.FOLLOW);
mStepList.add(bs);
success = buildList(theList, v.getIndex(), bs);
}
}
}
if (success) {
// We're already finished!
return true;
} else {
// We failed, and we've exhausted all the paths that we
// could take. The only choice is to back ourselves out.
if (follow == null)
mStepList.add(new BuildStep(null, BuildStep.FAIL));
else
mStepList.add(new BuildStep(follow.getVertex(),
BuildStep.BACK));
return false;
}
}
}
/**
* Prints out a string representation of this AdjacencyList.
*
* @return String representation
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[\n");
int i = 0;
for (List<Vertex> l : mOrigList) {
sb.append("LinkedList[").append(i++).append("]:\n");
for (Vertex step : l) {
sb.append(step.toString()).append("\n");
}
}
sb.append("]\n");
return sb.toString();
}
}

View File

@@ -0,0 +1,395 @@
/*
* Copyright (c) 2009, 2021, 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.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.EnumSet;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.KeyFactory;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.TrustAnchor;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.PKIXReason;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import sun.security.util.ConstraintsParameters;
import sun.security.util.Debug;
import sun.security.util.DisabledAlgorithmConstraints;
import sun.security.validator.Validator;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X509CertImpl;
/**
* A {@code PKIXCertPathChecker} implementation to check whether a
* specified certificate contains the required algorithm constraints.
* <p>
* Certificate fields such as the subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to
* the specified algorithm constraints.
*
* @see PKIXCertPathChecker
* @see PKIXParameters
*/
public final class AlgorithmChecker extends PKIXCertPathChecker {
private static final Debug debug = Debug.getInstance("certpath");
private final AlgorithmConstraints constraints;
private final PublicKey trustedPubKey;
private final Date date;
private PublicKey prevPubKey;
private final String variant;
private TrustAnchor anchor;
private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
private static final Set<CryptoPrimitive> KU_PRIMITIVE_SET =
Collections.unmodifiableSet(EnumSet.of(
CryptoPrimitive.SIGNATURE,
CryptoPrimitive.KEY_ENCAPSULATION,
CryptoPrimitive.PUBLIC_KEY_ENCRYPTION,
CryptoPrimitive.KEY_AGREEMENT));
private static final DisabledAlgorithmConstraints
certPathDefaultConstraints =
DisabledAlgorithmConstraints.certPathConstraints();
/**
* Create a new {@code AlgorithmChecker} with the given
* {@code TrustAnchor} and {@code String} variant.
*
* @param anchor the trust anchor selected to validate the target
* certificate
* @param variant the Validator variant of the operation. A null value
* passed will set it to Validator.GENERIC.
*/
public AlgorithmChecker(TrustAnchor anchor, String variant) {
this(anchor, certPathDefaultConstraints, null, variant);
}
/**
* Create a new {@code AlgorithmChecker} with the given
* {@code AlgorithmConstraints} and {@code String} variant.
*
* Note that this constructor can initialize a variation of situations where
* the AlgorithmConstraints or Variant maybe known.
*
* @param constraints the algorithm constraints (or null)
* @param variant the Validator variant of the operation. A null value
* passed will set it to Validator.GENERIC.
*/
public AlgorithmChecker(AlgorithmConstraints constraints, String variant) {
this(null, constraints, null, variant);
}
/**
* Create a new {@code AlgorithmChecker} with the
* given {@code TrustAnchor}, {@code AlgorithmConstraints}, {@code Date},
* and {@code String} variant.
*
* @param anchor the trust anchor selected to validate the target
* certificate
* @param constraints the algorithm constraints (or null)
* @param date the date specified by the PKIXParameters date, or the
* timestamp if JAR files are being validated and the
* JAR is timestamped. May be null if no timestamp or
* PKIXParameter date is set.
* @param variant the Validator variant of the operation. A null value
* passed will set it to Validator.GENERIC.
*/
public AlgorithmChecker(TrustAnchor anchor,
AlgorithmConstraints constraints, Date date, String variant) {
if (anchor != null) {
if (anchor.getTrustedCert() != null) {
this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
} else {
this.trustedPubKey = anchor.getCAPublicKey();
}
this.anchor = anchor;
} else {
this.trustedPubKey = null;
}
this.prevPubKey = this.trustedPubKey;
this.constraints = (constraints == null ? certPathDefaultConstraints :
constraints);
this.date = date;
this.variant = (variant == null ? Validator.VAR_GENERIC : variant);
}
/**
* Create a new {@code AlgorithmChecker} with the given {@code TrustAnchor},
* {@code PKIXParameter} date, and {@code variant}.
*
* @param anchor the trust anchor selected to validate the target
* certificate
* @param date the date specified by the PKIXParameters date, or the
* timestamp if JAR files are being validated and the
* JAR is timestamped. May be null if no timestamp or
* PKIXParameter date is set.
* @param variant the Validator variant of the operation. A null value
* passed will set it to Validator.GENERIC.
*/
public AlgorithmChecker(TrustAnchor anchor, Date date, String variant) {
this(anchor, certPathDefaultConstraints, date, variant);
}
@Override
public void init(boolean forward) throws CertPathValidatorException {
// Note that this class does not support forward mode.
if (!forward) {
if (trustedPubKey != null) {
prevPubKey = trustedPubKey;
} else {
prevPubKey = null;
}
} else {
throw new
CertPathValidatorException("forward checking not supported");
}
}
@Override
public boolean isForwardCheckingSupported() {
// Note that as this class does not support forward mode, the method
// will always returns false.
return false;
}
@Override
public Set<String> getSupportedExtensions() {
return null;
}
@Override
public void check(Certificate cert,
Collection<String> unresolvedCritExts)
throws CertPathValidatorException {
if (!(cert instanceof X509Certificate) || constraints == null) {
// ignore the check for non-x.509 certificate or null constraints
return;
}
// check the key usage and key size
boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage();
if (keyUsage != null && keyUsage.length < 9) {
throw new CertPathValidatorException(
"incorrect KeyUsage extension",
null, null, -1, PKIXReason.INVALID_KEY_USAGE);
}
X509CertImpl x509Cert;
AlgorithmId algorithmId;
try {
x509Cert = X509CertImpl.toImpl((X509Certificate)cert);
algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG);
} catch (CertificateException ce) {
throw new CertPathValidatorException(ce);
}
AlgorithmParameters currSigAlgParams = algorithmId.getParameters();
PublicKey currPubKey = cert.getPublicKey();
String currSigAlg = x509Cert.getSigAlgName();
// Check the signature algorithm and parameters against constraints.
if (!constraints.permits(SIGNATURE_PRIMITIVE_SET, currSigAlg,
currSigAlgParams)) {
throw new CertPathValidatorException(
"Algorithm constraints check failed on signature " +
"algorithm: " + currSigAlg, null, null, -1,
BasicReason.ALGORITHM_CONSTRAINED);
}
// Assume all key usage bits are set if key usage is not present
Set<CryptoPrimitive> primitives = KU_PRIMITIVE_SET;
if (keyUsage != null) {
primitives = EnumSet.noneOf(CryptoPrimitive.class);
if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) {
// keyUsage[0]: KeyUsage.digitalSignature
// keyUsage[1]: KeyUsage.nonRepudiation
// keyUsage[5]: KeyUsage.keyCertSign
// keyUsage[6]: KeyUsage.cRLSign
primitives.add(CryptoPrimitive.SIGNATURE);
}
if (keyUsage[2]) { // KeyUsage.keyEncipherment
primitives.add(CryptoPrimitive.KEY_ENCAPSULATION);
}
if (keyUsage[3]) { // KeyUsage.dataEncipherment
primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION);
}
if (keyUsage[4]) { // KeyUsage.keyAgreement
primitives.add(CryptoPrimitive.KEY_AGREEMENT);
}
// KeyUsage.encipherOnly and KeyUsage.decipherOnly are
// undefined in the absence of the keyAgreement bit.
if (primitives.isEmpty()) {
throw new CertPathValidatorException(
"incorrect KeyUsage extension bits",
null, null, -1, PKIXReason.INVALID_KEY_USAGE);
}
}
ConstraintsParameters cp =
new CertPathConstraintsParameters(x509Cert, variant,
anchor, date);
// Check against local constraints if it is DisabledAlgorithmConstraints
if (constraints instanceof DisabledAlgorithmConstraints) {
((DisabledAlgorithmConstraints)constraints).permits(currSigAlg,
currSigAlgParams, cp, true);
// DisabledAlgorithmsConstraints does not check primitives, so key
// additional key check.
} else {
// Perform the default constraints checking anyway.
certPathDefaultConstraints.permits(currSigAlg, currSigAlgParams, cp, true);
// Call locally set constraints to check key with primitives.
if (!constraints.permits(primitives, currPubKey)) {
throw new CertPathValidatorException(
"Algorithm constraints check failed on key " +
currPubKey.getAlgorithm() + " with size of " +
sun.security.util.KeyUtil.getKeySize(currPubKey) +
"bits",
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
}
// If there is no previous key, set one and exit
if (prevPubKey == null) {
prevPubKey = currPubKey;
return;
}
// Check with previous cert for signature algorithm and public key
if (!constraints.permits(
SIGNATURE_PRIMITIVE_SET,
currSigAlg, prevPubKey, currSigAlgParams)) {
throw new CertPathValidatorException(
"Algorithm constraints check failed on " +
"signature algorithm: " + currSigAlg,
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
// Inherit key parameters from previous key
if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) {
// Inherit DSA parameters from previous key
if (!(prevPubKey instanceof DSAPublicKey)) {
throw new CertPathValidatorException("Input key is not " +
"of a appropriate type for inheriting parameters");
}
DSAParams params = ((DSAPublicKey)prevPubKey).getParams();
if (params == null) {
throw new CertPathValidatorException(
"Key parameters missing from public key.");
}
try {
BigInteger y = ((DSAPublicKey)currPubKey).getY();
KeyFactory kf = KeyFactory.getInstance("DSA");
DSAPublicKeySpec ks = new DSAPublicKeySpec(y, params.getP(),
params.getQ(), params.getG());
currPubKey = kf.generatePublic(ks);
} catch (GeneralSecurityException e) {
throw new CertPathValidatorException("Unable to generate " +
"key with inherited parameters: " + e.getMessage(), e);
}
}
// reset the previous public key
prevPubKey = currPubKey;
}
/**
* Try to set the trust anchor of the checker.
* <p>
* If there is no trust anchor specified and the checker has not started,
* set the trust anchor.
*
* @param anchor the trust anchor selected to validate the target
* certificate
*/
void trySetTrustAnchor(TrustAnchor anchor) {
// Don't bother if the check has started or trust anchor has already
// specified.
if (prevPubKey == null) {
if (anchor == null) {
throw new IllegalArgumentException(
"The trust anchor cannot be null");
}
// Don't bother to change the trustedPubKey.
if (anchor.getTrustedCert() != null) {
prevPubKey = anchor.getTrustedCert().getPublicKey();
} else {
prevPubKey = anchor.getCAPublicKey();
}
this.anchor = anchor;
}
}
/**
* Check the signature algorithm with the specified public key.
*
* @param key the public key to verify the CRL signature
* @param algorithmId signature algorithm Algorithm ID
* @param variant the Validator variant of the operation. A null
* value passed will set it to Validator.GENERIC.
* @param anchor the trust anchor selected to validate the public key
*/
static void check(PublicKey key, AlgorithmId algorithmId, String variant,
TrustAnchor anchor) throws CertPathValidatorException {
certPathDefaultConstraints.permits(algorithmId.getName(),
algorithmId.getParameters(),
new CertPathConstraintsParameters(key, variant, anchor), true);
}
}

View File

@@ -0,0 +1,302 @@
/*
* 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.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.X509Certificate;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXReason;
import java.security.cert.TrustAnchor;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import javax.security.auth.x500.X500Principal;
import sun.security.x509.X500Name;
import sun.security.util.Debug;
/**
* BasicChecker is a PKIXCertPathChecker that checks the basic information
* on a PKIX certificate, namely the signature, validity, and subject/issuer
* name chaining.
*
* @since 1.4
* @author Yassir Elley
*/
class BasicChecker extends PKIXCertPathChecker {
private static final Debug debug = Debug.getInstance("certpath");
private final PublicKey trustedPubKey;
private final X500Principal caName;
private final Date date;
private final String sigProvider;
private final boolean sigOnly;
private X500Principal prevSubject;
private PublicKey prevPubKey;
/**
* Constructor that initializes the input parameters.
*
* @param anchor the anchor selected to validate the target certificate
* @param testDate the time for which the validity of the certificate
* should be determined
* @param sigProvider the name of the signature provider
* @param sigOnly true if only signature checking is to be done;
* if false, all checks are done
*/
BasicChecker(TrustAnchor anchor, Date date, String sigProvider,
boolean sigOnly) {
if (anchor.getTrustedCert() != null) {
this.trustedPubKey = anchor.getTrustedCert().getPublicKey();
this.caName = anchor.getTrustedCert().getSubjectX500Principal();
} else {
this.trustedPubKey = anchor.getCAPublicKey();
this.caName = anchor.getCA();
}
this.date = date;
this.sigProvider = sigProvider;
this.sigOnly = sigOnly;
this.prevPubKey = trustedPubKey;
}
/**
* Initializes the internal state of the checker from parameters
* specified in the constructor.
*/
@Override
public void init(boolean forward) throws CertPathValidatorException {
if (!forward) {
prevPubKey = trustedPubKey;
if (PKIX.isDSAPublicKeyWithoutParams(prevPubKey)) {
// If TrustAnchor is a DSA public key and it has no params, it
// cannot be used to verify the signature of the first cert,
// so throw exception
throw new CertPathValidatorException("Key parameters missing");
}
prevSubject = caName;
} else {
throw new
CertPathValidatorException("forward checking not supported");
}
}
@Override
public boolean isForwardCheckingSupported() {
return false;
}
@Override
public Set<String> getSupportedExtensions() {
return null;
}
/**
* Performs the signature, validity, and subject/issuer name chaining
* checks on the certificate using its internal state. This method does
* not remove any critical extensions from the Collection.
*
* @param cert the Certificate
* @param unresolvedCritExts a Collection of the unresolved critical
* extensions
* @throws CertPathValidatorException if certificate does not verify
*/
@Override
public void check(Certificate cert, Collection<String> unresolvedCritExts)
throws CertPathValidatorException
{
X509Certificate currCert = (X509Certificate)cert;
if (!sigOnly) {
verifyValidity(currCert);
verifyNameChaining(currCert);
}
verifySignature(currCert);
updateState(currCert);
}
/**
* Verifies the signature on the certificate using the previous public key.
*
* @param cert the X509Certificate
* @throws CertPathValidatorException if certificate does not verify
*/
private void verifySignature(X509Certificate cert)
throws CertPathValidatorException
{
String msg = "signature";
if (debug != null)
debug.println("---checking " + msg + "...");
try {
cert.verify(prevPubKey, sigProvider);
} catch (SignatureException e) {
throw new CertPathValidatorException
(msg + " check failed", e, null, -1,
BasicReason.INVALID_SIGNATURE);
} catch (GeneralSecurityException e) {
throw new CertPathValidatorException(msg + " check failed", e);
}
if (debug != null)
debug.println(msg + " verified.");
}
/**
* Internal method to verify the validity on a certificate
*/
private void verifyValidity(X509Certificate cert)
throws CertPathValidatorException
{
String msg = "validity";
if (debug != null)
debug.println("---checking " + msg + ":" + date.toString() + "...");
try {
cert.checkValidity(date);
} catch (CertificateExpiredException e) {
throw new CertPathValidatorException
(msg + " check failed", e, null, -1, BasicReason.EXPIRED);
} catch (CertificateNotYetValidException e) {
throw new CertPathValidatorException
(msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID);
}
if (debug != null)
debug.println(msg + " verified.");
}
/**
* Internal method to check that cert has a valid DN to be next in a chain
*/
private void verifyNameChaining(X509Certificate cert)
throws CertPathValidatorException
{
if (prevSubject != null) {
String msg = "subject/issuer name chaining";
if (debug != null)
debug.println("---checking " + msg + "...");
X500Principal currIssuer = cert.getIssuerX500Principal();
// reject null or empty issuer DNs
if (X500Name.asX500Name(currIssuer).isEmpty()) {
throw new CertPathValidatorException
(msg + " check failed: " +
"empty/null issuer DN in certificate is invalid", null,
null, -1, PKIXReason.NAME_CHAINING);
}
if (!(currIssuer.equals(prevSubject))) {
throw new CertPathValidatorException
(msg + " check failed", null, null, -1,
PKIXReason.NAME_CHAINING);
}
if (debug != null)
debug.println(msg + " verified.");
}
}
/**
* Internal method to manage state information at each iteration
*/
private void updateState(X509Certificate currCert)
throws CertPathValidatorException
{
PublicKey cKey = currCert.getPublicKey();
if (debug != null) {
debug.println("BasicChecker.updateState issuer: " +
currCert.getIssuerX500Principal().toString() + "; subject: " +
currCert.getSubjectX500Principal() + "; serial#: " +
currCert.getSerialNumber().toString());
}
if (PKIX.isDSAPublicKeyWithoutParams(cKey)) {
// cKey needs to inherit DSA parameters from prev key
cKey = makeInheritedParamsKey(cKey, prevPubKey);
if (debug != null) debug.println("BasicChecker.updateState Made " +
"key with inherited params");
}
prevPubKey = cKey;
prevSubject = currCert.getSubjectX500Principal();
}
/**
* Internal method to create a new key with inherited key parameters.
*
* @param keyValueKey key from which to obtain key value
* @param keyParamsKey key from which to obtain key parameters
* @return new public key having value and parameters
* @throws CertPathValidatorException if keys are not appropriate types
* for this operation
*/
static PublicKey makeInheritedParamsKey(PublicKey keyValueKey,
PublicKey keyParamsKey) throws CertPathValidatorException
{
if (!(keyValueKey instanceof DSAPublicKey) ||
!(keyParamsKey instanceof DSAPublicKey))
throw new CertPathValidatorException("Input key is not " +
"appropriate type for " +
"inheriting parameters");
DSAParams params = ((DSAPublicKey)keyParamsKey).getParams();
if (params == null)
throw new CertPathValidatorException("Key parameters missing");
try {
BigInteger y = ((DSAPublicKey)keyValueKey).getY();
KeyFactory kf = KeyFactory.getInstance("DSA");
DSAPublicKeySpec ks = new DSAPublicKeySpec(y,
params.getP(),
params.getQ(),
params.getG());
return kf.generatePublic(ks);
} catch (GeneralSecurityException e) {
throw new CertPathValidatorException("Unable to generate key with" +
" inherited parameters: " +
e.getMessage(), e);
}
}
/**
* return the public key associated with the last certificate processed
*
* @return PublicKey the last public key processed
*/
PublicKey getPublicKey() {
return prevPubKey;
}
}

View File

@@ -0,0 +1,277 @@
/*
* 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.security.cert.X509Certificate;
/**
* Describes one step of a certification path build, consisting of a
* <code>Vertex</code> state description, a certificate, a possible throwable,
* and a result code.
*
* @author Anne Anderson
* @since 1.4
* @see sun.security.provider.certpath.Vertex
*/
public class BuildStep {
private Vertex vertex;
private X509Certificate cert;
private Throwable throwable;
private int result;
/**
* result code associated with a certificate that may continue a path from
* the current certificate.
*/
public static final int POSSIBLE = 1;
/**
* result code associated with a certificate that was tried, but that
* represents an unsuccessful path, so the certificate has been backed out
* to allow backtracking to the next possible path.
*/
public static final int BACK = 2;
/**
* result code associated with a certificate that successfully continues the
* current path, but does not yet reach the target.
*/
public static final int FOLLOW = 3;
/**
* result code associated with a certificate that represents the end of the
* last possible path, where no path successfully reached the target.
*/
public static final int FAIL = 4;
/**
* result code associated with a certificate that represents the end of a
* path that successfully reaches the target.
*/
public static final int SUCCEED = 5;
/**
* construct a BuildStep
*
* @param vtx description of the vertex at this step
* @param res result, where result is one of POSSIBLE, BACK,
* FOLLOW, FAIL, SUCCEED
*/
public BuildStep(Vertex vtx, int res) {
vertex = vtx;
if (vertex != null) {
cert = vertex.getCertificate();
throwable = vertex.getThrowable();
}
result = res;
}
/**
* return vertex description for this build step
*
* @returns Vertex
*/
public Vertex getVertex() {
return vertex;
}
/**
* return the certificate associated with this build step
*
* @returns X509Certificate
*/
public X509Certificate getCertificate() {
return cert;
}
/**
* return string form of issuer name from certificate associated with this
* build step
*
* @returns String form of issuer name or null, if no certificate.
*/
public String getIssuerName() {
return getIssuerName(null);
}
/**
* return string form of issuer name from certificate associated with this
* build step, or a default name if no certificate associated with this
* build step, or if issuer name could not be obtained from the certificate.
*
* @param defaultName name to use as default if unable to return an issuer
* name from the certificate, or if no certificate.
* @returns String form of issuer name or defaultName, if no certificate or
* exception received while trying to extract issuer name from certificate.
*/
public String getIssuerName(String defaultName) {
return (cert == null ? defaultName
: cert.getIssuerX500Principal().toString());
}
/**
* return string form of subject name from certificate associated with this
* build step.
*
* @returns String form of subject name or null, if no certificate.
*/
public String getSubjectName() {
return getSubjectName(null);
}
/**
* return string form of subject name from certificate associated with this
* build step, or a default name if no certificate associated with this
* build step, or if subject name could not be obtained from the
* certificate.
*
* @param defaultName name to use as default if unable to return a subject
* name from the certificate, or if no certificate.
* @returns String form of subject name or defaultName, if no certificate or
* if an exception was received while attempting to extract the subject name
* from the certificate.
*/
public String getSubjectName(String defaultName) {
return (cert == null ? defaultName
: cert.getSubjectX500Principal().toString());
}
/**
* return the exception associated with this build step.
*
* @returns Throwable
*/
public Throwable getThrowable() {
return throwable;
}
/**
* return the result code associated with this build step. The result codes
* are POSSIBLE, FOLLOW, BACK, FAIL, SUCCEED.
*
* @returns int result code
*/
public int getResult() {
return result;
}
/**
* return a string representing the meaning of the result code associated
* with this build step.
*
* @param res result code
* @returns String string representing meaning of the result code
*/
public String resultToString(int res) {
String resultString = "";
switch (res) {
case POSSIBLE:
resultString = "Certificate to be tried.\n";
break;
case BACK:
resultString = "Certificate backed out since path does not "
+ "satisfy build requirements.\n";
break;
case FOLLOW:
resultString = "Certificate satisfies conditions.\n";
break;
case FAIL:
resultString = "Certificate backed out since path does not "
+ "satisfy conditions.\n";
break;
case SUCCEED:
resultString = "Certificate satisfies conditions.\n";
break;
default:
resultString = "Internal error: Invalid step result value.\n";
}
return resultString;
}
/**
* return a string representation of this build step, showing minimal
* detail.
*
* @returns String
*/
@Override
public String toString() {
String out = "Internal Error\n";
switch (result) {
case BACK:
case FAIL:
out = resultToString(result);
out = out + vertex.throwableToString();
break;
case FOLLOW:
case SUCCEED:
case POSSIBLE:
out = resultToString(result);
break;
default:
out = "Internal Error: Invalid step result\n";
}
return out;
}
/**
* return a string representation of this build step, showing all detail of
* the vertex state appropriate to the result of this build step, and the
* certificate contents.
*
* @returns String
*/
public String verboseToString() {
String out = resultToString(getResult());
switch (result) {
case BACK:
case FAIL:
out = out + vertex.throwableToString();
break;
case FOLLOW:
case SUCCEED:
out = out + vertex.moreToString();
break;
case POSSIBLE:
break;
default:
break;
}
out = out + "Certificate contains:\n" + vertex.certToString();
return out;
}
/**
* return a string representation of this build step, including all possible
* detail of the vertex state, but not including the certificate contents.
*
* @returns String
*/
public String fullToString() {
return resultToString(getResult()) + vertex.toString();
}
}

View File

@@ -0,0 +1,472 @@
/*
* Copyright (c) 2000, 2023, 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.AccessController;
import java.security.GeneralSecurityException;
import java.security.cert.*;
import java.util.*;
import sun.security.action.GetBooleanAction;
import sun.security.provider.certpath.PKIX.BuilderParams;
import sun.security.util.Debug;
import sun.security.x509.GeneralNames;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.GeneralSubtrees;
import sun.security.x509.NameConstraintsExtension;
import sun.security.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
/**
* Abstract class representing a builder, which is able to retrieve
* matching certificates and is able to verify a particular certificate.
*
* @since 1.4
* @author Sean Mullan
* @author Yassir Elley
*/
public abstract class Builder {
private static final Debug debug = Debug.getInstance("certpath");
private Set<String> matchingPolicies;
final BuilderParams buildParams;
final X509CertSelector targetCertConstraints;
/**
* Flag indicating whether support for the caIssuers field of the
* Authority Information Access extension shall be enabled. Currently
* disabled by default for compatibility reasons.
*/
final static boolean USE_AIA = AccessController.doPrivileged
(new GetBooleanAction("com.sun.security.enableAIAcaIssuers"));
/**
* Initialize the builder with the input parameters.
*
* @param params the parameter set used to build a certification path
*/
Builder(BuilderParams buildParams) {
this.buildParams = buildParams;
this.targetCertConstraints =
(X509CertSelector)buildParams.targetCertConstraints();
}
/**
* Retrieves certificates from the list of certStores using the buildParams
* and the currentState as a filter
*
* @param currentState the current State
* @param certStores list of CertStores
*/
abstract Collection<X509Certificate> getMatchingCerts
(State currentState, List<CertStore> certStores)
throws CertStoreException, CertificateException, IOException;
/**
* Verifies the cert against the currentState, using the certPathList
* generated thus far to help with loop detection
*
* @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
*/
abstract void verifyCert(X509Certificate cert, State currentState,
List<X509Certificate> certPathList)
throws GeneralSecurityException;
/**
* Verifies whether the input certificate completes the path.
* When building in the forward direction, a trust anchor will
* complete the path.
*
* @param cert the certificate to test
* @return a boolean value indicating whether the cert completes the path.
*/
abstract boolean isPathCompleted(X509Certificate cert);
/**
* Adds the certificate to the certPathList
*
* @param cert the certificate to be added
* @param certPathList the certification path list
*/
abstract void addCertToPath(X509Certificate cert,
LinkedList<X509Certificate> certPathList);
/**
* Removes final certificate from the certPathList
*
* @param certPathList the certification path list
*/
abstract void removeFinalCertFromPath
(LinkedList<X509Certificate> certPathList);
/**
* get distance of one GeneralName from another
*
* @param base GeneralName at base of subtree
* @param test GeneralName to be tested against base
* @param incomparable the value to return if the names are
* incomparable
* @return distance of test name from base, where 0
* means exact match, 1 means test is an immediate
* child of base, 2 means test is a grandchild, etc.
* -1 means test is a parent of base, -2 means test
* is a grandparent, etc.
*/
static int distance(GeneralNameInterface base,
GeneralNameInterface test, int incomparable)
{
switch (base.constrains(test)) {
case GeneralNameInterface.NAME_DIFF_TYPE:
if (debug != null) {
debug.println("Builder.distance(): Names are different types");
}
return incomparable;
case GeneralNameInterface.NAME_SAME_TYPE:
if (debug != null) {
debug.println("Builder.distance(): Names are same type but " +
"in different subtrees");
}
return incomparable;
case GeneralNameInterface.NAME_MATCH:
return 0;
case GeneralNameInterface.NAME_WIDENS:
break;
case GeneralNameInterface.NAME_NARROWS:
break;
default: // should never occur
return incomparable;
}
/* names are in same subtree */
return test.subtreeDepth() - base.subtreeDepth();
}
/**
* get hop distance of one GeneralName from another in links where
* the names need not have an ancestor/descendant relationship.
* For example, the hop distance from ou=D,ou=C,o=B,c=US to
* ou=F,ou=E,ou=C,o=B,c=US is 3: D->C, C->E, E->F. The hop distance
* from ou=C,o=B,c=US to ou=D,ou=C,o=B,c=US is -1: C->D
*
* @param base GeneralName
* @param test GeneralName to be tested against base
* @param incomparable the value to return if the names are
* incomparable
* @return distance of test name from base measured in hops in the
* namespace hierarchy, where 0 means exact match. Result
* is positive if path is some number of up hops followed by
* some number of down hops; result is negative if path is
* some number of down hops.
*/
static int hops(GeneralNameInterface base, GeneralNameInterface test,
int incomparable)
{
int baseRtest = base.constrains(test);
switch (baseRtest) {
case GeneralNameInterface.NAME_DIFF_TYPE:
if (debug != null) {
debug.println("Builder.hops(): Names are different types");
}
return incomparable;
case GeneralNameInterface.NAME_SAME_TYPE:
/* base and test are in different subtrees */
break;
case GeneralNameInterface.NAME_MATCH:
/* base matches test */
return 0;
case GeneralNameInterface.NAME_WIDENS:
/* base is ancestor of test */
return (test.subtreeDepth()-base.subtreeDepth());
case GeneralNameInterface.NAME_NARROWS:
/* base is descendant of test */
return (test.subtreeDepth()-base.subtreeDepth());
default: // should never occur
return incomparable;
}
/* names are in different subtrees */
if (base.getType() != GeneralNameInterface.NAME_DIRECTORY) {
if (debug != null) {
debug.println("Builder.hops(): hopDistance not implemented " +
"for this name type");
}
return incomparable;
}
X500Name baseName = (X500Name)base;
X500Name testName = (X500Name)test;
X500Name commonName = baseName.commonAncestor(testName);
if (commonName == null) {
if (debug != null) {
debug.println("Builder.hops(): Names are in different " +
"namespaces");
}
return incomparable;
} else {
int commonDistance = commonName.subtreeDepth();
int baseDistance = baseName.subtreeDepth();
int testDistance = testName.subtreeDepth();
return (baseDistance + testDistance - (2 * commonDistance));
}
}
/**
* Determine how close a given certificate gets you toward
* a given target.
*
* @param constraints Current NameConstraints; if null,
* then caller must verify NameConstraints
* independently, realizing that this certificate
* may not actually lead to the target at all.
* @param cert Candidate certificate for chain
* @param target GeneralNameInterface name of target
* @return distance from this certificate to target:
* <ul>
* <li>-1 means certificate could be CA for target, but
* there are no NameConstraints limiting how close
* <li> 0 means certificate subject or subjectAltName
* matches target
* <li> 1 means certificate is permitted to be CA for
* target.
* <li> 2 means certificate is permitted to be CA for
* parent of target.
* <li>&gt;0 in general, means certificate is permitted
* to be a CA for this distance higher in the naming
* hierarchy than the target, plus 1.
* </ul>
* <p>Note that the subject and/or subjectAltName of the
* candidate cert does not have to be an ancestor of the
* target in order to be a CA that can issue a certificate to
* the target. In these cases, the target distance is calculated
* by inspecting the NameConstraints extension in the candidate
* certificate. For example, suppose the target is an X.500 DN with
* a value of "CN=mullan,OU=ireland,O=sun,C=us" and the
* NameConstraints extension in the candidate certificate
* includes a permitted component of "O=sun,C=us", which implies
* that the candidate certificate is allowed to issue certs in
* the "O=sun,C=us" namespace. The target distance is 3
* ((distance of permitted NC from target) + 1).
* The (+1) is added to distinguish the result from the case
* which returns (0).
* @throws IOException if certificate does not get closer
*/
static int targetDistance(NameConstraintsExtension constraints,
X509Certificate cert, GeneralNameInterface target)
throws IOException
{
/* ensure that certificate satisfies existing name constraints */
if (constraints != null && !constraints.verify(cert)) {
throw new IOException("certificate does not satisfy existing name "
+ "constraints");
}
X509CertImpl certImpl;
try {
certImpl = X509CertImpl.toImpl(cert);
} catch (CertificateException e) {
throw new IOException("Invalid certificate", e);
}
/* see if certificate subject matches target */
X500Name subject = X500Name.asX500Name(certImpl.getSubjectX500Principal());
if (subject.equals(target)) {
/* match! */
return 0;
}
SubjectAlternativeNameExtension altNameExt =
certImpl.getSubjectAlternativeNameExtension();
if (altNameExt != null) {
GeneralNames altNames = altNameExt.get(
SubjectAlternativeNameExtension.SUBJECT_NAME);
/* see if any alternative name matches target */
if (altNames != null) {
for (int j = 0, n = altNames.size(); j < n; j++) {
GeneralNameInterface altName = altNames.get(j).getName();
if (altName.equals(target)) {
return 0;
}
}
}
}
/* no exact match; see if certificate can get us to target */
/* first, get NameConstraints out of certificate */
NameConstraintsExtension ncExt = certImpl.getNameConstraintsExtension();
if (ncExt == null) {
return -1;
}
/* merge certificate's NameConstraints with current NameConstraints */
if (constraints != null) {
constraints.merge(ncExt);
} else {
// Make sure we do a clone here, because we're probably
// going to modify this object later and we don't want to
// be sharing it with a Certificate object!
constraints = (NameConstraintsExtension) ncExt.clone();
}
if (debug != null) {
debug.println("Builder.targetDistance() merged constraints: "
+ String.valueOf(constraints));
}
/* reduce permitted by excluded */
GeneralSubtrees permitted =
constraints.get(NameConstraintsExtension.PERMITTED_SUBTREES);
GeneralSubtrees excluded =
constraints.get(NameConstraintsExtension.EXCLUDED_SUBTREES);
if (permitted != null) {
permitted.reduce(excluded);
}
if (debug != null) {
debug.println("Builder.targetDistance() reduced constraints: "
+ permitted);
}
/* see if new merged constraints allow target */
if (!constraints.verify(target)) {
throw new IOException("New certificate not allowed to sign "
+ "certificate for target");
}
/* find distance to target, if any, in permitted */
if (permitted == null) {
/* certificate is unconstrained; could sign for anything */
return -1;
}
for (int i = 0, n = permitted.size(); i < n; i++) {
GeneralNameInterface perName = permitted.get(i).getName().getName();
int distance = distance(perName, target, -1);
if (distance >= 0) {
return (distance + 1);
}
}
/* no matching type in permitted; cert holder could certify target */
return -1;
}
/**
* This method can be used as an optimization to filter out
* certificates that do not have policies which are valid.
* It returns the set of policies (String OIDs) that should exist in
* the certificate policies extension of the certificate that is
* needed by the builder. The logic applied is as follows:
* <p>
* 1) If some initial policies have been set *and* policy mappings are
* inhibited, then acceptable certificates are those that include
* the ANY_POLICY OID or with policies that intersect with the
* initial policies.
* 2) If no initial policies have been set *or* policy mappings are
* not inhibited then we don't have much to work with. All we know is
* that a certificate must have *some* policy because if it didn't
* have any policy then the policy tree would become null (and validation
* would fail).
*
* @return the Set of policies any of which must exist in a
* cert's certificate policies extension in order for a cert to be selected.
*/
Set<String> getMatchingPolicies() {
if (matchingPolicies != null) {
Set<String> initialPolicies = buildParams.initialPolicies();
if ((!initialPolicies.isEmpty()) &&
(!initialPolicies.contains(PolicyChecker.ANY_POLICY)) &&
(buildParams.policyMappingInhibited()))
{
matchingPolicies = new HashSet<>(initialPolicies);
matchingPolicies.add(PolicyChecker.ANY_POLICY);
} else {
// we just return an empty set to make sure that there is
// at least a certificate policies extension in the cert
matchingPolicies = Collections.<String>emptySet();
}
}
return matchingPolicies;
}
/**
* Search the specified CertStores and add all certificates matching
* selector to resultCerts.
*
* If the targetCert criterion of the selector is set, only that cert
* is examined and the CertStores are not searched.
*
* If checkAll is true, all CertStores are searched for matching certs.
* If false, the method returns as soon as the first CertStore returns
* a matching cert(s).
*
* Returns true iff resultCerts changed (a cert was added to the collection)
*/
boolean addMatchingCerts(X509CertSelector selector,
Collection<CertStore> certStores,
Collection<X509Certificate> resultCerts,
boolean checkAll)
{
X509Certificate targetCert = selector.getCertificate();
if (targetCert != null) {
// no need to search CertStores
if (selector.match(targetCert)) {
if (debug != null) {
debug.println("Builder.addMatchingCerts: " +
"adding target cert" +
"\n SN: " + Debug.toHexString(
targetCert.getSerialNumber()) +
"\n Subject: " + targetCert.getSubjectX500Principal() +
"\n Issuer: " + targetCert.getIssuerX500Principal());
}
return resultCerts.add(targetCert);
}
return false;
}
boolean add = false;
for (CertStore store : certStores) {
try {
Collection<? extends Certificate> certs =
store.getCertificates(selector);
for (Certificate cert : certs) {
if (resultCerts.add((X509Certificate)cert)) {
add = true;
}
}
if (!checkAll && add) {
return true;
}
} catch (CertStoreException cse) {
// if getCertificates throws a CertStoreException, we ignore
// it and move on to the next CertStore
if (debug != null) {
debug.println("Builder.addMatchingCerts, non-fatal " +
"exception retrieving certs: " + cse);
cse.printStackTrace();
}
}
}
return add;
}
}

View File

@@ -0,0 +1,235 @@
/*
* Copyright (c) 2003, 2013, 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.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.security.auth.x500.X500Principal;
import sun.misc.HexDumpEncoder;
import sun.security.x509.*;
import sun.security.util.*;
/**
* This class corresponds to the CertId field in OCSP Request
* and the OCSP Response. The ASN.1 definition for CertID is defined
* in RFC 2560 as:
* <pre>
*
* CertID ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
* issuerNameHash OCTET STRING, -- Hash of Issuer's DN
* issuerKeyHash OCTET STRING, -- Hash of Issuers public key
* serialNumber CertificateSerialNumber
* }
*
* </pre>
*
* @author Ram Marti
*/
public class CertId {
private static final boolean debug = false;
private static final AlgorithmId SHA1_ALGID
= new AlgorithmId(AlgorithmId.SHA_oid);
private final AlgorithmId hashAlgId;
private final byte[] issuerNameHash;
private final byte[] issuerKeyHash;
private final SerialNumber certSerialNumber;
private int myhash = -1; // hashcode for this CertId
/**
* Creates a CertId. The hash algorithm used is SHA-1.
*/
public CertId(X509Certificate issuerCert, SerialNumber serialNumber)
throws IOException {
this(issuerCert.getSubjectX500Principal(),
issuerCert.getPublicKey(), serialNumber);
}
public CertId(X500Principal issuerName, PublicKey issuerKey,
SerialNumber serialNumber) throws IOException {
// compute issuerNameHash
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException nsae) {
throw new IOException("Unable to create CertId", nsae);
}
hashAlgId = SHA1_ALGID;
md.update(issuerName.getEncoded());
issuerNameHash = md.digest();
// compute issuerKeyHash (remove the tag and length)
byte[] pubKey = issuerKey.getEncoded();
DerValue val = new DerValue(pubKey);
DerValue[] seq = new DerValue[2];
seq[0] = val.data.getDerValue(); // AlgorithmID
seq[1] = val.data.getDerValue(); // Key
byte[] keyBytes = seq[1].getBitString();
md.update(keyBytes);
issuerKeyHash = md.digest();
certSerialNumber = serialNumber;
if (debug) {
HexDumpEncoder encoder = new HexDumpEncoder();
System.out.println("Issuer Name is " + issuerName);
System.out.println("issuerNameHash is " +
encoder.encodeBuffer(issuerNameHash));
System.out.println("issuerKeyHash is " +
encoder.encodeBuffer(issuerKeyHash));
System.out.println("SerialNumber is " + serialNumber.getNumber());
}
}
/**
* Creates a CertId from its ASN.1 DER encoding.
*/
public CertId(DerInputStream derIn) throws IOException {
hashAlgId = AlgorithmId.parse(derIn.getDerValue());
issuerNameHash = derIn.getOctetString();
issuerKeyHash = derIn.getOctetString();
certSerialNumber = new SerialNumber(derIn);
}
/**
* Return the hash algorithm identifier.
*/
public AlgorithmId getHashAlgorithm() {
return hashAlgId;
}
/**
* Return the hash value for the issuer name.
*/
public byte[] getIssuerNameHash() {
return issuerNameHash;
}
/**
* Return the hash value for the issuer key.
*/
public byte[] getIssuerKeyHash() {
return issuerKeyHash;
}
/**
* Return the serial number.
*/
public BigInteger getSerialNumber() {
return certSerialNumber.getNumber();
}
/**
* Encode the CertId using ASN.1 DER.
* The hash algorithm used is SHA-1.
*/
public void encode(DerOutputStream out) throws IOException {
DerOutputStream tmp = new DerOutputStream();
hashAlgId.encode(tmp);
tmp.putOctetString(issuerNameHash);
tmp.putOctetString(issuerKeyHash);
certSerialNumber.encode(tmp);
out.write(DerValue.tag_Sequence, tmp);
if (debug) {
HexDumpEncoder encoder = new HexDumpEncoder();
System.out.println("Encoded certId is " +
encoder.encode(out.toByteArray()));
}
}
/**
* Returns a hashcode value for this CertId.
*
* @return the hashcode value.
*/
@Override public int hashCode() {
if (myhash == -1) {
myhash = hashAlgId.hashCode();
for (int i = 0; i < issuerNameHash.length; i++) {
myhash += issuerNameHash[i] * i;
}
for (int i = 0; i < issuerKeyHash.length; i++) {
myhash += issuerKeyHash[i] * i;
}
myhash += certSerialNumber.getNumber().hashCode();
}
return myhash;
}
/**
* Compares this CertId for equality with the specified
* object. Two CertId objects are considered equal if their hash algorithms,
* their issuer name and issuer key hash values and their serial numbers
* are equal.
*
* @param other the object to test for equality with this object.
* @return true if the objects are considered equal, false otherwise.
*/
@Override public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || (!(other instanceof CertId))) {
return false;
}
CertId that = (CertId) other;
if (hashAlgId.equals(that.getHashAlgorithm()) &&
Arrays.equals(issuerNameHash, that.getIssuerNameHash()) &&
Arrays.equals(issuerKeyHash, that.getIssuerKeyHash()) &&
certSerialNumber.getNumber().equals(that.getSerialNumber())) {
return true;
} else {
return false;
}
}
/**
* Create a string representation of the CertId.
*/
@Override public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("CertId \n");
sb.append("Algorithm: " + hashAlgId.toString() +"\n");
sb.append("issuerNameHash \n");
HexDumpEncoder encoder = new HexDumpEncoder();
sb.append(encoder.encode(issuerNameHash));
sb.append("\nissuerKeyHash: \n");
sb.append(encoder.encode(issuerKeyHash));
sb.append("\n" + certSerialNumber.toString());
return sb.toString();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2020, 2021, 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.security.Key;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Set;
import java.util.Collections;
import sun.security.util.ConstraintsParameters;
import sun.security.validator.Validator;
/**
* This class contains parameters for checking certificates against
* constraints specified in the jdk.certpath.disabledAlgorithms security
* property.
*/
public class CertPathConstraintsParameters implements ConstraintsParameters {
// The public key of the certificate
private final Key key;
// The certificate's trust anchor which will be checked against the
// jdkCA constraint, if specified.
private final TrustAnchor anchor;
// The PKIXParameter validity date or the timestamp of the signed JAR
// file, if this chain is associated with a timestamped signed JAR.
private final Date date;
// The variant or usage of this certificate
private final String variant;
// The certificate being checked (may be null if a CRL or OCSPResponse is
// being checked)
private final X509Certificate cert;
public CertPathConstraintsParameters(X509Certificate cert,
String variant, TrustAnchor anchor, Date date) {
this(cert.getPublicKey(), variant, anchor, date, cert);
}
public CertPathConstraintsParameters(Key key, String variant,
TrustAnchor anchor) {
this(key, variant, anchor, null, null);
}
private CertPathConstraintsParameters(Key key, String variant,
TrustAnchor anchor, Date date, X509Certificate cert) {
this.key = key;
this.variant = (variant == null ? Validator.VAR_GENERIC : variant);
this.anchor = anchor;
this.date = date;
this.cert = cert;
}
@Override
public boolean anchorIsJdkCA() {
return CertPathHelper.isJdkCA(anchor);
}
@Override
public Set<Key> getKeys() {
return (key == null) ? Collections.emptySet()
: Collections.singleton(key);
}
@Override
public Date getDate() {
return date;
}
@Override
public String getVariant() {
return variant;
}
@Override
public String extendedExceptionMsg() {
return (cert == null ? "."
: " used with certificate: " +
cert.getSubjectX500Principal());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[\n");
sb.append(" Variant: ").append(variant);
if (anchor != null) {
sb.append("\n Anchor: ").append(anchor);
}
if (cert != null) {
sb.append("\n Cert Issuer: ")
.append(cert.getIssuerX500Principal());
sb.append("\n Cert Subject: ")
.append(cert.getSubjectX500Principal());
}
if (key != null) {
sb.append("\n Key: ").append(key.getAlgorithm());
}
if (date != null) {
sb.append("\n Date: ").append(date);
}
sb.append("\n]");
return sb.toString();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2002, 2023, 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.util.Date;
import java.security.cert.TrustAnchor;
import java.security.cert.X509CRLSelector;
/**
* Helper class that allows access to JDK specific known-public methods in the
* java.security.cert package. It relies on a subclass in the
* java.security.cert packages that is initialized before any of these methods
* are called (achieved via static initializers).
*
* The methods are made available in this fashion for performance reasons.
*
* @author Andreas Sterbenz
*/
public abstract class CertPathHelper {
/**
* Object used to tunnel the calls. Initialized by CertPathHelperImpl.
*/
protected static CertPathHelper instance;
protected CertPathHelper() {
// empty
}
protected abstract void implSetDateAndTime(X509CRLSelector sel, Date date, long skew);
protected abstract boolean implIsJdkCA(TrustAnchor anchor);
public static void setDateAndTime(X509CRLSelector sel, Date date, long skew) {
instance.implSetDateAndTime(sel, date, skew);
}
public static boolean isJdkCA(TrustAnchor anchor) {
return (anchor == null) ? false : instance.implIsJdkCA(anchor);
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2009, 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.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.security.AccessController;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.X509CertSelector;
import java.security.cert.X509CRLSelector;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import sun.security.util.Cache;
/**
* Helper used by URICertStore and others when delegating to another CertStore
* to fetch certs and CRLs.
*/
public abstract class CertStoreHelper {
private static final int NUM_TYPES = 2;
private final static Map<String,String> classMap = new HashMap<>(NUM_TYPES);
static {
classMap.put(
"LDAP",
"sun.security.provider.certpath.ldap.LDAPCertStoreHelper");
classMap.put(
"SSLServer",
"sun.security.provider.certpath.ssl.SSLServerCertStoreHelper");
};
private static Cache<String, CertStoreHelper> cache
= Cache.newSoftMemoryCache(NUM_TYPES);
public static CertStoreHelper getInstance(final String type)
throws NoSuchAlgorithmException
{
CertStoreHelper helper = cache.get(type);
if (helper != null) {
return helper;
}
final String cl = classMap.get(type);
if (cl == null) {
throw new NoSuchAlgorithmException(type + " not available");
}
try {
helper = AccessController.doPrivileged(
new PrivilegedExceptionAction<CertStoreHelper>() {
public CertStoreHelper run() throws ClassNotFoundException {
try {
Class<?> c = Class.forName(cl, true, null);
CertStoreHelper csh
= (CertStoreHelper)c.newInstance();
cache.put(type, csh);
return csh;
} catch (InstantiationException |
IllegalAccessException e) {
throw new AssertionError(e);
}
}
});
return helper;
} catch (PrivilegedActionException e) {
throw new NoSuchAlgorithmException(type + " not available",
e.getException());
}
}
static boolean isCausedByNetworkIssue(String type, CertStoreException cse) {
switch (type) {
case "LDAP":
case "SSLServer":
try {
CertStoreHelper csh = CertStoreHelper.getInstance(type);
return csh.isCausedByNetworkIssue(cse);
} catch (NoSuchAlgorithmException nsae) {
return false;
}
case "URI":
Throwable t = cse.getCause();
return (t != null && t instanceof IOException);
default:
// we don't know about any other remote CertStore types
return false;
}
}
/**
* Returns a CertStore using the given URI as parameters.
*/
public abstract CertStore getCertStore(URI uri)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
/**
* Wraps an existing X509CertSelector when needing to avoid DN matching
* issues.
*/
public abstract X509CertSelector wrap(X509CertSelector selector,
X500Principal certSubject,
String dn)
throws IOException;
/**
* Wraps an existing X509CRLSelector when needing to avoid DN matching
* issues.
*/
public abstract X509CRLSelector wrap(X509CRLSelector selector,
Collection<X500Principal> certIssuers,
String dn)
throws IOException;
/**
* Returns true if the cause of the CertStoreException is a network
* related issue.
*/
public abstract boolean isCausedByNetworkIssue(CertStoreException e);
}

View File

@@ -0,0 +1,185 @@
/*
* 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.security.InvalidAlgorithmParameterException;
import java.security.cert.Certificate;
import java.security.cert.CRL;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.security.cert.CertSelector;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertStoreParameters;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.CRLSelector;
import java.security.cert.CertStoreSpi;
/**
* A <code>CertStore</code> that retrieves <code>Certificates</code> and
* <code>CRL</code>s from a <code>Collection</code>.
* <p>
* Before calling the {@link #engineGetCertificates engineGetCertificates} or
* {@link #engineGetCRLs engineGetCRLs} methods, the
* {@link #CollectionCertStore(CertStoreParameters)
* CollectionCertStore(CertStoreParameters)} constructor is called to
* create the <code>CertStore</code> and establish the
* <code>Collection</code> from which <code>Certificate</code>s and
* <code>CRL</code>s will be retrieved. If the specified
* <code>Collection</code> contains an object that is not a
* <code>Certificate</code> or <code>CRL</code>, that object will be
* ignored.
* <p>
* <b>Concurrent Access</b>
* <p>
* As described in the javadoc for <code>CertStoreSpi</code>, the
* <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
* must be thread-safe. That is, multiple threads may concurrently
* invoke these methods on a single <code>CollectionCertStore</code>
* object (or more than one) with no ill effects.
* <p>
* This is achieved by requiring that the <code>Collection</code> passed to
* the {@link #CollectionCertStore(CertStoreParameters)
* CollectionCertStore(CertStoreParameters)} constructor (via the
* <code>CollectionCertStoreParameters</code> object) must have fail-fast
* iterators. Simultaneous modifications to the <code>Collection</code> can thus be
* detected and certificate or CRL retrieval can be retried. The fact that
* <code>Certificate</code>s and <code>CRL</code>s must be thread-safe is also
* essential.
*
* @see java.security.cert.CertStore
*
* @since 1.4
* @author Steve Hanna
*/
public class CollectionCertStore extends CertStoreSpi {
private Collection<?> coll;
/**
* Creates a <code>CertStore</code> with the specified parameters.
* For this class, the parameters object must be an instance of
* <code>CollectionCertStoreParameters</code>. The <code>Collection</code>
* included in the <code>CollectionCertStoreParameters</code> object
* must be thread-safe.
*
* @param params the algorithm parameters
* @exception InvalidAlgorithmParameterException if params is not an
* instance of <code>CollectionCertStoreParameters</code>
*/
public CollectionCertStore(CertStoreParameters params)
throws InvalidAlgorithmParameterException
{
super(params);
if (!(params instanceof CollectionCertStoreParameters))
throw new InvalidAlgorithmParameterException(
"parameters must be CollectionCertStoreParameters");
coll = ((CollectionCertStoreParameters) params).getCollection();
}
/**
* Returns a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector. If no <code>Certificate</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector a <code>CertSelector</code> used to select which
* <code>Certificate</code>s should be returned. Specify <code>null</code>
* to return all <code>Certificate</code>s.
* @return a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public Collection<Certificate> engineGetCertificates
(CertSelector selector) throws CertStoreException {
if (coll == null) {
throw new CertStoreException("Collection is null");
}
// Tolerate a few ConcurrentModificationExceptions
for (int c = 0; c < 10; c++) {
try {
HashSet<Certificate> result = new HashSet<>();
if (selector != null) {
for (Object o : coll) {
if ((o instanceof Certificate) &&
selector.match((Certificate) o))
result.add((Certificate)o);
}
} else {
for (Object o : coll) {
if (o instanceof Certificate)
result.add((Certificate)o);
}
}
return(result);
} catch (ConcurrentModificationException e) { }
}
throw new ConcurrentModificationException("Too many "
+ "ConcurrentModificationExceptions");
}
/**
* Returns a <code>Collection</code> of <code>CRL</code>s that
* match the specified selector. If no <code>CRL</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector a <code>CRLSelector</code> used to select which
* <code>CRL</code>s should be returned. Specify <code>null</code>
* to return all <code>CRL</code>s.
* @return a <code>Collection</code> of <code>CRL</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public Collection<CRL> engineGetCRLs(CRLSelector selector)
throws CertStoreException
{
if (coll == null)
throw new CertStoreException("Collection is null");
// Tolerate a few ConcurrentModificationExceptions
for (int c = 0; c < 10; c++) {
try {
HashSet<CRL> result = new HashSet<>();
if (selector != null) {
for (Object o : coll) {
if ((o instanceof CRL) && selector.match((CRL) o))
result.add((CRL)o);
}
} else {
for (Object o : coll) {
if (o instanceof CRL)
result.add((CRL)o);
}
}
return result;
} catch (ConcurrentModificationException e) { }
}
throw new ConcurrentModificationException("Too many "
+ "ConcurrentModificationExceptions");
}
}

View File

@@ -0,0 +1,308 @@
/*
* Copyright (c) 2000, 2015, 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.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXReason;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import sun.security.util.Debug;
import static sun.security.x509.PKIXExtensions.*;
import sun.security.x509.NameConstraintsExtension;
import sun.security.x509.X509CertImpl;
/**
* ConstraintsChecker is a <code>PKIXCertPathChecker</code> that checks
* constraints information on a PKIX certificate, namely basic constraints
* and name constraints.
*
* @since 1.4
* @author Yassir Elley
*/
class ConstraintsChecker extends PKIXCertPathChecker {
private static final Debug debug = Debug.getInstance("certpath");
/* length of cert path */
private final int certPathLength;
/* current maximum path length (as defined in PKIX) */
private int maxPathLength;
/* current index of cert */
private int i;
private NameConstraintsExtension prevNC;
private Set<String> supportedExts;
/**
* Creates a ConstraintsChecker.
*
* @param certPathLength the length of the certification path
*/
ConstraintsChecker(int certPathLength) {
this.certPathLength = certPathLength;
}
@Override
public void init(boolean forward) throws CertPathValidatorException {
if (!forward) {
i = 0;
maxPathLength = certPathLength;
prevNC = null;
} else {
throw new CertPathValidatorException
("forward checking not supported");
}
}
@Override
public boolean isForwardCheckingSupported() {
return false;
}
@Override
public Set<String> getSupportedExtensions() {
if (supportedExts == null) {
supportedExts = new HashSet<String>(2);
supportedExts.add(BasicConstraints_Id.toString());
supportedExts.add(NameConstraints_Id.toString());
supportedExts = Collections.unmodifiableSet(supportedExts);
}
return supportedExts;
}
/**
* Performs the basic constraints and name constraints
* checks on the certificate using its internal state.
*
* @param cert the <code>Certificate</code> to be checked
* @param unresCritExts a <code>Collection</code> of OID strings
* representing the current set of unresolved critical extensions
* @throws CertPathValidatorException if the specified certificate
* does not pass the check
*/
@Override
public void check(Certificate cert, Collection<String> unresCritExts)
throws CertPathValidatorException
{
X509Certificate currCert = (X509Certificate)cert;
i++;
// MUST run NC check second, since it depends on BC check to
// update remainingCerts
checkBasicConstraints(currCert);
verifyNameConstraints(currCert);
if (unresCritExts != null && !unresCritExts.isEmpty()) {
unresCritExts.remove(BasicConstraints_Id.toString());
unresCritExts.remove(NameConstraints_Id.toString());
}
}
/**
* Internal method to check the name constraints against a cert
*/
private void verifyNameConstraints(X509Certificate currCert)
throws CertPathValidatorException
{
String msg = "name constraints";
if (debug != null) {
debug.println("---checking " + msg + "...");
}
// check name constraints only if there is a previous name constraint
// and either the currCert is the final cert or the currCert is not
// self-issued
if (prevNC != null && ((i == certPathLength) ||
!X509CertImpl.isSelfIssued(currCert))) {
if (debug != null) {
debug.println("prevNC = " + prevNC +
", currDN = " + currCert.getSubjectX500Principal());
}
try {
if (!prevNC.verify(currCert)) {
throw new CertPathValidatorException(msg + " check failed",
null, null, -1, PKIXReason.INVALID_NAME);
}
} catch (IOException ioe) {
throw new CertPathValidatorException(ioe);
}
}
// merge name constraints regardless of whether cert is self-issued
prevNC = mergeNameConstraints(currCert, prevNC);
if (debug != null)
debug.println(msg + " verified.");
}
/**
* Helper to fold sets of name constraints together
*/
static NameConstraintsExtension mergeNameConstraints(
X509Certificate currCert, NameConstraintsExtension prevNC)
throws CertPathValidatorException
{
X509CertImpl currCertImpl;
try {
currCertImpl = X509CertImpl.toImpl(currCert);
} catch (CertificateException ce) {
throw new CertPathValidatorException(ce);
}
NameConstraintsExtension newConstraints =
currCertImpl.getNameConstraintsExtension();
if (debug != null) {
debug.println("prevNC = " + prevNC +
", newNC = " + String.valueOf(newConstraints));
}
// if there are no previous name constraints, we just return the
// new name constraints.
if (prevNC == null) {
if (debug != null) {
debug.println("mergedNC = " + String.valueOf(newConstraints));
}
if (newConstraints == null) {
return newConstraints;
} else {
// Make sure we do a clone here, because we're probably
// going to modify this object later and we don't want to
// be sharing it with a Certificate object!
return (NameConstraintsExtension)newConstraints.clone();
}
} else {
try {
// after merge, prevNC should contain the merged constraints
prevNC.merge(newConstraints);
} catch (IOException ioe) {
throw new CertPathValidatorException(ioe);
}
if (debug != null) {
debug.println("mergedNC = " + prevNC);
}
return prevNC;
}
}
/**
* Internal method to check that a given cert meets basic constraints.
*/
private void checkBasicConstraints(X509Certificate currCert)
throws CertPathValidatorException
{
String msg = "basic constraints";
if (debug != null) {
debug.println("---checking " + msg + "...");
debug.println("i = " + i +
", maxPathLength = " + maxPathLength);
}
/* check if intermediate cert */
if (i < certPathLength) {
// RFC5280: If certificate i is a version 3 certificate, verify
// that the basicConstraints extension is present and that cA is
// set to TRUE. (If certificate i is a version 1 or version 2
// certificate, then the application MUST either verify that
// certificate i is a CA certificate through out-of-band means
// or reject the certificate. Conforming implementations may
// choose to reject all version 1 and version 2 intermediate
// certificates.)
//
// We choose to reject all version 1 and version 2 intermediate
// certificates except that it is self issued by the trust
// anchor in order to support key rollover or changes in
// certificate policies.
int pathLenConstraint = -1;
if (currCert.getVersion() < 3) { // version 1 or version 2
if (i == 1) { // issued by a trust anchor
if (X509CertImpl.isSelfIssued(currCert)) {
pathLenConstraint = Integer.MAX_VALUE;
}
}
} else {
pathLenConstraint = currCert.getBasicConstraints();
}
if (pathLenConstraint == -1) {
throw new CertPathValidatorException
(msg + " check failed: this is not a CA certificate",
null, null, -1, PKIXReason.NOT_CA_CERT);
}
if (!X509CertImpl.isSelfIssued(currCert)) {
if (maxPathLength <= 0) {
throw new CertPathValidatorException
(msg + " check failed: pathLenConstraint violated - "
+ "this cert must be the last cert in the "
+ "certification path", null, null, -1,
PKIXReason.PATH_TOO_LONG);
}
maxPathLength--;
}
if (pathLenConstraint < maxPathLength)
maxPathLength = pathLenConstraint;
}
if (debug != null) {
debug.println("after processing, maxPathLength = " + maxPathLength);
debug.println(msg + " verified.");
}
}
/**
* Merges the specified maxPathLength with the pathLenConstraint
* obtained from the certificate.
*
* @param cert the <code>X509Certificate</code>
* @param maxPathLength the previous maximum path length
* @return the new maximum path length constraint (-1 means no more
* certificates can follow, Integer.MAX_VALUE means path length is
* unconstrained)
*/
static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) {
int pathLenConstraint = cert.getBasicConstraints();
if (!X509CertImpl.isSelfIssued(cert)) {
maxPathLength--;
}
if (pathLenConstraint < maxPathLength) {
maxPathLength = pathLenConstraint;
}
return maxPathLength;
}
}

View File

@@ -0,0 +1,804 @@
/*
* Copyright (c) 2002, 2017, 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.*;
import java.net.URI;
import java.security.*;
import java.security.cert.*;
import javax.security.auth.x500.X500Principal;
import java.util.*;
import sun.security.util.Debug;
import sun.security.validator.Validator;
import static sun.security.x509.PKIXExtensions.*;
import sun.security.x509.*;
/**
* Class to obtain CRLs via the CRLDistributionPoints extension.
* Note that the functionality of this class must be explicitly enabled
* via a system property, see the USE_CRLDP variable below.
*
* This class uses the URICertStore class to fetch CRLs. The URICertStore
* class also implements CRL caching: see the class description for more
* information.
*
* @author Andreas Sterbenz
* @author Sean Mullan
* @since 1.4.2
*/
public class DistributionPointFetcher {
private static final Debug debug = Debug.getInstance("certpath");
private static final boolean[] ALL_REASONS =
{true, true, true, true, true, true, true, true, true};
/**
* Private instantiation only.
*/
private DistributionPointFetcher() {}
/**
* Return the X509CRLs matching this selector. The selector must be
* an X509CRLSelector with certificateChecking set.
*/
public static Collection<X509CRL> getCRLs(X509CRLSelector selector,
boolean signFlag, PublicKey prevKey, String provider,
List<CertStore> certStores, boolean[] reasonsMask,
Set<TrustAnchor> trustAnchors, Date validity, String variant)
throws CertStoreException
{
return getCRLs(selector, signFlag, prevKey, null, provider, certStores,
reasonsMask, trustAnchors, validity, variant, null);
}
/**
* Return the X509CRLs matching this selector. The selector must be
* an X509CRLSelector with certificateChecking set.
*/
// Called by com.sun.deploy.security.RevocationChecker
public static Collection<X509CRL> getCRLs(X509CRLSelector selector,
boolean signFlag,
PublicKey prevKey,
String provider,
List<CertStore> certStores,
boolean[] reasonsMask,
Set<TrustAnchor> trustAnchors,
Date validity)
throws CertStoreException
{
if (trustAnchors.isEmpty()) {
throw new CertStoreException(
"at least one TrustAnchor must be specified");
}
TrustAnchor anchor = trustAnchors.iterator().next();
return getCRLs(selector, signFlag, prevKey, null, provider, certStores,
reasonsMask, trustAnchors, validity,
Validator.VAR_PLUGIN_CODE_SIGNING, anchor);
}
/**
* Return the X509CRLs matching this selector. The selector must be
* an X509CRLSelector with certificateChecking set.
*/
public static Collection<X509CRL> getCRLs(X509CRLSelector selector,
boolean signFlag,
PublicKey prevKey,
X509Certificate prevCert,
String provider,
List<CertStore> certStores,
boolean[] reasonsMask,
Set<TrustAnchor> trustAnchors,
Date validity,
String variant,
TrustAnchor anchor)
throws CertStoreException
{
X509Certificate cert = selector.getCertificateChecking();
if (cert == null) {
return Collections.emptySet();
}
try {
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
if (debug != null) {
debug.println("DistributionPointFetcher.getCRLs: Checking "
+ "CRLDPs for " + certImpl.getSubjectX500Principal());
}
CRLDistributionPointsExtension ext =
certImpl.getCRLDistributionPointsExtension();
if (ext == null) {
if (debug != null) {
debug.println("No CRLDP ext");
}
return Collections.emptySet();
}
List<DistributionPoint> points =
ext.get(CRLDistributionPointsExtension.POINTS);
Set<X509CRL> results = new HashSet<>();
for (Iterator<DistributionPoint> t = points.iterator();
t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) {
DistributionPoint point = t.next();
Collection<X509CRL> crls = getCRLs(selector, certImpl,
point, reasonsMask, signFlag, prevKey, prevCert, provider,
certStores, trustAnchors, validity, variant, anchor);
results.addAll(crls);
}
if (debug != null) {
debug.println("Returning " + results.size() + " CRLs");
}
return results;
} catch (CertificateException | IOException e) {
return Collections.emptySet();
}
}
/**
* Download CRLs from the given distribution point, verify and return them.
* See the top of the class for current limitations.
*
* @throws CertStoreException if there is an error retrieving the CRLs
* from one of the GeneralNames and no other CRLs are retrieved from
* the other GeneralNames. If more than one GeneralName throws an
* exception then the one from the last GeneralName is thrown.
*/
private static Collection<X509CRL> getCRLs(X509CRLSelector selector,
X509CertImpl certImpl, DistributionPoint point, boolean[] reasonsMask,
boolean signFlag, PublicKey prevKey, X509Certificate prevCert,
String provider, List<CertStore> certStores,
Set<TrustAnchor> trustAnchors, Date validity, String variant,
TrustAnchor anchor)
throws CertStoreException {
// check for full name
GeneralNames fullName = point.getFullName();
if (fullName == null) {
// check for relative name
RDN relativeName = point.getRelativeName();
if (relativeName == null) {
return Collections.emptySet();
}
try {
GeneralNames crlIssuers = point.getCRLIssuer();
if (crlIssuers == null) {
fullName = getFullNames
((X500Name) certImpl.getIssuerDN(), relativeName);
} else {
// should only be one CRL Issuer
if (crlIssuers.size() != 1) {
return Collections.emptySet();
} else {
fullName = getFullNames
((X500Name) crlIssuers.get(0).getName(), relativeName);
}
}
} catch (IOException ioe) {
return Collections.emptySet();
}
}
Collection<X509CRL> possibleCRLs = new ArrayList<>();
CertStoreException savedCSE = null;
for (Iterator<GeneralName> t = fullName.iterator(); t.hasNext(); ) {
try {
GeneralName name = t.next();
if (name.getType() == GeneralNameInterface.NAME_DIRECTORY) {
X500Name x500Name = (X500Name) name.getName();
possibleCRLs.addAll(
getCRLs(x500Name, certImpl.getIssuerX500Principal(),
certStores));
} else if (name.getType() == GeneralNameInterface.NAME_URI) {
URIName uriName = (URIName)name.getName();
X509CRL crl = getCRL(uriName);
if (crl != null) {
possibleCRLs.add(crl);
}
}
} catch (CertStoreException cse) {
savedCSE = cse;
}
}
// only throw CertStoreException if no CRLs are retrieved
if (possibleCRLs.isEmpty() && savedCSE != null) {
throw savedCSE;
}
Collection<X509CRL> crls = new ArrayList<>(2);
for (X509CRL crl : possibleCRLs) {
try {
// make sure issuer is not set
// we check the issuer in verifyCRLs method
selector.setIssuerNames(null);
if (selector.match(crl) && verifyCRL(certImpl, point, crl,
reasonsMask, signFlag, prevKey, prevCert, provider,
trustAnchors, certStores, validity, variant, anchor)) {
crls.add(crl);
}
} catch (IOException | CRLException e) {
// don't add the CRL
if (debug != null) {
debug.println("Exception verifying CRL: " + e.getMessage());
e.printStackTrace();
}
}
}
return crls;
}
/**
* Download CRL from given URI.
*/
private static X509CRL getCRL(URIName name) throws CertStoreException {
URI uri = name.getURI();
if (debug != null) {
debug.println("Trying to fetch CRL from DP " + uri);
}
CertStore ucs = null;
try {
ucs = URICertStore.getInstance
(new URICertStore.URICertStoreParameters(uri));
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException e) {
if (debug != null) {
debug.println("Can't create URICertStore: " + e.getMessage());
}
return null;
}
Collection<? extends CRL> crls = ucs.getCRLs(null);
if (crls.isEmpty()) {
return null;
} else {
return (X509CRL) crls.iterator().next();
}
}
/**
* Fetch CRLs from certStores.
*
* @throws CertStoreException if there is an error retrieving the CRLs from
* one of the CertStores and no other CRLs are retrieved from
* the other CertStores. If more than one CertStore throws an
* exception then the one from the last CertStore is thrown.
*/
private static Collection<X509CRL> getCRLs(X500Name name,
X500Principal certIssuer,
List<CertStore> certStores)
throws CertStoreException
{
if (debug != null) {
debug.println("Trying to fetch CRL from DP " + name);
}
X509CRLSelector xcs = new X509CRLSelector();
xcs.addIssuer(name.asX500Principal());
xcs.addIssuer(certIssuer);
Collection<X509CRL> crls = new ArrayList<>();
CertStoreException savedCSE = null;
for (CertStore store : certStores) {
try {
for (CRL crl : store.getCRLs(xcs)) {
crls.add((X509CRL)crl);
}
} catch (CertStoreException cse) {
if (debug != null) {
debug.println("Exception while retrieving " +
"CRLs: " + cse);
cse.printStackTrace();
}
savedCSE = new PKIX.CertStoreTypeException(store.getType(),cse);
}
}
// only throw CertStoreException if no CRLs are retrieved
if (crls.isEmpty() && savedCSE != null) {
throw savedCSE;
} else {
return crls;
}
}
/**
* Verifies a CRL for the given certificate's Distribution Point to
* ensure it is appropriate for checking the revocation status.
*
* @param certImpl the certificate whose revocation status is being checked
* @param point one of the distribution points of the certificate
* @param crl the CRL
* @param reasonsMask the interim reasons mask
* @param signFlag true if prevKey can be used to verify the CRL
* @param prevKey the public key that verifies the certificate's signature
* @param prevCert the certificate whose public key verifies
* {@code certImpl}'s signature
* @param provider the Signature provider to use
* @param trustAnchors a {@code Set} of {@code TrustAnchor}s
* @param certStores a {@code List} of {@code CertStore}s to be used in
* finding certificates and CRLs
* @param validity the time for which the validity of the CRL issuer's
* certification path should be determined
* @return true if ok, false if not
*/
static boolean verifyCRL(X509CertImpl certImpl, DistributionPoint point,
X509CRL crl, boolean[] reasonsMask, boolean signFlag,
PublicKey prevKey, X509Certificate prevCert, String provider,
Set<TrustAnchor> trustAnchors, List<CertStore> certStores,
Date validity, String variant, TrustAnchor anchor)
throws CRLException, IOException {
if (debug != null) {
debug.println("DistributionPointFetcher.verifyCRL: " +
"checking revocation status for" +
"\n SN: " + Debug.toHexString(certImpl.getSerialNumber()) +
"\n Subject: " + certImpl.getSubjectX500Principal() +
"\n Issuer: " + certImpl.getIssuerX500Principal());
}
boolean indirectCRL = false;
X509CRLImpl crlImpl = X509CRLImpl.toImpl(crl);
IssuingDistributionPointExtension idpExt =
crlImpl.getIssuingDistributionPointExtension();
X500Name certIssuer = (X500Name) certImpl.getIssuerDN();
X500Name crlIssuer = (X500Name) crlImpl.getIssuerDN();
// if crlIssuer is set, verify that it matches the issuer of the
// CRL and the CRL contains an IDP extension with the indirectCRL
// boolean asserted. Otherwise, verify that the CRL issuer matches the
// certificate issuer.
GeneralNames pointCrlIssuers = point.getCRLIssuer();
X500Name pointCrlIssuer = null;
if (pointCrlIssuers != null) {
if (idpExt == null ||
((Boolean) idpExt.get
(IssuingDistributionPointExtension.INDIRECT_CRL)).equals
(Boolean.FALSE)) {
return false;
}
boolean match = false;
for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
!match && t.hasNext(); ) {
GeneralNameInterface name = t.next().getName();
if (crlIssuer.equals(name) == true) {
pointCrlIssuer = (X500Name) name;
match = true;
}
}
if (match == false) {
return false;
}
// we accept the case that a CRL issuer provide status
// information for itself.
if (issues(certImpl, crlImpl, provider)) {
// reset the public key used to verify the CRL's signature
prevKey = certImpl.getPublicKey();
} else {
indirectCRL = true;
}
} else if (crlIssuer.equals(certIssuer) == false) {
if (debug != null) {
debug.println("crl issuer does not equal cert issuer.\n" +
"crl issuer: " + crlIssuer + "\n" +
"cert issuer: " + certIssuer);
}
return false;
} else {
// in case of self-issued indirect CRL issuer.
KeyIdentifier certAKID = certImpl.getAuthKeyId();
KeyIdentifier crlAKID = crlImpl.getAuthKeyId();
if (certAKID == null || crlAKID == null) {
// cannot recognize indirect CRL without AKID
// we accept the case that a CRL issuer provide status
// information for itself.
if (issues(certImpl, crlImpl, provider)) {
// reset the public key used to verify the CRL's signature
prevKey = certImpl.getPublicKey();
}
} else if (!certAKID.equals(crlAKID)) {
// we accept the case that a CRL issuer provide status
// information for itself.
if (issues(certImpl, crlImpl, provider)) {
// reset the public key used to verify the CRL's signature
prevKey = certImpl.getPublicKey();
} else {
indirectCRL = true;
}
}
}
if (!indirectCRL && !signFlag) {
// cert's key cannot be used to verify the CRL
return false;
}
if (idpExt != null) {
DistributionPointName idpPoint = (DistributionPointName)
idpExt.get(IssuingDistributionPointExtension.POINT);
if (idpPoint != null) {
GeneralNames idpNames = idpPoint.getFullName();
if (idpNames == null) {
RDN relativeName = idpPoint.getRelativeName();
if (relativeName == null) {
if (debug != null) {
debug.println("IDP must be relative or full DN");
}
return false;
}
if (debug != null) {
debug.println("IDP relativeName:" + relativeName);
}
idpNames = getFullNames(crlIssuer, relativeName);
}
// if the DP name is present in the IDP CRL extension and the
// DP field is present in the DP, then verify that one of the
// names in the IDP matches one of the names in the DP
if (point.getFullName() != null ||
point.getRelativeName() != null) {
GeneralNames pointNames = point.getFullName();
if (pointNames == null) {
RDN relativeName = point.getRelativeName();
if (relativeName == null) {
if (debug != null) {
debug.println("DP must be relative or full DN");
}
return false;
}
if (debug != null) {
debug.println("DP relativeName:" + relativeName);
}
if (indirectCRL) {
if (pointCrlIssuers.size() != 1) {
// RFC 5280: there must be only 1 CRL issuer
// name when relativeName is present
if (debug != null) {
debug.println("must only be one CRL " +
"issuer when relative name present");
}
return false;
}
pointNames = getFullNames
(pointCrlIssuer, relativeName);
} else {
pointNames = getFullNames(certIssuer, relativeName);
}
}
boolean match = false;
for (Iterator<GeneralName> i = idpNames.iterator();
!match && i.hasNext(); ) {
GeneralNameInterface idpName = i.next().getName();
if (debug != null) {
debug.println("idpName: " + idpName);
}
for (Iterator<GeneralName> p = pointNames.iterator();
!match && p.hasNext(); ) {
GeneralNameInterface pointName = p.next().getName();
if (debug != null) {
debug.println("pointName: " + pointName);
}
match = idpName.equals(pointName);
}
}
if (!match) {
if (debug != null) {
debug.println("IDP name does not match DP name");
}
return false;
}
// if the DP name is present in the IDP CRL extension and the
// DP field is absent from the DP, then verify that one of the
// names in the IDP matches one of the names in the crlIssuer
// field of the DP
} else {
// verify that one of the names in the IDP matches one of
// the names in the cRLIssuer of the cert's DP
boolean match = false;
for (Iterator<GeneralName> t = pointCrlIssuers.iterator();
!match && t.hasNext(); ) {
GeneralNameInterface crlIssuerName = t.next().getName();
for (Iterator<GeneralName> i = idpNames.iterator();
!match && i.hasNext(); ) {
GeneralNameInterface idpName = i.next().getName();
match = crlIssuerName.equals(idpName);
}
}
if (!match) {
return false;
}
}
}
// if the onlyContainsUserCerts boolean is asserted, verify that the
// cert is not a CA cert
Boolean b = (Boolean)
idpExt.get(IssuingDistributionPointExtension.ONLY_USER_CERTS);
if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() != -1) {
if (debug != null) {
debug.println("cert must be a EE cert");
}
return false;
}
// if the onlyContainsCACerts boolean is asserted, verify that the
// cert is a CA cert
b = (Boolean)
idpExt.get(IssuingDistributionPointExtension.ONLY_CA_CERTS);
if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() == -1) {
if (debug != null) {
debug.println("cert must be a CA cert");
}
return false;
}
// verify that the onlyContainsAttributeCerts boolean is not
// asserted
b = (Boolean) idpExt.get
(IssuingDistributionPointExtension.ONLY_ATTRIBUTE_CERTS);
if (b.equals(Boolean.TRUE)) {
if (debug != null) {
debug.println("cert must not be an AA cert");
}
return false;
}
}
// compute interim reasons mask
boolean[] interimReasonsMask = new boolean[9];
ReasonFlags reasons = null;
if (idpExt != null) {
reasons = (ReasonFlags)
idpExt.get(IssuingDistributionPointExtension.REASONS);
}
boolean[] pointReasonFlags = point.getReasonFlags();
if (reasons != null) {
if (pointReasonFlags != null) {
// set interim reasons mask to the intersection of
// reasons in the DP and onlySomeReasons in the IDP
boolean[] idpReasonFlags = reasons.getFlags();
for (int i = 0; i < interimReasonsMask.length; i++) {
interimReasonsMask[i] =
(i < idpReasonFlags.length && idpReasonFlags[i]) &&
(i < pointReasonFlags.length && pointReasonFlags[i]);
}
} else {
// set interim reasons mask to the value of
// onlySomeReasons in the IDP (and clone it since we may
// modify it)
interimReasonsMask = reasons.getFlags().clone();
}
} else if (idpExt == null || reasons == null) {
if (pointReasonFlags != null) {
// set interim reasons mask to the value of DP reasons
interimReasonsMask = pointReasonFlags.clone();
} else {
// set interim reasons mask to the special value all-reasons
Arrays.fill(interimReasonsMask, true);
}
}
// verify that interim reasons mask includes one or more reasons
// not included in the reasons mask
boolean oneOrMore = false;
for (int i = 0; i < interimReasonsMask.length && !oneOrMore; i++) {
if (interimReasonsMask[i] &&
!(i < reasonsMask.length && reasonsMask[i]))
{
oneOrMore = true;
}
}
if (!oneOrMore) {
return false;
}
// Obtain and validate the certification path for the complete
// CRL issuer (if indirect CRL). If a key usage extension is present
// in the CRL issuer's certificate, verify that the cRLSign bit is set.
if (indirectCRL) {
X509CertSelector certSel = new X509CertSelector();
certSel.setSubject(crlIssuer.asX500Principal());
boolean[] crlSign = {false,false,false,false,false,false,true};
certSel.setKeyUsage(crlSign);
// Currently by default, forward builder does not enable
// subject/authority key identifier identifying for target
// certificate, instead, it only compares the CRL issuer and
// the target certificate subject. If the certificate of the
// delegated CRL issuer is a self-issued certificate, the
// builder is unable to find the proper CRL issuer by issuer
// name only, there is a potential dead loop on finding the
// proper issuer. It is of great help to narrow the target
// scope down to aware of authority key identifiers in the
// selector, for the purposes of breaking the dead loop.
AuthorityKeyIdentifierExtension akidext =
crlImpl.getAuthKeyIdExtension();
if (akidext != null) {
byte[] kid = akidext.getEncodedKeyIdentifier();
if (kid != null) {
certSel.setSubjectKeyIdentifier(kid);
}
SerialNumber asn = (SerialNumber)akidext.get(
AuthorityKeyIdentifierExtension.SERIAL_NUMBER);
if (asn != null) {
certSel.setSerialNumber(asn.getNumber());
}
// the subject criterion will be set by builder automatically.
}
// By now, we have validated the previous certificate, so we can
// trust it during the validation of the CRL issuer.
// In addition to the performance improvement, another benefit is to
// break the dead loop while looking for the issuer back and forth
// between the delegated self-issued certificate and its issuer.
Set<TrustAnchor> newTrustAnchors = new HashSet<>(trustAnchors);
if (prevKey != null) {
// Add the previous certificate as a trust anchor.
// If prevCert is not null, we want to construct a TrustAnchor
// using the cert object because when the certpath for the CRL
// is built later, the CertSelector will make comparisons with
// the TrustAnchor's trustedCert member rather than its pubKey.
TrustAnchor temporary;
if (prevCert != null) {
temporary = new TrustAnchor(prevCert, null);
} else {
X500Principal principal = certImpl.getIssuerX500Principal();
temporary = new TrustAnchor(principal, prevKey, null);
}
newTrustAnchors.add(temporary);
}
PKIXBuilderParameters params = null;
try {
params = new PKIXBuilderParameters(newTrustAnchors, certSel);
} catch (InvalidAlgorithmParameterException iape) {
throw new CRLException(iape);
}
params.setCertStores(certStores);
params.setSigProvider(provider);
params.setDate(validity);
try {
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
PKIXCertPathBuilderResult result =
(PKIXCertPathBuilderResult) builder.build(params);
prevKey = result.getPublicKey();
} catch (GeneralSecurityException e) {
throw new CRLException(e);
}
}
// check the crl signature algorithm
try {
AlgorithmChecker.check(prevKey, crlImpl.getSigAlgId(),
variant, anchor);
} catch (CertPathValidatorException cpve) {
if (debug != null) {
debug.println("CRL signature algorithm check failed: " + cpve);
}
return false;
}
// validate the signature on the CRL
try {
crl.verify(prevKey, provider);
} catch (GeneralSecurityException e) {
if (debug != null) {
debug.println("CRL signature failed to verify");
}
return false;
}
// reject CRL if any unresolved critical extensions remain in the CRL.
Set<String> unresCritExts = crl.getCriticalExtensionOIDs();
// remove any that we have processed
if (unresCritExts != null) {
unresCritExts.remove(IssuingDistributionPoint_Id.toString());
if (!unresCritExts.isEmpty()) {
if (debug != null) {
debug.println("Unrecognized critical extension(s) in CRL: "
+ unresCritExts);
for (String ext : unresCritExts) {
debug.println(ext);
}
}
return false;
}
}
// update reasonsMask
for (int i = 0; i < reasonsMask.length; i++) {
reasonsMask[i] = reasonsMask[i] ||
(i < interimReasonsMask.length && interimReasonsMask[i]);
}
return true;
}
/**
* Append relative name to the issuer name and return a new
* GeneralNames object.
*/
private static GeneralNames getFullNames(X500Name issuer, RDN rdn)
throws IOException
{
List<RDN> rdns = new ArrayList<>(issuer.rdns());
rdns.add(rdn);
X500Name fullName = new X500Name(rdns.toArray(new RDN[0]));
GeneralNames fullNames = new GeneralNames();
fullNames.add(new GeneralName(fullName));
return fullNames;
}
/**
* Verifies whether a CRL is issued by a certain certificate
*
* @param cert the certificate
* @param crl the CRL to be verified
* @param provider the name of the signature provider
*/
private static boolean issues(X509CertImpl cert, X509CRLImpl crl,
String provider) throws IOException
{
boolean matched = false;
AdaptableX509CertSelector issuerSelector =
new AdaptableX509CertSelector();
// check certificate's key usage
boolean[] usages = cert.getKeyUsage();
if (usages != null) {
usages[6] = true; // cRLSign
issuerSelector.setKeyUsage(usages);
}
// check certificate's subject
X500Principal crlIssuer = crl.getIssuerX500Principal();
issuerSelector.setSubject(crlIssuer);
/*
* Facilitate certification path construction with authority
* key identifier and subject key identifier.
*
* In practice, conforming CAs MUST use the key identifier method,
* and MUST include authority key identifier extension in all CRLs
* issued. [section 5.2.1, RFC 2459]
*/
AuthorityKeyIdentifierExtension crlAKID = crl.getAuthKeyIdExtension();
issuerSelector.setSkiAndSerialNumber(crlAKID);
matched = issuerSelector.match(cert);
// if AKID is unreliable, verify the CRL signature with the cert
if (matched && (crlAKID == null ||
cert.getAuthorityKeyIdentifierExtension() == null)) {
try {
crl.verify(cert.getPublicKey(), provider);
matched = true;
} catch (GeneralSecurityException e) {
matched = false;
}
}
return matched;
}
}

View File

@@ -0,0 +1,896 @@
/*
* Copyright (c) 2000, 2023, 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.InvalidKeyException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXReason;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.security.cert.X509CertSelector;
import java.util.*;
import javax.security.auth.x500.X500Principal;
import sun.security.provider.certpath.PKIX.BuilderParams;
import sun.security.util.Debug;
import sun.security.x509.AccessDescription;
import sun.security.x509.AuthorityInfoAccessExtension;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import static sun.security.x509.PKIXExtensions.*;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
/**
* This class represents a forward builder, which is able to retrieve
* matching certificates from CertStores and verify a particular certificate
* against a ForwardState.
*
* @since 1.4
* @author Yassir Elley
* @author Sean Mullan
*/
class ForwardBuilder extends Builder {
private static final Debug debug = Debug.getInstance("certpath");
private final Set<X509Certificate> trustedCerts;
private final Set<X500Principal> trustedSubjectDNs;
private final Set<TrustAnchor> trustAnchors;
private X509CertSelector eeSelector;
private AdaptableX509CertSelector caSelector;
private X509CertSelector caTargetSelector;
TrustAnchor trustAnchor;
private boolean searchAllCertStores = true;
/**
* Initialize the builder with the input parameters.
*
* @param params the parameter set used to build a certification path
*/
ForwardBuilder(BuilderParams buildParams, boolean searchAllCertStores) {
super(buildParams);
// populate sets of trusted certificates and subject DNs
trustAnchors = buildParams.trustAnchors();
trustedCerts = new HashSet<X509Certificate>(trustAnchors.size());
trustedSubjectDNs = new HashSet<X500Principal>(trustAnchors.size());
for (TrustAnchor anchor : trustAnchors) {
X509Certificate trustedCert = anchor.getTrustedCert();
if (trustedCert != null) {
trustedCerts.add(trustedCert);
trustedSubjectDNs.add(trustedCert.getSubjectX500Principal());
} else {
trustedSubjectDNs.add(anchor.getCA());
}
}
this.searchAllCertStores = searchAllCertStores;
}
/**
* 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>ForwardState</code>
* @param certStores list of CertStores
*/
@Override
Collection<X509Certificate> getMatchingCerts(State currentState,
List<CertStore> certStores)
throws CertStoreException, CertificateException, IOException
{
if (debug != null) {
debug.println("ForwardBuilder.getMatchingCerts()...");
}
ForwardState currState = (ForwardState) currentState;
/*
* We store certs in a Set because we don't want duplicates.
* As each cert is added, it is sorted based on the PKIXCertComparator
* algorithm.
*/
Comparator<X509Certificate> comparator =
new PKIXCertComparator(trustedSubjectDNs, currState.cert);
Set<X509Certificate> certs = new TreeSet<>(comparator);
/*
* Only look for EE certs if search has just started.
*/
if (currState.isInitial()) {
getMatchingEECerts(currState, certStores, certs);
}
getMatchingCACerts(currState, certStores, certs);
return certs;
}
/*
* Retrieves all end-entity certificates which satisfy constraints
* and requirements specified in the parameters and PKIX state.
*/
private void getMatchingEECerts(ForwardState currentState,
List<CertStore> certStores,
Collection<X509Certificate> eeCerts)
throws IOException
{
if (debug != null) {
debug.println("ForwardBuilder.getMatchingEECerts()...");
}
/*
* Compose a certificate matching rule to filter out
* certs which don't satisfy constraints
*
* First, retrieve clone of current target cert constraints,
* and then add more selection criteria based on current validation
* state. Since selector never changes, cache local copy & reuse.
*/
if (eeSelector == null) {
eeSelector = (X509CertSelector) targetCertConstraints.clone();
/*
* Match on certificate validity date
*/
eeSelector.setCertificateValid(buildParams.date());
/*
* Policy processing optimizations
*/
if (buildParams.explicitPolicyRequired()) {
eeSelector.setPolicy(getMatchingPolicies());
}
/*
* Require EE certs
*/
eeSelector.setBasicConstraints(-2);
}
/* Retrieve matching EE certs from CertStores */
addMatchingCerts(eeSelector, certStores, eeCerts, searchAllCertStores);
}
/**
* Retrieves all CA certificates which satisfy constraints
* and requirements specified in the parameters and PKIX state.
*/
private void getMatchingCACerts(ForwardState currentState,
List<CertStore> certStores,
Collection<X509Certificate> caCerts)
throws IOException
{
if (debug != null) {
debug.println("ForwardBuilder.getMatchingCACerts()...");
}
int initialSize = caCerts.size();
/*
* Compose a CertSelector to filter out
* certs which do not satisfy requirements.
*/
X509CertSelector sel = null;
if (currentState.isInitial()) {
if (targetCertConstraints.getBasicConstraints() == -2) {
// no need to continue: this means we never can match a CA cert
return;
}
/* This means a CA is the target, so match on same stuff as
* getMatchingEECerts
*/
if (debug != null) {
debug.println("ForwardBuilder.getMatchingCACerts(): " +
"the target is a CA");
}
if (caTargetSelector == null) {
caTargetSelector =
(X509CertSelector) targetCertConstraints.clone();
/*
* Since we don't check the validity period of trusted
* certificates, please don't set the certificate valid
* criterion unless the trusted certificate matching is
* completed.
*/
/*
* Policy processing optimizations
*/
if (buildParams.explicitPolicyRequired())
caTargetSelector.setPolicy(getMatchingPolicies());
}
sel = caTargetSelector;
} else {
if (caSelector == null) {
caSelector = new AdaptableX509CertSelector();
/*
* Since we don't check the validity period of trusted
* certificates, please don't set the certificate valid
* criterion unless the trusted certificate matching is
* completed.
*/
/*
* Policy processing optimizations
*/
if (buildParams.explicitPolicyRequired())
caSelector.setPolicy(getMatchingPolicies());
}
/*
* Match on subject (issuer of previous cert)
*/
caSelector.setSubject(currentState.issuerDN);
/*
* check the validity period
*/
caSelector.setValidityPeriod(currentState.cert.getNotBefore(),
currentState.cert.getNotAfter());
sel = caSelector;
}
/*
* For compatibility, conservatively, we don't check the path
* length constraint of trusted anchors. Please don't set the
* basic constraints criterion unless the trusted certificate
* matching is completed.
*/
sel.setBasicConstraints(-1);
for (X509Certificate trustedCert : trustedCerts) {
if (sel.match(trustedCert)) {
if (debug != null) {
debug.println("ForwardBuilder.getMatchingCACerts: " +
"found matching trust anchor." +
"\n SN: " +
Debug.toHexString(trustedCert.getSerialNumber()) +
"\n Subject: " +
trustedCert.getSubjectX500Principal() +
"\n Issuer: " +
trustedCert.getIssuerX500Principal());
}
caCerts.add(trustedCert);
}
}
/*
* The trusted certificate matching is completed. We need to match
* on certificate validity date.
*/
sel.setCertificateValid(buildParams.date());
/*
* Require CA certs with a pathLenConstraint that allows
* at least as many CA certs that have already been traversed
*/
sel.setBasicConstraints(currentState.traversedCACerts);
/*
* If we have already traversed as many CA certs as the maxPathLength
* will allow us to, then we don't bother looking through these
* certificate pairs. If maxPathLength has a value of -1, this
* means it is unconstrained, so we always look through the
* certificate pairs.
*/
if (currentState.isInitial() ||
(buildParams.maxPathLength() == -1) ||
(buildParams.maxPathLength() > currentState.traversedCACerts))
{
if (addMatchingCerts(sel, certStores,
caCerts, searchAllCertStores)
&& !searchAllCertStores) {
return;
}
}
if (!currentState.isInitial() && Builder.USE_AIA) {
// check for AuthorityInformationAccess extension
AuthorityInfoAccessExtension aiaExt =
currentState.cert.getAuthorityInfoAccessExtension();
if (aiaExt != null) {
getCerts(aiaExt, caCerts);
}
}
if (debug != null) {
int numCerts = caCerts.size() - initialSize;
debug.println("ForwardBuilder.getMatchingCACerts: found " +
numCerts + " CA certs");
}
}
// Thread-local gate to prevent recursive provider lookups
private static ThreadLocal<Object> gate = new ThreadLocal<>();
/**
* Download certificates from the given AIA and add them to the
* specified Collection.
*/
// cs.getCertificates(caSelector) returns a collection of X509Certificate's
// because of the selector, so the cast is safe
@SuppressWarnings("unchecked")
private boolean getCerts(AuthorityInfoAccessExtension aiaExt,
Collection<X509Certificate> certs)
{
if (Builder.USE_AIA == false) {
return false;
}
List<AccessDescription> adList = aiaExt.getAccessDescriptions();
if (adList == null || adList.isEmpty()) {
return false;
}
if (gate.get() != null) {
// Avoid recursive fetching of certificates
if (debug != null) {
debug.println("Recursive fetching of certs via the AIA " +
"extension detected");
}
return false;
}
gate.set(gate);
try {
boolean add = false;
for (AccessDescription ad : adList) {
CertStore cs = URICertStore.getInstance(ad);
if (cs != null) {
try {
if (certs.addAll((Collection<X509Certificate>)
cs.getCertificates(caSelector))) {
add = true;
if (!searchAllCertStores) {
return true;
}
}
} catch (CertStoreException cse) {
if (debug != null) {
debug.println("exception getting certs from CertStore:");
cse.printStackTrace();
}
}
}
}
return add;
} finally {
gate.set(null);
}
}
/**
* This inner class compares 2 PKIX certificates according to which
* should be tried first when building a path from the target.
* The preference order is as follows:
*
* Given trusted certificate(s):
* Subject:ou=D,ou=C,o=B,c=A
*
* Preference order for current cert:
*
* 1) The key identifier of an AKID extension (if present) in the
* previous certificate matches the key identifier in the SKID extension
*
* 2) Issuer matches a trusted subject
* Issuer: ou=D,ou=C,o=B,c=A
*
* 3) Issuer is a descendant of a trusted subject (in order of
* number of links to the trusted subject)
* a) Issuer: ou=E,ou=D,ou=C,o=B,c=A [links=1]
* b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A [links=2]
*
* 4) Issuer is an ancestor of a trusted subject (in order of number of
* links to the trusted subject)
* a) Issuer: ou=C,o=B,c=A [links=1]
* b) Issuer: o=B,c=A [links=2]
*
* 5) Issuer is in the same namespace as a trusted subject (in order of
* number of links to the trusted subject)
* a) Issuer: ou=G,ou=C,o=B,c=A [links=2]
* b) Issuer: ou=H,o=B,c=A [links=3]
*
* 6) Issuer is an ancestor of certificate subject (in order of number
* of links to the certificate subject)
* a) Issuer: ou=K,o=J,c=A
* Subject: ou=L,ou=K,o=J,c=A
* b) Issuer: o=J,c=A
* Subject: ou=L,ou=K,0=J,c=A
*
* 7) Any other certificates
*/
static class PKIXCertComparator implements Comparator<X509Certificate> {
static final String METHOD_NME = "PKIXCertComparator.compare()";
private final Set<X500Principal> trustedSubjectDNs;
private final X509CertSelector certSkidSelector;
PKIXCertComparator(Set<X500Principal> trustedSubjectDNs,
X509CertImpl previousCert) throws IOException {
this.trustedSubjectDNs = trustedSubjectDNs;
this.certSkidSelector = getSelector(previousCert);
}
/**
* Returns an X509CertSelector for matching on the authority key
* identifier, or null if not applicable.
*/
private X509CertSelector getSelector(X509CertImpl previousCert)
throws IOException {
if (previousCert != null) {
AuthorityKeyIdentifierExtension akidExt =
previousCert.getAuthorityKeyIdentifierExtension();
if (akidExt != null) {
byte[] skid = akidExt.getEncodedKeyIdentifier();
if (skid != null) {
X509CertSelector selector = new X509CertSelector();
selector.setSubjectKeyIdentifier(skid);
return selector;
}
}
}
return null;
}
/**
* @param oCert1 First X509Certificate to be compared
* @param oCert2 Second X509Certificate to be compared
* @return -1 if oCert1 is preferable to oCert2, or
* if oCert1 and oCert2 are equally preferable (in this
* case it doesn't matter which is preferable, but we don't
* return 0 because the comparator would behave strangely
* when used in a SortedSet).
* 1 if oCert2 is preferable to oCert1
* 0 if oCert1.equals(oCert2). We only return 0 if the
* certs are equal so that this comparator behaves
* correctly when used in a SortedSet.
* @throws ClassCastException if either argument is not of type
* X509Certificate
*/
@Override
public int compare(X509Certificate oCert1, X509Certificate oCert2) {
// if certs are the same, return 0
if (oCert1.equals(oCert2)) return 0;
// If akid/skid match then it is preferable
if (certSkidSelector != null) {
if (certSkidSelector.match(oCert1)) {
return -1;
}
if (certSkidSelector.match(oCert2)) {
return 1;
}
}
X500Principal cIssuer1 = oCert1.getIssuerX500Principal();
X500Principal cIssuer2 = oCert2.getIssuerX500Principal();
X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1);
X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2);
if (debug != null) {
debug.println(METHOD_NME + " o1 Issuer: " + cIssuer1);
debug.println(METHOD_NME + " o2 Issuer: " + cIssuer2);
}
/* If one cert's issuer matches a trusted subject, then it is
* preferable.
*/
if (debug != null) {
debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST...");
}
boolean m1 = trustedSubjectDNs.contains(cIssuer1);
boolean m2 = trustedSubjectDNs.contains(cIssuer2);
if (debug != null) {
debug.println(METHOD_NME + " m1: " + m1);
debug.println(METHOD_NME + " m2: " + m2);
}
if (m1 && m2) {
return -1;
} else if (m1) {
return -1;
} else if (m2) {
return 1;
}
/* If one cert's issuer is a naming descendant of a trusted subject,
* then it is preferable, in order of increasing naming distance.
*/
if (debug != null) {
debug.println(METHOD_NME + " NAMING DESCENDANT TEST...");
}
for (X500Principal tSubject : trustedSubjectDNs) {
X500Name tSubjectName = X500Name.asX500Name(tSubject);
int distanceTto1 =
Builder.distance(tSubjectName, cIssuer1Name, -1);
int distanceTto2 =
Builder.distance(tSubjectName, cIssuer2Name, -1);
if (debug != null) {
debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
}
if (distanceTto1 > 0 || distanceTto2 > 0) {
if (distanceTto1 == distanceTto2) {
return -1;
} else if (distanceTto1 > 0 && distanceTto2 <= 0) {
return -1;
} else if (distanceTto1 <= 0 && distanceTto2 > 0) {
return 1;
} else if (distanceTto1 < distanceTto2) {
return -1;
} else { // distanceTto1 > distanceTto2
return 1;
}
}
}
/* If one cert's issuer is a naming ancestor of a trusted subject,
* then it is preferable, in order of increasing naming distance.
*/
if (debug != null) {
debug.println(METHOD_NME + " NAMING ANCESTOR TEST...");
}
for (X500Principal tSubject : trustedSubjectDNs) {
X500Name tSubjectName = X500Name.asX500Name(tSubject);
int distanceTto1 = Builder.distance
(tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
int distanceTto2 = Builder.distance
(tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
if (debug != null) {
debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
}
if (distanceTto1 < 0 || distanceTto2 < 0) {
if (distanceTto1 == distanceTto2) {
return -1;
} else if (distanceTto1 < 0 && distanceTto2 >= 0) {
return -1;
} else if (distanceTto1 >= 0 && distanceTto2 < 0) {
return 1;
} else if (distanceTto1 > distanceTto2) {
return -1;
} else {
return 1;
}
}
}
/* If one cert's issuer is in the same namespace as a trusted
* subject, then it is preferable, in order of increasing naming
* distance.
*/
if (debug != null) {
debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST...");
}
for (X500Principal tSubject : trustedSubjectDNs) {
X500Name tSubjectName = X500Name.asX500Name(tSubject);
X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name);
X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name);
if (debug != null) {
debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1));
debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2));
}
if (tAo1 != null || tAo2 != null) {
if (tAo1 != null && tAo2 != null) {
int hopsTto1 = Builder.hops
(tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
int hopsTto2 = Builder.hops
(tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
if (debug != null) {
debug.println(METHOD_NME +" hopsTto1: " + hopsTto1);
debug.println(METHOD_NME +" hopsTto2: " + hopsTto2);
}
if (hopsTto1 == hopsTto2) {
} else if (hopsTto1 > hopsTto2) {
return 1;
} else { // hopsTto1 < hopsTto2
return -1;
}
} else if (tAo1 == null) {
return 1;
} else {
return -1;
}
}
}
/* If one cert's issuer is an ancestor of that cert's subject,
* then it is preferable, in order of increasing naming distance.
*/
if (debug != null) {
debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST...");
}
X500Principal cSubject1 = oCert1.getSubjectX500Principal();
X500Principal cSubject2 = oCert2.getSubjectX500Principal();
X500Name cSubject1Name = X500Name.asX500Name(cSubject1);
X500Name cSubject2Name = X500Name.asX500Name(cSubject2);
if (debug != null) {
debug.println(METHOD_NME + " o1 Subject: " + cSubject1);
debug.println(METHOD_NME + " o2 Subject: " + cSubject2);
}
int distanceStoI1 = Builder.distance
(cSubject1Name, cIssuer1Name, Integer.MAX_VALUE);
int distanceStoI2 = Builder.distance
(cSubject2Name, cIssuer2Name, Integer.MAX_VALUE);
if (debug != null) {
debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1);
debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2);
}
if (distanceStoI2 > distanceStoI1) {
return -1;
} else if (distanceStoI2 < distanceStoI1) {
return 1;
}
/* Otherwise, certs are equally preferable.
*/
if (debug != null) {
debug.println(METHOD_NME + " no tests matched; RETURN 0");
}
return -1;
}
}
/**
* Verifies a matching certificate.
*
* This method executes the validation steps in the PKIX path
* validation algorithm <draft-ietf-pkix-new-part1-08.txt> which were
* not satisfied by the selection criteria used by getCertificates()
* to find the certs and only the steps that can be executed in a
* forward direction (target to trust anchor). Those steps that can
* only be executed in a reverse direction are deferred until the
* complete path has been built.
*
* Trust anchor certs are not validated.
*
* If the last certificate is being verified (the one whose subject
* matches the target subject, then steps in 6.1.4 of the PKIX
* 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
*/
@Override
void verifyCert(X509Certificate cert, State currentState,
List<X509Certificate> certPathList)
throws GeneralSecurityException
{
if (debug != null) {
debug.println("ForwardBuilder.verifyCert(SN: "
+ Debug.toHexString(cert.getSerialNumber())
+ "\n Issuer: " + cert.getIssuerX500Principal() + ")"
+ "\n Subject: " + cert.getSubjectX500Principal() + ")");
}
ForwardState currState = (ForwardState)currentState;
// Don't bother to verify untrusted certificate more.
currState.untrustedChecker.check(cert, Collections.<String>emptySet());
/* check if trusted cert */
boolean isTrustedCert = trustedCerts.contains(cert);
/* we don't perform any validation of the trusted cert */
if (!isTrustedCert) {
/*
* Check CRITICAL private extensions for user checkers that
* support forward checking (forwardCheckers) and remove
* ones we know how to check.
*/
Set<String> unresCritExts = cert.getCriticalExtensionOIDs();
if (unresCritExts == null) {
unresCritExts = Collections.<String>emptySet();
}
for (PKIXCertPathChecker checker : currState.forwardCheckers) {
checker.check(cert, unresCritExts);
}
/*
* Remove extensions from user checkers that don't support
* forward checking. After this step, we will have removed
* all extensions that all user checkers are capable of
* processing.
*/
for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) {
if (!checker.isForwardCheckingSupported()) {
Set<String> supportedExts = checker.getSupportedExtensions();
if (supportedExts != null) {
unresCritExts.removeAll(supportedExts);
}
}
}
/*
* Look at the remaining extensions and remove any ones we know how
* to check. If there are any left, throw an exception!
*/
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 this is the target certificate (init=true), then we are
* not able to do any more verification, so just return
*/
if (currState.isInitial()) {
return;
}
/* we don't perform any validation of the trusted cert */
if (!isTrustedCert) {
/* Make sure this is a CA cert */
if (cert.getBasicConstraints() == -1) {
throw new CertificateException("cert is NOT a CA cert");
}
/*
* Check keyUsage extension
*/
KeyChecker.verifyCAKeyUsage(cert);
}
}
/**
* Verifies whether the input certificate completes the path.
* First checks the cert against each trust anchor that was specified,
* in order, and returns true if the cert matches the trust anchor
* specified as a certificate or has the same key and subject of an anchor
* specified as a trusted {pubkey, caname} pair.
* If no match has been found, does a second check of the cert against
* anchors specified as a trusted {pubkey, caname} pair to see if the cert
* was issued by that anchor.
* Returns false if none of the trust anchors are valid for this cert.
*
* @param cert the certificate to test
* @return a boolean value indicating whether the cert completes the path.
*/
@Override
boolean isPathCompleted(X509Certificate cert) {
List<TrustAnchor> otherAnchors = new ArrayList<>();
// first, check if cert is already trusted
for (TrustAnchor anchor : trustAnchors) {
if (anchor.getTrustedCert() != null) {
if (cert.equals(anchor.getTrustedCert())) {
this.trustAnchor = anchor;
return true;
} else {
continue;
}
}
X500Principal principal = anchor.getCA();
PublicKey publicKey = anchor.getCAPublicKey();
if (principal != null && publicKey != null &&
principal.equals(cert.getSubjectX500Principal())) {
if (publicKey.equals(cert.getPublicKey())) {
// the cert itself is a trust anchor
this.trustAnchor = anchor;
return true;
}
// else, it is a self-issued certificate of the anchor
}
otherAnchors.add(anchor);
}
// next, check if cert is issued by anchor specified by key/name
for (TrustAnchor anchor : otherAnchors) {
X500Principal principal = anchor.getCA();
PublicKey publicKey = anchor.getCAPublicKey();
// Check subject/issuer name chaining
if (principal == null ||
!principal.equals(cert.getIssuerX500Principal())) {
continue;
}
// skip anchor if it contains a DSA key with no DSA params
if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) {
continue;
}
/*
* Check signature
*/
try {
cert.verify(publicKey, buildParams.sigProvider());
} catch (InvalidKeyException ike) {
if (debug != null) {
debug.println("ForwardBuilder.isPathCompleted() invalid "
+ "DSA key found");
}
continue;
} catch (GeneralSecurityException e){
if (debug != null) {
debug.println("ForwardBuilder.isPathCompleted() " +
"unexpected exception");
e.printStackTrace();
}
continue;
}
this.trustAnchor = anchor;
return true;
}
return false;
}
/** Adds the certificate to the certPathList
*
* @param cert the certificate to be added
* @param certPathList the certification path list
*/
@Override
void addCertToPath(X509Certificate cert,
LinkedList<X509Certificate> certPathList)
{
certPathList.addFirst(cert);
}
/** Removes final certificate from the certPathList
*
* @param certPathList the certification path list
*/
@Override
void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
certPathList.removeFirst();
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2000, 2023, 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.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.security.auth.x500.X500Principal;
import sun.security.util.Debug;
import sun.security.x509.X509CertImpl;
/**
* A specification of a forward PKIX validation state
* which is initialized by each build and updated each time a
* certificate is added to the current path.
* @since 1.4
* @author Yassir Elley
*/
class ForwardState implements State {
private static final Debug debug = Debug.getInstance("certpath");
/* The issuer DN of the last cert in the path */
X500Principal issuerDN;
/* The last cert in the path */
X509CertImpl cert;
/*
* The number of intermediate CA certs which have been traversed so
* far in the path
*/
int traversedCACerts;
/* Flag indicating if state is initial (path is just starting) */
private boolean init = true;
/* the untrusted certificates checker */
UntrustedChecker untrustedChecker;
/* The list of user-defined checkers that support forward checking */
ArrayList<PKIXCertPathChecker> forwardCheckers;
/* Flag indicating if last cert in path is self-issued */
boolean selfIssued;
/**
* Returns a boolean flag indicating if the state is initial
* (just starting)
*
* @return boolean flag indicating if the state is initial (just starting)
*/
@Override
public boolean isInitial() {
return init;
}
/**
* Display state for debugging purposes
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("State [");
sb.append("\n issuerDN of last cert: ").append(issuerDN);
sb.append("\n traversedCACerts: ").append(traversedCACerts);
sb.append("\n init: ").append(String.valueOf(init));
sb.append("\n selfIssued: ").append
(String.valueOf(selfIssued));
sb.append("]\n");
return sb.toString();
}
/**
* Initialize the state.
*
* @param certPathCheckers the list of user-defined PKIXCertPathCheckers
*/
public void initState(List<PKIXCertPathChecker> certPathCheckers)
throws CertPathValidatorException
{
traversedCACerts = 0;
/*
* Populate forwardCheckers with every user-defined checker
* that supports forward checking and initialize the forwardCheckers
*/
forwardCheckers = new ArrayList<PKIXCertPathChecker>();
for (PKIXCertPathChecker checker : certPathCheckers) {
if (checker.isForwardCheckingSupported()) {
checker.init(true);
forwardCheckers.add(checker);
}
}
init = true;
}
/**
* Update the state with the next certificate added to the path.
*
* @param cert the certificate which is used to update the state
*/
@Override
public void updateState(X509Certificate cert)
throws CertificateException, IOException, CertPathValidatorException {
if (cert == null)
return;
X509CertImpl icert = X509CertImpl.toImpl(cert);
/* update certificate */
this.cert = icert;
/* update issuer DN */
issuerDN = cert.getIssuerX500Principal();
selfIssued = X509CertImpl.isSelfIssued(cert);
if (!selfIssued) {
/*
* update traversedCACerts only if this is a non-self-issued
* intermediate CA cert
*/
if (!init && cert.getBasicConstraints() != -1) {
traversedCACerts++;
}
}
init = false;
}
/*
* Clone current state. The state is cloned as each cert is
* added to the path. This is necessary if backtracking occurs,
* and a prior state needs to be restored.
*/
@Override
@SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
public Object clone() {
try {
ForwardState clonedState = (ForwardState) super.clone();
/* clone checkers, if cloneable */
clonedState.forwardCheckers = (ArrayList<PKIXCertPathChecker>)
forwardCheckers.clone();
ListIterator<PKIXCertPathChecker> li =
clonedState.forwardCheckers.listIterator();
while (li.hasNext()) {
PKIXCertPathChecker checker = li.next();
if (checker instanceof Cloneable) {
li.set((PKIXCertPathChecker)checker.clone());
}
}
return clonedState;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString(), e);
}
}
}

View File

@@ -0,0 +1,424 @@
/*
* Copyright (c) 2002, 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.util.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.*;
import javax.security.auth.x500.X500Principal;
/**
* A <code>CertStore</code> that retrieves <code>Certificates</code> and
* <code>CRL</code>s from a <code>Collection</code>.
* <p>
* This implementation is functionally equivalent to CollectionCertStore
* with two differences:
* <ol>
* <li>Upon construction, the elements in the specified Collection are
* partially indexed. X509Certificates are indexed by subject, X509CRLs
* by issuer, non-X509 Certificates and CRLs are copied without indexing,
* other objects are ignored. This increases CertStore construction time
* but allows significant speedups for searches which specify the indexed
* attributes, in particular for large Collections (reduction from linear
* time to effectively constant time). Searches for non-indexed queries
* are as fast (or marginally faster) than for the standard
* CollectionCertStore. Certificate subjects and CRL issuers
* were found to be specified in most searches used internally by the
* CertPath provider. Additional attributes could indexed if there are
* queries that justify the effort.
*
* <li>Changes to the specified Collection after construction time are
* not detected and ignored. This is because there is no way to efficiently
* detect if a Collection has been modified, a full traversal would be
* required. That would degrade lookup performance to linear time and
* eliminated the benefit of indexing. We may fix this via the introduction
* of new public APIs in the future.
* </ol>
* <p>
* Before calling the {@link #engineGetCertificates engineGetCertificates} or
* {@link #engineGetCRLs engineGetCRLs} methods, the
* {@link #CollectionCertStore(CertStoreParameters)
* CollectionCertStore(CertStoreParameters)} constructor is called to
* create the <code>CertStore</code> and establish the
* <code>Collection</code> from which <code>Certificate</code>s and
* <code>CRL</code>s will be retrieved. If the specified
* <code>Collection</code> contains an object that is not a
* <code>Certificate</code> or <code>CRL</code>, that object will be
* ignored.
* <p>
* <b>Concurrent Access</b>
* <p>
* As described in the javadoc for <code>CertStoreSpi</code>, the
* <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
* must be thread-safe. That is, multiple threads may concurrently
* invoke these methods on a single <code>CollectionCertStore</code>
* object (or more than one) with no ill effects.
* <p>
* This is achieved by requiring that the <code>Collection</code> passed to
* the {@link #CollectionCertStore(CertStoreParameters)
* CollectionCertStore(CertStoreParameters)} constructor (via the
* <code>CollectionCertStoreParameters</code> object) must have fail-fast
* iterators. Simultaneous modifications to the <code>Collection</code> can thus be
* detected and certificate or CRL retrieval can be retried. The fact that
* <code>Certificate</code>s and <code>CRL</code>s must be thread-safe is also
* essential.
*
* @see java.security.cert.CertStore
* @see CollectionCertStore
*
* @author Andreas Sterbenz
*/
public class IndexedCollectionCertStore extends CertStoreSpi {
/**
* Map X500Principal(subject) -> X509Certificate | List of X509Certificate
*/
private Map<X500Principal, Object> certSubjects;
/**
* Map X500Principal(issuer) -> X509CRL | List of X509CRL
*/
private Map<X500Principal, Object> crlIssuers;
/**
* Sets of non-X509 certificates and CRLs
*/
private Set<Certificate> otherCertificates;
private Set<CRL> otherCRLs;
/**
* Creates a <code>CertStore</code> with the specified parameters.
* For this class, the parameters object must be an instance of
* <code>CollectionCertStoreParameters</code>.
*
* @param params the algorithm parameters
* @exception InvalidAlgorithmParameterException if params is not an
* instance of <code>CollectionCertStoreParameters</code>
*/
public IndexedCollectionCertStore(CertStoreParameters params)
throws InvalidAlgorithmParameterException {
super(params);
if (!(params instanceof CollectionCertStoreParameters)) {
throw new InvalidAlgorithmParameterException(
"parameters must be CollectionCertStoreParameters");
}
Collection<?> coll = ((CollectionCertStoreParameters)params).getCollection();
if (coll == null) {
throw new InvalidAlgorithmParameterException
("Collection must not be null");
}
buildIndex(coll);
}
/**
* Index the specified Collection copying all references to Certificates
* and CRLs.
*/
private void buildIndex(Collection<?> coll) {
certSubjects = new HashMap<X500Principal, Object>();
crlIssuers = new HashMap<X500Principal, Object>();
otherCertificates = null;
otherCRLs = null;
for (Object obj : coll) {
if (obj instanceof X509Certificate) {
indexCertificate((X509Certificate)obj);
} else if (obj instanceof X509CRL) {
indexCRL((X509CRL)obj);
} else if (obj instanceof Certificate) {
if (otherCertificates == null) {
otherCertificates = new HashSet<Certificate>();
}
otherCertificates.add((Certificate)obj);
} else if (obj instanceof CRL) {
if (otherCRLs == null) {
otherCRLs = new HashSet<CRL>();
}
otherCRLs.add((CRL)obj);
} else {
// ignore
}
}
if (otherCertificates == null) {
otherCertificates = Collections.<Certificate>emptySet();
}
if (otherCRLs == null) {
otherCRLs = Collections.<CRL>emptySet();
}
}
/**
* Add an X509Certificate to the index.
*/
private void indexCertificate(X509Certificate cert) {
X500Principal subject = cert.getSubjectX500Principal();
Object oldEntry = certSubjects.put(subject, cert);
if (oldEntry != null) { // assume this is unlikely
if (oldEntry instanceof X509Certificate) {
if (cert.equals(oldEntry)) {
return;
}
List<X509Certificate> list = new ArrayList<>(2);
list.add(cert);
list.add((X509Certificate)oldEntry);
certSubjects.put(subject, list);
} else {
@SuppressWarnings("unchecked") // See certSubjects javadoc.
List<X509Certificate> list = (List<X509Certificate>)oldEntry;
if (list.contains(cert) == false) {
list.add(cert);
}
certSubjects.put(subject, list);
}
}
}
/**
* Add an X509CRL to the index.
*/
private void indexCRL(X509CRL crl) {
X500Principal issuer = crl.getIssuerX500Principal();
Object oldEntry = crlIssuers.put(issuer, crl);
if (oldEntry != null) { // assume this is unlikely
if (oldEntry instanceof X509CRL) {
if (crl.equals(oldEntry)) {
return;
}
List<X509CRL> list = new ArrayList<>(2);
list.add(crl);
list.add((X509CRL)oldEntry);
crlIssuers.put(issuer, list);
} else {
// See crlIssuers javadoc.
@SuppressWarnings("unchecked")
List<X509CRL> list = (List<X509CRL>)oldEntry;
if (list.contains(crl) == false) {
list.add(crl);
}
crlIssuers.put(issuer, list);
}
}
}
/**
* Returns a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector. If no <code>Certificate</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector a <code>CertSelector</code> used to select which
* <code>Certificate</code>s should be returned. Specify <code>null</code>
* to return all <code>Certificate</code>s.
* @return a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public Collection<? extends Certificate> engineGetCertificates(CertSelector selector)
throws CertStoreException {
// no selector means match all
if (selector == null) {
Set<Certificate> matches = new HashSet<>();
matchX509Certs(new X509CertSelector(), matches);
matches.addAll(otherCertificates);
return matches;
}
if (selector instanceof X509CertSelector == false) {
Set<Certificate> matches = new HashSet<>();
matchX509Certs(selector, matches);
for (Certificate cert : otherCertificates) {
if (selector.match(cert)) {
matches.add(cert);
}
}
return matches;
}
if (certSubjects.isEmpty()) {
return Collections.<X509Certificate>emptySet();
}
X509CertSelector x509Selector = (X509CertSelector)selector;
// see if the subject is specified
X500Principal subject;
X509Certificate matchCert = x509Selector.getCertificate();
if (matchCert != null) {
subject = matchCert.getSubjectX500Principal();
} else {
subject = x509Selector.getSubject();
}
if (subject != null) {
// yes, narrow down candidates to indexed possibilities
Object entry = certSubjects.get(subject);
if (entry == null) {
return Collections.<X509Certificate>emptySet();
}
if (entry instanceof X509Certificate) {
X509Certificate x509Entry = (X509Certificate)entry;
if (x509Selector.match(x509Entry)) {
return Collections.singleton(x509Entry);
} else {
return Collections.<X509Certificate>emptySet();
}
} else {
// See certSubjects javadoc.
@SuppressWarnings("unchecked")
List<X509Certificate> list = (List<X509Certificate>)entry;
Set<X509Certificate> matches = new HashSet<>(16);
for (X509Certificate cert : list) {
if (x509Selector.match(cert)) {
matches.add(cert);
}
}
return matches;
}
}
// cannot use index, iterate all
Set<Certificate> matches = new HashSet<>(16);
matchX509Certs(x509Selector, matches);
return matches;
}
/**
* Iterate through all the X509Certificates and add matches to the
* collection.
*/
private void matchX509Certs(CertSelector selector,
Collection<Certificate> matches) {
for (Object obj : certSubjects.values()) {
if (obj instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)obj;
if (selector.match(cert)) {
matches.add(cert);
}
} else {
// See certSubjects javadoc.
@SuppressWarnings("unchecked")
List<X509Certificate> list = (List<X509Certificate>)obj;
for (X509Certificate cert : list) {
if (selector.match(cert)) {
matches.add(cert);
}
}
}
}
}
/**
* Returns a <code>Collection</code> of <code>CRL</code>s that
* match the specified selector. If no <code>CRL</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector a <code>CRLSelector</code> used to select which
* <code>CRL</code>s should be returned. Specify <code>null</code>
* to return all <code>CRL</code>s.
* @return a <code>Collection</code> of <code>CRL</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public Collection<CRL> engineGetCRLs(CRLSelector selector)
throws CertStoreException {
if (selector == null) {
Set<CRL> matches = new HashSet<>();
matchX509CRLs(new X509CRLSelector(), matches);
matches.addAll(otherCRLs);
return matches;
}
if (selector instanceof X509CRLSelector == false) {
Set<CRL> matches = new HashSet<>();
matchX509CRLs(selector, matches);
for (CRL crl : otherCRLs) {
if (selector.match(crl)) {
matches.add(crl);
}
}
return matches;
}
if (crlIssuers.isEmpty()) {
return Collections.<CRL>emptySet();
}
X509CRLSelector x509Selector = (X509CRLSelector)selector;
// see if the issuer is specified
Collection<X500Principal> issuers = x509Selector.getIssuers();
if (issuers != null) {
HashSet<CRL> matches = new HashSet<>(16);
for (X500Principal issuer : issuers) {
Object entry = crlIssuers.get(issuer);
if (entry == null) {
// empty
} else if (entry instanceof X509CRL) {
X509CRL crl = (X509CRL)entry;
if (x509Selector.match(crl)) {
matches.add(crl);
}
} else { // List
// See crlIssuers javadoc.
@SuppressWarnings("unchecked")
List<X509CRL> list = (List<X509CRL>)entry;
for (X509CRL crl : list) {
if (x509Selector.match(crl)) {
matches.add(crl);
}
}
}
}
return matches;
}
// cannot use index, iterate all
Set<CRL> matches = new HashSet<>(16);
matchX509CRLs(x509Selector, matches);
return matches;
}
/**
* Iterate through all the X509CRLs and add matches to the
* collection.
*/
private void matchX509CRLs(CRLSelector selector, Collection<CRL> matches) {
for (Object obj : crlIssuers.values()) {
if (obj instanceof X509CRL) {
X509CRL crl = (X509CRL)obj;
if (selector.match(crl)) {
matches.add(crl);
}
} else {
// See crlIssuers javadoc.
@SuppressWarnings("unchecked")
List<X509CRL> list = (List<X509CRL>)obj;
for (X509CRL crl : list) {
if (selector.match(crl)) {
matches.add(crl);
}
}
}
}
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.util.*;
import java.security.cert.*;
import java.security.cert.PKIXReason;
import sun.security.util.Debug;
import static sun.security.x509.PKIXExtensions.*;
/**
* KeyChecker is a <code>PKIXCertPathChecker</code> that checks that the
* keyCertSign bit is set in the keyUsage extension in an intermediate CA
* certificate. It also checks whether the final certificate in a
* certification path meets the specified target constraints specified as
* a CertSelector in the PKIXParameters passed to the CertPathValidator.
*
* @since 1.4
* @author Yassir Elley
*/
class KeyChecker extends PKIXCertPathChecker {
private static final Debug debug = Debug.getInstance("certpath");
private final int certPathLen;
private final CertSelector targetConstraints;
private int remainingCerts;
private Set<String> supportedExts;
/**
* Creates a KeyChecker.
*
* @param certPathLen allowable cert path length
* @param targetCertSel a CertSelector object specifying the constraints
* on the target certificate
*/
KeyChecker(int certPathLen, CertSelector targetCertSel) {
this.certPathLen = certPathLen;
this.targetConstraints = targetCertSel;
}
/**
* Initializes the internal state of the checker from parameters
* specified in the constructor
*/
@Override
public void init(boolean forward) throws CertPathValidatorException {
if (!forward) {
remainingCerts = certPathLen;
} else {
throw new CertPathValidatorException
("forward checking not supported");
}
}
@Override
public boolean isForwardCheckingSupported() {
return false;
}
@Override
public Set<String> getSupportedExtensions() {
if (supportedExts == null) {
supportedExts = new HashSet<String>(3);
supportedExts.add(KeyUsage_Id.toString());
supportedExts.add(ExtendedKeyUsage_Id.toString());
supportedExts.add(SubjectAlternativeName_Id.toString());
supportedExts = Collections.unmodifiableSet(supportedExts);
}
return supportedExts;
}
/**
* Checks that keyUsage and target constraints are satisfied by
* the specified certificate.
*
* @param cert the Certificate
* @param unresolvedCritExts the unresolved critical extensions
* @throws CertPathValidatorException if certificate does not verify
*/
@Override
public void check(Certificate cert, Collection<String> unresCritExts)
throws CertPathValidatorException
{
X509Certificate currCert = (X509Certificate)cert;
remainingCerts--;
// if final certificate, check that target constraints are satisfied
if (remainingCerts == 0) {
if (targetConstraints != null &&
targetConstraints.match(currCert) == false) {
throw new CertPathValidatorException("target certificate " +
"constraints check failed");
}
} else {
// otherwise, verify that keyCertSign bit is set in CA certificate
verifyCAKeyUsage(currCert);
}
// remove the extensions that we have checked
if (unresCritExts != null && !unresCritExts.isEmpty()) {
unresCritExts.remove(KeyUsage_Id.toString());
unresCritExts.remove(ExtendedKeyUsage_Id.toString());
unresCritExts.remove(SubjectAlternativeName_Id.toString());
}
}
// the index of keyCertSign in the boolean KeyUsage array
private static final int KEY_CERT_SIGN = 5;
/**
* Verifies the key usage extension in a CA cert.
* The key usage extension, if present, must assert the keyCertSign bit.
* The extended key usage extension is not checked (see CR 4776794 for
* more information).
*/
static void verifyCAKeyUsage(X509Certificate cert)
throws CertPathValidatorException {
String msg = "CA key usage";
if (debug != null) {
debug.println("KeyChecker.verifyCAKeyUsage() ---checking " + msg
+ "...");
}
boolean[] keyUsageBits = cert.getKeyUsage();
// getKeyUsage returns null if the KeyUsage extension is not present
// in the certificate - in which case there is nothing to check
if (keyUsageBits == null) {
return;
}
// throw an exception if the keyCertSign bit is not set
if (!keyUsageBits[KEY_CERT_SIGN]) {
throw new CertPathValidatorException
(msg + " check failed: keyCertSign bit is not set", null,
null, -1, PKIXReason.INVALID_KEY_USAGE);
}
if (debug != null) {
debug.println("KeyChecker.verifyCAKeyUsage() " + msg
+ " verified.");
}
}
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright (c) 2009, 2017, 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.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.HttpURLConnection;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.CRLReason;
import java.security.cert.Extension;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import sun.security.action.GetIntegerAction;
import sun.security.util.Debug;
import sun.security.validator.Validator;
import sun.security.x509.AccessDescription;
import sun.security.x509.AuthorityInfoAccessExtension;
import sun.security.x509.GeneralName;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.PKIXExtensions;
import sun.security.x509.URIName;
import sun.security.x509.X509CertImpl;
/**
* This is a class that checks the revocation status of a certificate(s) using
* OCSP. It is not a PKIXCertPathChecker and therefore can be used outside of
* the CertPathValidator framework. It is useful when you want to
* just check the revocation status of a certificate, and you don't want to
* incur the overhead of validating all of the certificates in the
* associated certificate chain.
*
* @author Sean Mullan
*/
public final class OCSP {
private static final Debug debug = Debug.getInstance("certpath");
private static final int DEFAULT_CONNECT_TIMEOUT = 15000;
/**
* Integer value indicating the timeout length, in seconds, to be
* used for the OCSP check. A timeout of zero is interpreted as
* an infinite timeout.
*/
private static final int CONNECT_TIMEOUT = initializeTimeout();
/**
* Initialize the timeout length by getting the OCSP timeout
* system property. If the property has not been set, or if its
* value is negative, set the timeout length to the default.
*/
private static int initializeTimeout() {
Integer tmp = java.security.AccessController.doPrivileged(
new GetIntegerAction("com.sun.security.ocsp.timeout"));
if (tmp == null || tmp < 0) {
return DEFAULT_CONNECT_TIMEOUT;
}
// Convert to milliseconds, as the system property will be
// specified in seconds
return tmp * 1000;
}
private OCSP() {}
/**
* Obtains the revocation status of a certificate using OCSP.
*
* @param cert the certificate to be checked
* @param issuerCert the issuer certificate
* @param responderURI the URI of the OCSP responder
* @param responderCert the OCSP responder's certificate
* @param date the time the validity of the OCSP responder's certificate
* should be checked against. If null, the current time is used.
* @return the RevocationStatus
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
* @throws CertPathValidatorException if an exception occurs while
* encoding the OCSP Request or validating the OCSP Response
*/
// Called by com.sun.deploy.security.TrustDecider
public static RevocationStatus check(X509Certificate cert,
X509Certificate issuerCert,
URI responderURI,
X509Certificate responderCert,
Date date)
throws IOException, CertPathValidatorException
{
return check(cert, issuerCert, responderURI, responderCert, date,
Collections.<Extension>emptyList(),
Validator.VAR_PLUGIN_CODE_SIGNING);
}
public static RevocationStatus check(X509Certificate cert,
X509Certificate issuerCert, URI responderURI,
X509Certificate responderCert, Date date, List<Extension> extensions,
String variant)
throws IOException, CertPathValidatorException
{
return check(cert, responderURI, null, issuerCert, responderCert, date,
extensions, variant);
}
public static RevocationStatus check(X509Certificate cert,
URI responderURI, TrustAnchor anchor, X509Certificate issuerCert,
X509Certificate responderCert, Date date,
List<Extension> extensions, String variant)
throws IOException, CertPathValidatorException
{
CertId certId;
try {
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
certId = new CertId(issuerCert, certImpl.getSerialNumberObject());
} catch (CertificateException | IOException e) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", e);
}
OCSPResponse ocspResponse = check(Collections.singletonList(certId),
responderURI, new OCSPResponse.IssuerInfo(anchor, issuerCert),
responderCert, date, extensions, variant);
return (RevocationStatus) ocspResponse.getSingleResponse(certId);
}
/**
* Checks the revocation status of a list of certificates using OCSP.
*
* @param certIds the CertIds to be checked
* @param responderURI the URI of the OCSP responder
* @param issuerInfo the issuer's certificate and/or subject and public key
* @param responderCert the OCSP responder's certificate
* @param date the time the validity of the OCSP responder's certificate
* should be checked against. If null, the current time is used.
* @param extensions zero or more OCSP extensions to be included in the
* request. If no extensions are requested, an empty {@code List} must
* be used. A {@code null} value is not allowed.
* @return the OCSPResponse
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
* @throws CertPathValidatorException if an exception occurs while
* encoding the OCSP Request or validating the OCSP Response
*/
static OCSPResponse check(List<CertId> certIds, URI responderURI,
OCSPResponse.IssuerInfo issuerInfo,
X509Certificate responderCert, Date date,
List<Extension> extensions, String variant)
throws IOException, CertPathValidatorException
{
byte[] nonce = null;
for (Extension ext : extensions) {
if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) {
nonce = ext.getValue();
}
}
OCSPResponse ocspResponse = null;
try {
byte[] response = getOCSPBytes(certIds, responderURI, extensions);
ocspResponse = new OCSPResponse(response);
// verify the response
ocspResponse.verify(certIds, issuerInfo, responderCert, date,
nonce, variant);
} catch (IOException ioe) {
throw new CertPathValidatorException(
"Unable to determine revocation status due to network error",
ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
return ocspResponse;
}
/**
* Send an OCSP request, then read and return the OCSP response bytes.
*
* @param certIds the CertIds to be checked
* @param responderURI the URI of the OCSP responder
* @param extensions zero or more OCSP extensions to be included in the
* request. If no extensions are requested, an empty {@code List} must
* be used. A {@code null} value is not allowed.
*
* @return the OCSP response bytes
*
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
*/
public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI,
List<Extension> extensions) throws IOException {
OCSPRequest request = new OCSPRequest(certIds, extensions);
byte[] bytes = request.encodeBytes();
InputStream in = null;
OutputStream out = null;
byte[] response = null;
try {
URL url = responderURI.toURL();
if (debug != null) {
debug.println("connecting to OCSP service at: " + url);
}
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(CONNECT_TIMEOUT);
con.setReadTimeout(CONNECT_TIMEOUT);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty
("Content-type", "application/ocsp-request");
con.setRequestProperty
("Content-length", String.valueOf(bytes.length));
out = con.getOutputStream();
out.write(bytes);
out.flush();
// Check the response
if (debug != null &&
con.getResponseCode() != HttpURLConnection.HTTP_OK) {
debug.println("Received HTTP error: " + con.getResponseCode()
+ " - " + con.getResponseMessage());
}
in = con.getInputStream();
int contentLength = con.getContentLength();
if (contentLength == -1) {
contentLength = Integer.MAX_VALUE;
}
response = new byte[contentLength > 2048 ? 2048 : contentLength];
int total = 0;
while (total < contentLength) {
int count = in.read(response, total, response.length - total);
if (count < 0)
break;
total += count;
if (total >= response.length && total < contentLength) {
response = Arrays.copyOf(response, total * 2);
}
}
response = Arrays.copyOf(response, total);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
throw ioe;
}
}
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
throw ioe;
}
}
}
return response;
}
/**
* Returns the URI of the OCSP Responder as specified in the
* certificate's Authority Information Access extension, or null if
* not specified.
*
* @param cert the certificate
* @return the URI of the OCSP Responder, or null if not specified
*/
// Called by com.sun.deploy.security.TrustDecider
public static URI getResponderURI(X509Certificate cert) {
try {
return getResponderURI(X509CertImpl.toImpl(cert));
} catch (CertificateException ce) {
// treat this case as if the cert had no extension
return null;
}
}
static URI getResponderURI(X509CertImpl certImpl) {
// Examine the certificate's AuthorityInfoAccess extension
AuthorityInfoAccessExtension aia =
certImpl.getAuthorityInfoAccessExtension();
if (aia == null) {
return null;
}
List<AccessDescription> descriptions = aia.getAccessDescriptions();
for (AccessDescription description : descriptions) {
if (description.getAccessMethod().equals(
AccessDescription.Ad_OCSP_Id)) {
GeneralName generalName = description.getAccessLocation();
if (generalName.getType() == GeneralNameInterface.NAME_URI) {
URIName uri = (URIName) generalName.getName();
return uri.getURI();
}
}
}
return null;
}
/**
* The Revocation Status of a certificate.
*/
public static interface RevocationStatus {
public enum CertStatus { GOOD, REVOKED, UNKNOWN };
/**
* Returns the revocation status.
*/
CertStatus getCertStatus();
/**
* Returns the time when the certificate was revoked, or null
* if it has not been revoked.
*/
Date getRevocationTime();
/**
* Returns the reason the certificate was revoked, or null if it
* has not been revoked.
*/
CRLReason getRevocationReason();
/**
* Returns a Map of additional extensions.
*/
Map<String, Extension> getSingleExtensions();
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (c) 2015, 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.util.Objects;
import java.security.SecureRandom;
import sun.security.x509.Extension;
import sun.security.x509.PKIXExtensions;
import sun.security.util.Debug;
import sun.security.util.DerValue;
/**
* Represent the OCSP Nonce Extension.
* This extension, if present, provides a nonce value in OCSP requests
* and responses. This will cryptographically bind requests and responses
* and help to prevent replay attacks (see RFC 6960, section 4.4.1).
*
* @see Extension
*/
public final class OCSPNonceExtension extends Extension {
/**
* Attribute name.
*/
private static final String EXTENSION_NAME = "OCSPNonce";
private byte[] nonceData = null;
/**
* Create an {@code OCSPNonceExtension} by providing the nonce length.
* The criticality is set to false, and the OID for the extension will
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
*
* @param length the number of random bytes composing the nonce
*
* @throws IOException if any errors happen during encoding of the
* extension.
* @throws IllegalArgumentException if length is not a positive integer.
*/
public OCSPNonceExtension(int length) throws IOException {
this(false, length);
}
/**
* Create an {@code OCSPNonceExtension} by providing the nonce length and
* criticality setting. The OID for the extension will
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
*
* @param isCritical a boolean flag indicating whether the criticality bit
* is set for this extension
* @param length the number of random bytes composing the nonce
*
* @throws IOException if any errors happen during encoding of the
* extension.
* @throws IllegalArgumentException if length is not a positive integer.
*/
public OCSPNonceExtension(boolean isCritical, int length)
throws IOException {
this.extensionId = PKIXExtensions.OCSPNonce_Id;
this.critical = isCritical;
if (length > 0) {
SecureRandom rng = new SecureRandom();
this.nonceData = new byte[length];
rng.nextBytes(nonceData);
this.extensionValue = new DerValue(DerValue.tag_OctetString,
nonceData).toByteArray();
} else {
throw new IllegalArgumentException(
"Length must be a positive integer");
}
}
/**
* Create an {@code OCSPNonceExtension} by providing a nonce value.
* The criticality is set to false, and the OID for the extension will
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
*
* @param incomingNonce The nonce data to be set for the extension. This
* must be a non-null array of at least one byte long.
*
* @throws IOException if any errors happen during encoding of the
* extension.
* @throws IllegalArgumentException if the incomingNonce length is not a
* positive integer.
* @throws NullPointerException if the incomingNonce is null.
*/
public OCSPNonceExtension(byte[] incomingNonce) throws IOException {
this(false, incomingNonce);
}
/**
* Create an {@code OCSPNonceExtension} by providing a nonce value and
* criticality setting. The OID for the extension will
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
*
* @param isCritical a boolean flag indicating whether the criticality bit
* is set for this extension
* @param incomingNonce The nonce data to be set for the extension. This
* must be a non-null array of at least one byte long.
*
* @throws IOException if any errors happen during encoding of the
* extension.
* @throws IllegalArgumentException if the incomingNonce length is not a
* positive integer.
* @throws NullPointerException if the incomingNonce is null.
*/
public OCSPNonceExtension(boolean isCritical, byte[] incomingNonce)
throws IOException {
this.extensionId = PKIXExtensions.OCSPNonce_Id;
this.critical = isCritical;
Objects.requireNonNull(incomingNonce, "Nonce data must be non-null");
if (incomingNonce.length > 0) {
this.nonceData = incomingNonce.clone();
this.extensionValue = new DerValue(DerValue.tag_OctetString,
nonceData).toByteArray();
} else {
throw new IllegalArgumentException(
"Nonce data must be at least 1 byte in length");
}
}
/**
* Return the nonce bytes themselves, without any DER encoding.
*
* @return A copy of the underlying nonce bytes
*/
public byte[] getNonceValue() {
return nonceData.clone();
}
/**
* Returns a printable representation of the {@code OCSPNonceExtension}.
*
* @return a string representation of the extension.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(EXTENSION_NAME).append(": ");
sb.append((nonceData == null) ? "" : Debug.toString(nonceData));
sb.append("\n");
return sb.toString();
}
/**
* Return the name of the extension as a {@code String}
*
* @return the name of the extension
*/
public String getName() {
return EXTENSION_NAME;
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2003, 2017, 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.cert.Extension;
import java.util.Collections;
import java.util.List;
import sun.misc.HexDumpEncoder;
import sun.security.util.*;
import sun.security.x509.PKIXExtensions;
/**
* This class can be used to generate an OCSP request and send it over
* an output stream. Currently we do not support signing requests.
* The OCSP Request is specified in RFC 2560 and
* the ASN.1 definition is as follows:
* <pre>
*
* OCSPRequest ::= SEQUENCE {
* tbsRequest TBSRequest,
* optionalSignature [0] EXPLICIT Signature OPTIONAL }
*
* TBSRequest ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* requestorName [1] EXPLICIT GeneralName OPTIONAL,
* requestList SEQUENCE OF Request,
* requestExtensions [2] EXPLICIT Extensions OPTIONAL }
*
* Signature ::= SEQUENCE {
* signatureAlgorithm AlgorithmIdentifier,
* signature BIT STRING,
* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
* }
*
* Version ::= INTEGER { v1(0) }
*
* Request ::= SEQUENCE {
* reqCert CertID,
* singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
*
* CertID ::= SEQUENCE {
* hashAlgorithm AlgorithmIdentifier,
* issuerNameHash OCTET STRING, -- Hash of Issuer's DN
* issuerKeyHash OCTET STRING, -- Hash of Issuers public key
* serialNumber CertificateSerialNumber
* }
*
* </pre>
*
* @author Ram Marti
*/
class OCSPRequest {
private static final Debug debug = Debug.getInstance("certpath");
private static final boolean dump = debug != null && Debug.isOn("ocsp");
// List of request CertIds
private final List<CertId> certIds;
private final List<Extension> extensions;
private byte[] nonce;
/*
* Constructs an OCSPRequest. This constructor is used
* to construct an unsigned OCSP Request for a single user cert.
*/
OCSPRequest(CertId certId) {
this(Collections.singletonList(certId));
}
OCSPRequest(List<CertId> certIds) {
this.certIds = certIds;
this.extensions = Collections.<Extension>emptyList();
}
OCSPRequest(List<CertId> certIds, List<Extension> extensions) {
this.certIds = certIds;
this.extensions = extensions;
}
byte[] encodeBytes() throws IOException {
// encode tbsRequest
DerOutputStream tmp = new DerOutputStream();
DerOutputStream requestsOut = new DerOutputStream();
for (CertId certId : certIds) {
DerOutputStream certIdOut = new DerOutputStream();
certId.encode(certIdOut);
requestsOut.write(DerValue.tag_Sequence, certIdOut);
}
tmp.write(DerValue.tag_Sequence, requestsOut);
if (!extensions.isEmpty()) {
DerOutputStream extOut = new DerOutputStream();
for (Extension ext : extensions) {
ext.encode(extOut);
if (ext.getId().equals(
PKIXExtensions.OCSPNonce_Id.toString())) {
nonce = ext.getValue();
}
}
DerOutputStream extsOut = new DerOutputStream();
extsOut.write(DerValue.tag_Sequence, extOut);
tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)2), extsOut);
}
DerOutputStream tbsRequest = new DerOutputStream();
tbsRequest.write(DerValue.tag_Sequence, tmp);
// OCSPRequest without the signature
DerOutputStream ocspRequest = new DerOutputStream();
ocspRequest.write(DerValue.tag_Sequence, tbsRequest);
byte[] bytes = ocspRequest.toByteArray();
if (dump) {
HexDumpEncoder hexEnc = new HexDumpEncoder();
debug.println("OCSPRequest bytes...\n\n" +
hexEnc.encode(bytes) + "\n");
}
return bytes;
}
List<CertId> getCertIds() {
return certIds;
}
byte[] getNonce() {
return nonce;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,350 @@
/*
* Copyright (c) 2012, 2020, 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.security.InvalidAlgorithmParameterException;
import java.security.PublicKey;
import java.security.Timestamp;
import java.security.cert.*;
import java.security.interfaces.DSAPublicKey;
import java.util.*;
import javax.security.auth.x500.X500Principal;
import sun.security.util.Debug;
import sun.security.validator.Validator;
/**
* Common utility methods and classes used by the PKIX CertPathValidator and
* CertPathBuilder implementation.
*/
class PKIX {
private static final Debug debug = Debug.getInstance("certpath");
private PKIX() { }
static boolean isDSAPublicKeyWithoutParams(PublicKey publicKey) {
return (publicKey instanceof DSAPublicKey &&
((DSAPublicKey)publicKey).getParams() == null);
}
static ValidatorParams checkParams(CertPath cp, CertPathParameters params)
throws InvalidAlgorithmParameterException
{
if (!(params instanceof PKIXParameters)) {
throw new InvalidAlgorithmParameterException("inappropriate "
+ "params, must be an instance of PKIXParameters");
}
return new ValidatorParams(cp, (PKIXParameters)params);
}
static BuilderParams checkBuilderParams(CertPathParameters params)
throws InvalidAlgorithmParameterException
{
if (!(params instanceof PKIXBuilderParameters)) {
throw new InvalidAlgorithmParameterException("inappropriate "
+ "params, must be an instance of PKIXBuilderParameters");
}
return new BuilderParams((PKIXBuilderParameters)params);
}
/**
* PKIXParameters that are shared by the PKIX CertPathValidator
* implementation. Provides additional functionality and avoids
* unnecessary cloning.
*/
static class ValidatorParams {
private final PKIXParameters params;
private CertPath certPath;
private List<PKIXCertPathChecker> checkers;
private List<CertStore> stores;
private boolean gotDate;
private Date date;
private Set<String> policies;
private boolean gotConstraints;
private CertSelector constraints;
private Set<TrustAnchor> anchors;
private List<X509Certificate> certs;
private Timestamp timestamp;
private Date timestampDate;
private String variant = Validator.VAR_GENERIC;
ValidatorParams(CertPath cp, PKIXParameters params)
throws InvalidAlgorithmParameterException
{
this(params);
if (!cp.getType().equals("X.509") && !cp.getType().equals("X509")) {
throw new InvalidAlgorithmParameterException("inappropriate "
+ "CertPath type specified, must be X.509 or X509");
}
this.certPath = cp;
}
ValidatorParams(PKIXParameters params)
throws InvalidAlgorithmParameterException
{
if (params instanceof PKIXExtendedParameters) {
timestamp = ((PKIXExtendedParameters) params).getTimestamp();
variant = ((PKIXExtendedParameters) params).getVariant();
}
this.anchors = params.getTrustAnchors();
// Make sure that none of the trust anchors include name constraints
// (not supported).
for (TrustAnchor anchor : this.anchors) {
if (anchor.getNameConstraints() != null) {
throw new InvalidAlgorithmParameterException
("name constraints in trust anchor not supported");
}
}
this.params = params;
}
CertPath certPath() {
return certPath;
}
// called by CertPathBuilder after path has been built
void setCertPath(CertPath cp) {
this.certPath = cp;
}
List<X509Certificate> certificates() {
if (certs == null) {
if (certPath == null) {
certs = Collections.emptyList();
} else {
// Reverse the ordering for validation so that the target
// cert is the last certificate
@SuppressWarnings("unchecked")
List<X509Certificate> xc = new ArrayList<>
((List<X509Certificate>)certPath.getCertificates());
Collections.reverse(xc);
certs = xc;
}
}
return certs;
}
List<PKIXCertPathChecker> certPathCheckers() {
if (checkers == null)
checkers = params.getCertPathCheckers();
return checkers;
}
List<CertStore> certStores() {
if (stores == null)
stores = params.getCertStores();
return stores;
}
// The date() param is used when enforcing the validity period
// of certificates and when checking the time period of revocation data.
// The main difference between the date() and timestamp() method is
// that the date() method only uses the timestamp (if specified)
// for certificates in a code signer's chain.
Date date() {
if (!gotDate) {
// Use timestamp if checking signed code that is
// timestamped, otherwise use date parameter.
// Note that TSA server certificates do not use the
// timestamp, which means that an expired TSA certificate
// is considered a validation failure. This policy means
// that signed and timestamped code is valid until the TSA
// certificate expires (assuming all other checks are valid).
if (timestamp != null &&
(variant.equals(Validator.VAR_CODE_SIGNING) ||
variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING))) {
date = timestamp.getTimestamp();
} else {
date = params.getDate();
if (date == null)
date = new Date();
}
gotDate = true;
}
return date;
}
Set<String> initialPolicies() {
if (policies == null)
policies = params.getInitialPolicies();
return policies;
}
CertSelector targetCertConstraints() {
if (!gotConstraints) {
constraints = params.getTargetCertConstraints();
gotConstraints = true;
}
return constraints;
}
Set<TrustAnchor> trustAnchors() {
return anchors;
}
boolean revocationEnabled() {
return params.isRevocationEnabled();
}
boolean policyMappingInhibited() {
return params.isPolicyMappingInhibited();
}
boolean explicitPolicyRequired() {
return params.isExplicitPolicyRequired();
}
boolean policyQualifiersRejected() {
return params.getPolicyQualifiersRejected();
}
String sigProvider() { return params.getSigProvider(); }
boolean anyPolicyInhibited() { return params.isAnyPolicyInhibited(); }
// in rare cases we need access to the original params, for example
// in order to clone CertPathCheckers before building a new chain
PKIXParameters getPKIXParameters() {
return params;
}
String variant() {
return variant;
}
// The timestamp() param is passed as the date param when creating an
// AlgorithmChecker. An AlgorithmChecker always uses the timestamp
// if specified in order to enforce the denyAfter constraint.
Date timestamp() {
// return timestamp date if set, otherwise use date parameter
if (timestampDate == null) {
timestampDate = (timestamp != null)
? timestamp.getTimestamp() : date();
}
return timestampDate;
}
}
static class BuilderParams extends ValidatorParams {
private PKIXBuilderParameters params;
private List<CertStore> stores;
private X500Principal targetSubject;
BuilderParams(PKIXBuilderParameters params)
throws InvalidAlgorithmParameterException
{
super(params);
checkParams(params);
}
private void checkParams(PKIXBuilderParameters params)
throws InvalidAlgorithmParameterException
{
CertSelector sel = targetCertConstraints();
if (!(sel instanceof X509CertSelector)) {
throw new InvalidAlgorithmParameterException("the "
+ "targetCertConstraints parameter must be an "
+ "X509CertSelector");
}
this.params = params;
this.targetSubject = getTargetSubject(
certStores(), (X509CertSelector)targetCertConstraints());
}
@Override List<CertStore> certStores() {
if (stores == null) {
// reorder CertStores so that local CertStores are tried first
stores = new ArrayList<>(params.getCertStores());
Collections.sort(stores, new CertStoreComparator());
}
return stores;
}
int maxPathLength() { return params.getMaxPathLength(); }
PKIXBuilderParameters params() { return params; }
X500Principal targetSubject() { return targetSubject; }
/**
* Returns the target subject DN from the first X509Certificate that
* is fetched that matches the specified X509CertSelector.
*/
private static X500Principal getTargetSubject(List<CertStore> stores,
X509CertSelector sel)
throws InvalidAlgorithmParameterException
{
X500Principal subject = sel.getSubject();
if (subject != null) {
return subject;
}
X509Certificate cert = sel.getCertificate();
if (cert != null) {
subject = cert.getSubjectX500Principal();
}
if (subject != null) {
return subject;
}
for (CertStore store : stores) {
try {
Collection<? extends Certificate> certs =
(Collection<? extends Certificate>)
store.getCertificates(sel);
if (!certs.isEmpty()) {
X509Certificate xc =
(X509Certificate)certs.iterator().next();
return xc.getSubjectX500Principal();
}
} catch (CertStoreException e) {
// ignore but log it
if (debug != null) {
debug.println("BuilderParams.getTargetSubjectDN: " +
"non-fatal exception retrieving certs: " + e);
e.printStackTrace();
}
}
}
throw new InvalidAlgorithmParameterException
("Could not determine unique target subject");
}
}
/**
* A CertStoreException with additional information about the type of
* CertStore that generated the exception.
*/
static class CertStoreTypeException extends CertStoreException {
private static final long serialVersionUID = 7463352639238322556L;
private final String type;
CertStoreTypeException(String type, CertStoreException cse) {
super(cse.getMessage(), cse.getCause());
this.type = type;
}
String getType() {
return type;
}
}
/**
* Comparator that orders CertStores so that local CertStores come before
* remote CertStores.
*/
private static class CertStoreComparator implements Comparator<CertStore> {
@Override
public int compare(CertStore store1, CertStore store2) {
if (store1.getType().equals("Collection") ||
store1.getCertStoreParameters() instanceof
CollectionCertStoreParameters) {
return -1;
} else {
return 1;
}
}
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2000, 2021, 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.InvalidAlgorithmParameterException;
import java.security.cert.*;
import java.util.*;
import sun.security.provider.certpath.PKIX.ValidatorParams;
import sun.security.x509.X509CertImpl;
import sun.security.util.Debug;
/**
* This class implements the PKIX validation algorithm for certification
* paths consisting exclusively of <code>X509Certificates</code>. It uses
* the specified input parameter set (which must be a
* <code>PKIXParameters</code> object).
*
* @since 1.4
* @author Yassir Elley
*/
public final class PKIXCertPathValidator extends CertPathValidatorSpi {
private static final Debug debug = Debug.getInstance("certpath");
/**
* Default constructor.
*/
public PKIXCertPathValidator() {}
@Override
public CertPathChecker engineGetRevocationChecker() {
return new RevocationChecker();
}
/**
* Validates a certification path consisting exclusively of
* <code>X509Certificate</code>s using the PKIX validation algorithm,
* which uses the specified input parameter set.
* The input parameter set must be a <code>PKIXParameters</code> object.
*
* @param cp the X509 certification path
* @param params the input PKIX parameter set
* @return the result
* @throws CertPathValidatorException if cert path does not validate.
* @throws InvalidAlgorithmParameterException if the specified
* parameters are inappropriate for this CertPathValidator
*/
@Override
public CertPathValidatorResult engineValidate(CertPath cp,
CertPathParameters params)
throws CertPathValidatorException, InvalidAlgorithmParameterException
{
ValidatorParams valParams = PKIX.checkParams(cp, params);
return validate(valParams);
}
private static PKIXCertPathValidatorResult validate(ValidatorParams params)
throws CertPathValidatorException
{
if (debug != null)
debug.println("PKIXCertPathValidator.engineValidate()...");
// Retrieve the first certificate in the certpath
// (to be used later in pre-screening)
AdaptableX509CertSelector selector = null;
List<X509Certificate> certList = params.certificates();
if (!certList.isEmpty()) {
selector = new AdaptableX509CertSelector();
X509Certificate firstCert = certList.get(0);
// check trusted certificate's subject
selector.setSubject(firstCert.getIssuerX500Principal());
/*
* Facilitate certification path construction with authority
* key identifier and subject key identifier.
*/
try {
X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert);
selector.setSkiAndSerialNumber(
firstCertImpl.getAuthorityKeyIdentifierExtension());
} catch (CertificateException | IOException e) {
// ignore
}
}
CertPathValidatorException lastException = null;
// We iterate through the set of trust anchors until we find
// one that works at which time we stop iterating
for (TrustAnchor anchor : params.trustAnchors()) {
X509Certificate trustedCert = anchor.getTrustedCert();
if (trustedCert != null) {
// if this trust anchor is not worth trying,
// we move on to the next one
if (selector != null && !selector.match(trustedCert)) {
if (debug != null) {
debug.println("NO - don't try this trustedCert");
}
continue;
}
if (debug != null) {
debug.println("YES - try this trustedCert");
debug.println("anchor.getTrustedCert()."
+ "getSubjectX500Principal() = "
+ trustedCert.getSubjectX500Principal());
}
} else {
if (debug != null) {
debug.println("PKIXCertPathValidator.engineValidate(): "
+ "anchor.getTrustedCert() == null");
}
}
try {
return validate(anchor, params);
} catch (CertPathValidatorException cpe) {
// remember this exception
lastException = cpe;
}
}
// could not find a trust anchor that verified
// (a) if we did a validation and it failed, use that exception
if (lastException != null) {
throw lastException;
}
// (b) otherwise, generate new exception
throw new CertPathValidatorException
("Path does not chain with any of the trust anchors",
null, null, -1, PKIXReason.NO_TRUST_ANCHOR);
}
private static PKIXCertPathValidatorResult validate(TrustAnchor anchor,
ValidatorParams params)
throws CertPathValidatorException
{
// check if anchor is untrusted
UntrustedChecker untrustedChecker = new UntrustedChecker();
X509Certificate anchorCert = anchor.getTrustedCert();
if (anchorCert != null) {
untrustedChecker.check(anchorCert);
}
int certPathLen = params.certificates().size();
// create PKIXCertPathCheckers
List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
// add standard checkers that we will be using
certPathCheckers.add(untrustedChecker);
certPathCheckers.add(new AlgorithmChecker(anchor, null,
params.timestamp(), params.variant()));
certPathCheckers.add(new KeyChecker(certPathLen,
params.targetCertConstraints()));
certPathCheckers.add(new ConstraintsChecker(certPathLen));
PolicyNodeImpl rootNode =
new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false,
Collections.singleton(PolicyChecker.ANY_POLICY),
false);
PolicyChecker pc = new PolicyChecker(params.initialPolicies(),
certPathLen,
params.explicitPolicyRequired(),
params.policyMappingInhibited(),
params.anyPolicyInhibited(),
params.policyQualifiersRejected(),
rootNode);
certPathCheckers.add(pc);
BasicChecker bc = new BasicChecker(anchor, params.date(),
params.sigProvider(), false);
certPathCheckers.add(bc);
boolean revCheckerAdded = false;
List<PKIXCertPathChecker> checkers = params.certPathCheckers();
for (PKIXCertPathChecker checker : checkers) {
if (checker instanceof PKIXRevocationChecker) {
if (revCheckerAdded) {
throw new CertPathValidatorException(
"Only one PKIXRevocationChecker can be specified");
}
revCheckerAdded = true;
// if it's our own, initialize it
if (checker instanceof RevocationChecker) {
((RevocationChecker)checker).init(anchor, params);
}
}
}
// only add a RevocationChecker if revocation is enabled and
// a PKIXRevocationChecker has not already been added
if (params.revocationEnabled() && !revCheckerAdded) {
certPathCheckers.add(new RevocationChecker(anchor, params));
}
// add user-specified checkers
certPathCheckers.addAll(checkers);
PKIXMasterCertPathValidator.validate(params.certPath(),
params.certificates(),
certPathCheckers);
return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
bc.getPublicKey());
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2016, 2017, 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.security.InvalidAlgorithmParameterException;
import java.security.Timestamp;
import java.security.cert.CertSelector;
import java.security.cert.CertStore;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.TrustAnchor;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* This class is a wrapper for PKIXBuilderParameters so that a Timestamp object
* and a string for the variant type, can be passed when doing certpath
* checking.
*/
public class PKIXExtendedParameters extends PKIXBuilderParameters {
private final PKIXBuilderParameters p;
private Timestamp jarTimestamp;
private final String variant;
public PKIXExtendedParameters(PKIXBuilderParameters params,
Timestamp timestamp, String variant)
throws InvalidAlgorithmParameterException {
super(params.getTrustAnchors(), null);
p = params;
jarTimestamp = timestamp;
this.variant = variant;
}
public Timestamp getTimestamp() {
return jarTimestamp;
}
public void setTimestamp(Timestamp t) {
jarTimestamp = t;
}
public String getVariant() {
return variant;
}
@Override
public void setDate(Date d) {
p.setDate(d);
}
@Override
public void addCertPathChecker(PKIXCertPathChecker c) {
p.addCertPathChecker(c);
}
@Override
public void setMaxPathLength(int maxPathLength) {
p.setMaxPathLength(maxPathLength);
}
@Override
public int getMaxPathLength() {
return p.getMaxPathLength();
}
@Override
public String toString() {
return p.toString();
}
@Override
public Set<TrustAnchor> getTrustAnchors() {
return p.getTrustAnchors();
}
@Override
public void setTrustAnchors(Set<TrustAnchor> trustAnchors)
throws InvalidAlgorithmParameterException {
// To avoid problems with PKIXBuilderParameter's constructors
if (p == null) {
return;
}
p.setTrustAnchors(trustAnchors);
}
@Override
public Set<String> getInitialPolicies() {
return p.getInitialPolicies();
}
@Override
public void setInitialPolicies(Set<String> initialPolicies) {
p.setInitialPolicies(initialPolicies);
}
@Override
public void setCertStores(List<CertStore> stores) {
p.setCertStores(stores);
}
@Override
public void addCertStore(CertStore store) {
p.addCertStore(store);
}
@Override
public List<CertStore> getCertStores() {
return p.getCertStores();
}
@Override
public void setRevocationEnabled(boolean val) {
p.setRevocationEnabled(val);
}
@Override
public boolean isRevocationEnabled() {
return p.isRevocationEnabled();
}
@Override
public void setExplicitPolicyRequired(boolean val) {
p.setExplicitPolicyRequired(val);
}
@Override
public boolean isExplicitPolicyRequired() {
return p.isExplicitPolicyRequired();
}
@Override
public void setPolicyMappingInhibited(boolean val) {
p.setPolicyMappingInhibited(val);
}
@Override
public boolean isPolicyMappingInhibited() {
return p.isPolicyMappingInhibited();
}
@Override
public void setAnyPolicyInhibited(boolean val) {
p.setAnyPolicyInhibited(val);
}
@Override
public boolean isAnyPolicyInhibited() {
return p.isAnyPolicyInhibited();
}
@Override
public void setPolicyQualifiersRejected(boolean qualifiersRejected) {
p.setPolicyQualifiersRejected(qualifiersRejected);
}
@Override
public boolean getPolicyQualifiersRejected() {
return p.getPolicyQualifiersRejected();
}
@Override
public Date getDate() {
return p.getDate();
}
@Override
public void setCertPathCheckers(List<PKIXCertPathChecker> checkers) {
p.setCertPathCheckers(checkers);
}
@Override
public List<PKIXCertPathChecker> getCertPathCheckers() {
return p.getCertPathCheckers();
}
@Override
public String getSigProvider() {
return p.getSigProvider();
}
@Override
public void setSigProvider(String sigProvider) {
p.setSigProvider(sigProvider);
}
@Override
public CertSelector getTargetCertConstraints() {
return p.getTargetCertConstraints();
}
@Override
public void setTargetCertConstraints(CertSelector selector) {
// To avoid problems with PKIXBuilderParameter's constructors
if (p == null) {
return;
}
p.setTargetCertConstraints(selector);
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (c) 2000, 2015, 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 sun.security.util.Debug;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXReason;
import java.security.cert.X509Certificate;
/**
* This class is initialized with a list of <code>PKIXCertPathChecker</code>s
* and is used to verify the certificates in a <code>CertPath</code> by
* feeding each certificate to each <code>PKIXCertPathChecker</code>.
*
* @since 1.4
* @author Yassir Elley
*/
class PKIXMasterCertPathValidator {
private static final Debug debug = Debug.getInstance("certpath");
/**
* Validates a certification path consisting exclusively of
* <code>X509Certificate</code>s using the specified
* <code>PKIXCertPathChecker</code>s. It is assumed that the
* <code>PKIXCertPathChecker</code>s
* have been initialized with any input parameters they may need.
*
* @param cpOriginal the original X509 CertPath passed in by the user
* @param reversedCertList the reversed X509 CertPath (as a List)
* @param certPathCheckers the PKIXCertPathCheckers
* @throws CertPathValidatorException if cert path does not validate
*/
static void validate(CertPath cpOriginal,
List<X509Certificate> reversedCertList,
List<PKIXCertPathChecker> certPathCheckers)
throws CertPathValidatorException
{
// we actually process reversedCertList, but we keep cpOriginal because
// we need to return the original certPath when we throw an exception.
// we will also need to modify the index appropriately when we
// throw an exception.
int cpSize = reversedCertList.size();
if (debug != null) {
debug.println("--------------------------------------------------"
+ "------------");
debug.println("Executing PKIX certification path validation "
+ "algorithm.");
}
for (int i = 0; i < cpSize; i++) {
/* The basic loop algorithm is that we get the
* current certificate, we verify the current certificate using
* information from the previous certificate and from the state,
* and we modify the state for the next loop by setting the
* current certificate of this loop to be the previous certificate
* of the next loop. The state is initialized during first loop.
*/
X509Certificate currCert = reversedCertList.get(i);
if (debug != null) {
debug.println("Checking cert" + (i+1) + " - Subject: " +
currCert.getSubjectX500Principal());
}
Set<String> unresCritExts = currCert.getCriticalExtensionOIDs();
if (unresCritExts == null) {
unresCritExts = Collections.<String>emptySet();
}
if (debug != null && !unresCritExts.isEmpty()) {
StringJoiner joiner = new StringJoiner(", ", "{", "}");
for (String oid : unresCritExts) {
joiner.add(oid);
}
debug.println("Set of critical extensions: " +
joiner.toString());
}
for (int j = 0; j < certPathCheckers.size(); j++) {
PKIXCertPathChecker currChecker = certPathCheckers.get(j);
if (debug != null) {
debug.println("-Using checker" + (j + 1) + " ... [" +
currChecker.getClass().getName() + "]");
}
if (i == 0)
currChecker.init(false);
try {
currChecker.check(currCert, unresCritExts);
if (debug != null) {
debug.println("-checker" + (j + 1) +
" validation succeeded");
}
} catch (CertPathValidatorException cpve) {
throw new CertPathValidatorException(cpve.getMessage(),
(cpve.getCause() != null) ? cpve.getCause() : cpve,
cpOriginal, cpSize - (i + 1), cpve.getReason());
}
}
if (!unresCritExts.isEmpty()) {
throw new CertPathValidatorException("unrecognized " +
"critical extension(s)", null, cpOriginal, cpSize-(i+1),
PKIXReason.UNRECOGNIZED_CRIT_EXT);
}
if (debug != null)
debug.println("\ncert" + (i+1) + " validation succeeded.\n");
}
if (debug != null) {
debug.println("Cert path validation succeeded. (PKIX validation "
+ "algorithm)");
debug.println("-------------------------------------------------"
+ "-------------");
}
}
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (c) 2016, 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.security.InvalidAlgorithmParameterException;
import java.security.Timestamp;
import java.security.cert.CertSelector;
import java.security.cert.CertStore;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.TrustAnchor;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* This class is a wrapper for PKIXBuilderParameters so that a Timestamp object
* can be passed alone when PKIXCertPath is checking signed jar files.
*/
public class PKIXTimestampParameters extends PKIXBuilderParameters {
private final PKIXBuilderParameters p;
private Timestamp jarTimestamp;
public PKIXTimestampParameters(PKIXBuilderParameters params,
Timestamp timestamp) throws InvalidAlgorithmParameterException {
super(params.getTrustAnchors(), null);
p = params;
jarTimestamp = timestamp;
}
public Timestamp getTimestamp() {
return jarTimestamp;
}
public void setTimestamp(Timestamp t) {
jarTimestamp = t;
}
@Override
public void setDate(Date d) {
p.setDate(d);
}
@Override
public void addCertPathChecker(PKIXCertPathChecker c) {
p.addCertPathChecker(c);
}
@Override
public void setMaxPathLength(int maxPathLength) {
p.setMaxPathLength(maxPathLength);
}
@Override
public int getMaxPathLength() {
return p.getMaxPathLength();
}
@Override
public String toString() {
return p.toString();
}
@Override
public Set<TrustAnchor> getTrustAnchors() {
return p.getTrustAnchors();
}
@Override
public void setTrustAnchors(Set<TrustAnchor> trustAnchors)
throws InvalidAlgorithmParameterException {
// To avoid problems with PKIXBuilderParameter's constructors
if (p == null) {
return;
}
p.setTrustAnchors(trustAnchors);
}
@Override
public Set<String> getInitialPolicies() {
return p.getInitialPolicies();
}
@Override
public void setInitialPolicies(Set<String> initialPolicies) {
p.setInitialPolicies(initialPolicies);
}
@Override
public void setCertStores(List<CertStore> stores) {
p.setCertStores(stores);
}
@Override
public void addCertStore(CertStore store) {
p.addCertStore(store);
}
@Override
public List<CertStore> getCertStores() {
return p.getCertStores();
}
@Override
public void setRevocationEnabled(boolean val) {
p.setRevocationEnabled(val);
}
@Override
public boolean isRevocationEnabled() {
return p.isRevocationEnabled();
}
@Override
public void setExplicitPolicyRequired(boolean val) {
p.setExplicitPolicyRequired(val);
}
@Override
public boolean isExplicitPolicyRequired() {
return p.isExplicitPolicyRequired();
}
@Override
public void setPolicyMappingInhibited(boolean val) {
p.setPolicyMappingInhibited(val);
}
@Override
public boolean isPolicyMappingInhibited() {
return p.isPolicyMappingInhibited();
}
@Override
public void setAnyPolicyInhibited(boolean val) {
p.setAnyPolicyInhibited(val);
}
@Override
public boolean isAnyPolicyInhibited() {
return p.isAnyPolicyInhibited();
}
@Override
public void setPolicyQualifiersRejected(boolean qualifiersRejected) {
p.setPolicyQualifiersRejected(qualifiersRejected);
}
@Override
public boolean getPolicyQualifiersRejected() {
return p.getPolicyQualifiersRejected();
}
@Override
public Date getDate() {
return p.getDate();
}
@Override
public void setCertPathCheckers(List<PKIXCertPathChecker> checkers) {
p.setCertPathCheckers(checkers);
}
@Override
public List<PKIXCertPathChecker> getCertPathCheckers() {
return p.getCertPathCheckers();
}
@Override
public String getSigProvider() {
return p.getSigProvider();
}
@Override
public void setSigProvider(String sigProvider) {
p.setSigProvider(sigProvider);
}
@Override
public CertSelector getTargetCertConstraints() {
return p.getTargetCertConstraints();
}
@Override
public void setTargetCertConstraints(CertSelector selector) {
// To avoid problems with PKIXBuilderParameter's constructors
if (p == null) {
return;
}
p.setTargetCertConstraints(selector);
}
}

View File

@@ -0,0 +1,924 @@
/*
* Copyright (c) 2000, 2014, 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.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXReason;
import java.security.cert.PolicyNode;
import java.security.cert.PolicyQualifierInfo;
import java.security.cert.X509Certificate;
import java.util.*;
import sun.security.util.Debug;
import sun.security.x509.CertificatePoliciesExtension;
import sun.security.x509.PolicyConstraintsExtension;
import sun.security.x509.PolicyMappingsExtension;
import sun.security.x509.CertificatePolicyMap;
import static sun.security.x509.PKIXExtensions.*;
import sun.security.x509.PolicyInformation;
import sun.security.x509.X509CertImpl;
import sun.security.x509.InhibitAnyPolicyExtension;
/**
* PolicyChecker is a <code>PKIXCertPathChecker</code> that checks policy
* information on a PKIX certificate, namely certificate policies, policy
* mappings, policy constraints and policy qualifiers.
*
* @since 1.4
* @author Yassir Elley
*/
class PolicyChecker extends PKIXCertPathChecker {
private final Set<String> initPolicies;
private final int certPathLen;
private final boolean expPolicyRequired;
private final boolean polMappingInhibited;
private final boolean anyPolicyInhibited;
private final boolean rejectPolicyQualifiers;
private PolicyNodeImpl rootNode;
private int explicitPolicy;
private int policyMapping;
private int inhibitAnyPolicy;
private int certIndex;
private Set<String> supportedExts;
private static final Debug debug = Debug.getInstance("certpath");
static final String ANY_POLICY = "2.5.29.32.0";
/**
* Constructs a Policy Checker.
*
* @param initialPolicies Set of initial policies
* @param certPathLen length of the certification path to be checked
* @param expPolicyRequired true if explicit policy is required
* @param polMappingInhibited true if policy mapping is inhibited
* @param anyPolicyInhibited true if the ANY_POLICY OID should be inhibited
* @param rejectPolicyQualifiers true if pol qualifiers are to be rejected
* @param rootNode the initial root node of the valid policy tree
*/
PolicyChecker(Set<String> initialPolicies, int certPathLen,
boolean expPolicyRequired, boolean polMappingInhibited,
boolean anyPolicyInhibited, boolean rejectPolicyQualifiers,
PolicyNodeImpl rootNode)
{
if (initialPolicies.isEmpty()) {
// if no initialPolicies are specified by user, set
// initPolicies to be anyPolicy by default
this.initPolicies = new HashSet<String>(1);
this.initPolicies.add(ANY_POLICY);
} else {
this.initPolicies = new HashSet<String>(initialPolicies);
}
this.certPathLen = certPathLen;
this.expPolicyRequired = expPolicyRequired;
this.polMappingInhibited = polMappingInhibited;
this.anyPolicyInhibited = anyPolicyInhibited;
this.rejectPolicyQualifiers = rejectPolicyQualifiers;
this.rootNode = rootNode;
}
/**
* Initializes the internal state of the checker from parameters
* specified in the constructor
*
* @param forward a boolean indicating whether this checker should be
* initialized capable of building in the forward direction
* @throws CertPathValidatorException if user wants to enable forward
* checking and forward checking is not supported.
*/
@Override
public void init(boolean forward) throws CertPathValidatorException {
if (forward) {
throw new CertPathValidatorException
("forward checking not supported");
}
certIndex = 1;
explicitPolicy = (expPolicyRequired ? 0 : certPathLen + 1);
policyMapping = (polMappingInhibited ? 0 : certPathLen + 1);
inhibitAnyPolicy = (anyPolicyInhibited ? 0 : certPathLen + 1);
}
/**
* Checks if forward checking is supported. Forward checking refers
* to the ability of the PKIXCertPathChecker to perform its checks
* when presented with certificates in the forward direction (from
* target to anchor).
*
* @return true if forward checking is supported, false otherwise
*/
@Override
public boolean isForwardCheckingSupported() {
return false;
}
/**
* Gets an immutable Set of the OID strings for the extensions that
* the PKIXCertPathChecker supports (i.e. recognizes, is able to
* process), or null if no extensions are
* supported. All OID strings that a PKIXCertPathChecker might
* possibly be able to process should be included.
*
* @return the Set of extensions supported by this PKIXCertPathChecker,
* or null if no extensions are supported
*/
@Override
public Set<String> getSupportedExtensions() {
if (supportedExts == null) {
supportedExts = new HashSet<String>(4);
supportedExts.add(CertificatePolicies_Id.toString());
supportedExts.add(PolicyMappings_Id.toString());
supportedExts.add(PolicyConstraints_Id.toString());
supportedExts.add(InhibitAnyPolicy_Id.toString());
supportedExts = Collections.unmodifiableSet(supportedExts);
}
return supportedExts;
}
/**
* Performs the policy processing checks on the certificate using its
* internal state.
*
* @param cert the Certificate to be processed
* @param unresCritExts the unresolved critical extensions
* @throws CertPathValidatorException if the certificate does not verify
*/
@Override
public void check(Certificate cert, Collection<String> unresCritExts)
throws CertPathValidatorException
{
// now do the policy checks
checkPolicy((X509Certificate) cert);
if (unresCritExts != null && !unresCritExts.isEmpty()) {
unresCritExts.remove(CertificatePolicies_Id.toString());
unresCritExts.remove(PolicyMappings_Id.toString());
unresCritExts.remove(PolicyConstraints_Id.toString());
unresCritExts.remove(InhibitAnyPolicy_Id.toString());
}
}
/**
* Internal method to run through all the checks.
*
* @param currCert the certificate to be processed
* @exception CertPathValidatorException Exception thrown if
* the certificate does not verify
*/
private void checkPolicy(X509Certificate currCert)
throws CertPathValidatorException
{
String msg = "certificate policies";
if (debug != null) {
debug.println("PolicyChecker.checkPolicy() ---checking " + msg
+ "...");
debug.println("PolicyChecker.checkPolicy() certIndex = "
+ certIndex);
debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+ "explicitPolicy = " + explicitPolicy);
debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+ "policyMapping = " + policyMapping);
debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+ "inhibitAnyPolicy = " + inhibitAnyPolicy);
debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
+ "policyTree = " + rootNode);
}
X509CertImpl currCertImpl = null;
try {
currCertImpl = X509CertImpl.toImpl(currCert);
} catch (CertificateException ce) {
throw new CertPathValidatorException(ce);
}
boolean finalCert = (certIndex == certPathLen);
rootNode = processPolicies(certIndex, initPolicies, explicitPolicy,
policyMapping, inhibitAnyPolicy, rejectPolicyQualifiers, rootNode,
currCertImpl, finalCert);
if (!finalCert) {
explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCertImpl,
finalCert);
policyMapping = mergePolicyMapping(policyMapping, currCertImpl);
inhibitAnyPolicy = mergeInhibitAnyPolicy(inhibitAnyPolicy,
currCertImpl);
}
certIndex++;
if (debug != null) {
debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+ "explicitPolicy = " + explicitPolicy);
debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+ "policyMapping = " + policyMapping);
debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+ "inhibitAnyPolicy = " + inhibitAnyPolicy);
debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
+ "policyTree = " + rootNode);
debug.println("PolicyChecker.checkPolicy() " + msg + " verified");
}
}
/**
* Merges the specified explicitPolicy value with the
* requireExplicitPolicy field of the <code>PolicyConstraints</code>
* extension obtained from the certificate. An explicitPolicy
* value of -1 implies no constraint.
*
* @param explicitPolicy an integer which indicates if a non-null
* valid policy tree is required
* @param currCert the Certificate to be processed
* @param finalCert a boolean indicating whether currCert is
* the final cert in the cert path
* @return returns the new explicitPolicy value
* @exception CertPathValidatorException Exception thrown if an error
* occurs
*/
static int mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert,
boolean finalCert) throws CertPathValidatorException
{
if ((explicitPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
explicitPolicy--;
}
try {
PolicyConstraintsExtension polConstExt
= currCert.getPolicyConstraintsExtension();
if (polConstExt == null)
return explicitPolicy;
int require =
polConstExt.get(PolicyConstraintsExtension.REQUIRE).intValue();
if (debug != null) {
debug.println("PolicyChecker.mergeExplicitPolicy() "
+ "require Index from cert = " + require);
}
if (!finalCert) {
if (require != -1) {
if ((explicitPolicy == -1) || (require < explicitPolicy)) {
explicitPolicy = require;
}
}
} else {
if (require == 0)
explicitPolicy = require;
}
} catch (IOException e) {
if (debug != null) {
debug.println("PolicyChecker.mergeExplicitPolicy "
+ "unexpected exception");
e.printStackTrace();
}
throw new CertPathValidatorException(e);
}
return explicitPolicy;
}
/**
* Merges the specified policyMapping value with the
* inhibitPolicyMapping field of the <code>PolicyConstraints</code>
* extension obtained from the certificate. A policyMapping
* value of -1 implies no constraint.
*
* @param policyMapping an integer which indicates if policy mapping
* is inhibited
* @param currCert the Certificate to be processed
* @return returns the new policyMapping value
* @exception CertPathValidatorException Exception thrown if an error
* occurs
*/
static int mergePolicyMapping(int policyMapping, X509CertImpl currCert)
throws CertPathValidatorException
{
if ((policyMapping > 0) && !X509CertImpl.isSelfIssued(currCert)) {
policyMapping--;
}
try {
PolicyConstraintsExtension polConstExt
= currCert.getPolicyConstraintsExtension();
if (polConstExt == null)
return policyMapping;
int inhibit =
polConstExt.get(PolicyConstraintsExtension.INHIBIT).intValue();
if (debug != null)
debug.println("PolicyChecker.mergePolicyMapping() "
+ "inhibit Index from cert = " + inhibit);
if (inhibit != -1) {
if ((policyMapping == -1) || (inhibit < policyMapping)) {
policyMapping = inhibit;
}
}
} catch (IOException e) {
if (debug != null) {
debug.println("PolicyChecker.mergePolicyMapping "
+ "unexpected exception");
e.printStackTrace();
}
throw new CertPathValidatorException(e);
}
return policyMapping;
}
/**
* Merges the specified inhibitAnyPolicy value with the
* SkipCerts value of the InhibitAnyPolicy
* extension obtained from the certificate.
*
* @param inhibitAnyPolicy an integer which indicates whether
* "any-policy" is considered a match
* @param currCert the Certificate to be processed
* @return returns the new inhibitAnyPolicy value
* @exception CertPathValidatorException Exception thrown if an error
* occurs
*/
static int mergeInhibitAnyPolicy(int inhibitAnyPolicy,
X509CertImpl currCert) throws CertPathValidatorException
{
if ((inhibitAnyPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
inhibitAnyPolicy--;
}
try {
InhibitAnyPolicyExtension inhAnyPolExt = (InhibitAnyPolicyExtension)
currCert.getExtension(InhibitAnyPolicy_Id);
if (inhAnyPolExt == null)
return inhibitAnyPolicy;
int skipCerts =
inhAnyPolExt.get(InhibitAnyPolicyExtension.SKIP_CERTS).intValue();
if (debug != null)
debug.println("PolicyChecker.mergeInhibitAnyPolicy() "
+ "skipCerts Index from cert = " + skipCerts);
if (skipCerts != -1) {
if (skipCerts < inhibitAnyPolicy) {
inhibitAnyPolicy = skipCerts;
}
}
} catch (IOException e) {
if (debug != null) {
debug.println("PolicyChecker.mergeInhibitAnyPolicy "
+ "unexpected exception");
e.printStackTrace();
}
throw new CertPathValidatorException(e);
}
return inhibitAnyPolicy;
}
/**
* Processes certificate policies in the certificate.
*
* @param certIndex the index of the certificate
* @param initPolicies the initial policies required by the user
* @param explicitPolicy an integer which indicates if a non-null
* valid policy tree is required
* @param policyMapping an integer which indicates if policy
* mapping is inhibited
* @param inhibitAnyPolicy an integer which indicates whether
* "any-policy" is considered a match
* @param rejectPolicyQualifiers a boolean indicating whether the
* user wants to reject policies that have qualifiers
* @param origRootNode the root node of the valid policy tree
* @param currCert the Certificate to be processed
* @param finalCert a boolean indicating whether currCert is the final
* cert in the cert path
* @return the root node of the valid policy tree after modification
* @exception CertPathValidatorException Exception thrown if an
* error occurs while processing policies.
*/
static PolicyNodeImpl processPolicies(int certIndex, Set<String> initPolicies,
int explicitPolicy, int policyMapping, int inhibitAnyPolicy,
boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode,
X509CertImpl currCert, boolean finalCert)
throws CertPathValidatorException
{
boolean policiesCritical = false;
List<PolicyInformation> policyInfo;
PolicyNodeImpl rootNode = null;
Set<PolicyQualifierInfo> anyQuals = new HashSet<>();
if (origRootNode == null)
rootNode = null;
else
rootNode = origRootNode.copyTree();
// retrieve policyOIDs from currCert
CertificatePoliciesExtension currCertPolicies
= currCert.getCertificatePoliciesExtension();
// PKIX: Section 6.1.3: Step (d)
if ((currCertPolicies != null) && (rootNode != null)) {
policiesCritical = currCertPolicies.isCritical();
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "policiesCritical = " + policiesCritical);
try {
policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
} catch (IOException ioe) {
throw new CertPathValidatorException("Exception while "
+ "retrieving policyOIDs", ioe);
}
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "rejectPolicyQualifiers = " + rejectPolicyQualifiers);
boolean foundAnyPolicy = false;
// process each policy in cert
for (PolicyInformation curPolInfo : policyInfo) {
String curPolicy =
curPolInfo.getPolicyIdentifier().getIdentifier().toString();
if (curPolicy.equals(ANY_POLICY)) {
foundAnyPolicy = true;
anyQuals = curPolInfo.getPolicyQualifiers();
} else {
// PKIX: Section 6.1.3: Step (d)(1)
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "processing policy: " + curPolicy);
// retrieve policy qualifiers from cert
Set<PolicyQualifierInfo> pQuals =
curPolInfo.getPolicyQualifiers();
// reject cert if we find critical policy qualifiers and
// the policyQualifiersRejected flag is set in the params
if (!pQuals.isEmpty() && rejectPolicyQualifiers &&
policiesCritical) {
throw new CertPathValidatorException(
"critical policy qualifiers present in certificate",
null, null, -1, PKIXReason.INVALID_POLICY);
}
// PKIX: Section 6.1.3: Step (d)(1)(i)
boolean foundMatch = processParents(certIndex,
policiesCritical, rejectPolicyQualifiers, rootNode,
curPolicy, pQuals, false);
if (!foundMatch) {
// PKIX: Section 6.1.3: Step (d)(1)(ii)
processParents(certIndex, policiesCritical,
rejectPolicyQualifiers, rootNode, curPolicy,
pQuals, true);
}
}
}
// PKIX: Section 6.1.3: Step (d)(2)
if (foundAnyPolicy) {
if ((inhibitAnyPolicy > 0) ||
(!finalCert && X509CertImpl.isSelfIssued(currCert))) {
if (debug != null) {
debug.println("PolicyChecker.processPolicies() "
+ "processing policy: " + ANY_POLICY);
}
processParents(certIndex, policiesCritical,
rejectPolicyQualifiers, rootNode, ANY_POLICY, anyQuals,
true);
}
}
// PKIX: Section 6.1.3: Step (d)(3)
rootNode.prune(certIndex);
if (!rootNode.getChildren().hasNext()) {
rootNode = null;
}
} else if (currCertPolicies == null) {
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "no policies present in cert");
// PKIX: Section 6.1.3: Step (e)
rootNode = null;
}
// We delay PKIX: Section 6.1.3: Step (f) to the end
// because the code that follows may delete some nodes
// resulting in a null tree
if (rootNode != null) {
if (!finalCert) {
// PKIX: Section 6.1.4: Steps (a)-(b)
rootNode = processPolicyMappings(currCert, certIndex,
policyMapping, rootNode, policiesCritical, anyQuals);
}
}
// At this point, we optimize the PKIX algorithm by
// removing those nodes which would later have
// been removed by PKIX: Section 6.1.5: Step (g)(iii)
if ((rootNode != null) && (!initPolicies.contains(ANY_POLICY))
&& (currCertPolicies != null)) {
rootNode = removeInvalidNodes(rootNode, certIndex,
initPolicies, currCertPolicies);
// PKIX: Section 6.1.5: Step (g)(iii)
if ((rootNode != null) && finalCert) {
// rewrite anyPolicy leaf nodes (see method comments)
rootNode = rewriteLeafNodes(certIndex, initPolicies, rootNode);
}
}
if (finalCert) {
// PKIX: Section 6.1.5: Steps (a) and (b)
explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCert,
finalCert);
}
// PKIX: Section 6.1.3: Step (f)
// verify that either explicit policy is greater than 0 or
// the valid_policy_tree is not equal to NULL
if ((explicitPolicy == 0) && (rootNode == null)) {
throw new CertPathValidatorException
("non-null policy tree required and policy tree is null",
null, null, -1, PKIXReason.INVALID_POLICY);
}
return rootNode;
}
/**
* Rewrite leaf nodes at the end of validation as described in RFC 5280
* section 6.1.5: Step (g)(iii). Leaf nodes with anyPolicy are replaced
* by nodes explicitly representing initial policies not already
* represented by leaf nodes.
*
* This method should only be called when processing the final cert
* and if the policy tree is not null and initial policies is not
* anyPolicy.
*
* @param certIndex the depth of the tree
* @param initPolicies Set of user specified initial policies
* @param rootNode the root of the policy tree
*/
private static PolicyNodeImpl rewriteLeafNodes(int certIndex,
Set<String> initPolicies, PolicyNodeImpl rootNode) {
Set<PolicyNodeImpl> anyNodes =
rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
if (anyNodes.isEmpty()) {
return rootNode;
}
PolicyNodeImpl anyNode = anyNodes.iterator().next();
PolicyNodeImpl parentNode = (PolicyNodeImpl)anyNode.getParent();
parentNode.deleteChild(anyNode);
// see if there are any initialPolicies not represented by leaf nodes
Set<String> initial = new HashSet<>(initPolicies);
for (PolicyNodeImpl node : rootNode.getPolicyNodes(certIndex)) {
initial.remove(node.getValidPolicy());
}
if (initial.isEmpty()) {
// we deleted the anyPolicy node and have nothing to re-add,
// so we need to prune the tree
rootNode.prune(certIndex);
if (rootNode.getChildren().hasNext() == false) {
rootNode = null;
}
} else {
boolean anyCritical = anyNode.isCritical();
Set<PolicyQualifierInfo> anyQualifiers =
anyNode.getPolicyQualifiers();
for (String policy : initial) {
Set<String> expectedPolicies = Collections.singleton(policy);
PolicyNodeImpl node = new PolicyNodeImpl(parentNode, policy,
anyQualifiers, anyCritical, expectedPolicies, false);
}
}
return rootNode;
}
/**
* Finds the policy nodes of depth (certIndex-1) where curPolicy
* is in the expected policy set and creates a new child node
* appropriately. If matchAny is true, then a value of ANY_POLICY
* in the expected policy set will match any curPolicy. If matchAny
* is false, then the expected policy set must exactly contain the
* curPolicy to be considered a match. This method returns a boolean
* value indicating whether a match was found.
*
* @param certIndex the index of the certificate whose policy is
* being processed
* @param policiesCritical a boolean indicating whether the certificate
* policies extension is critical
* @param rejectPolicyQualifiers a boolean indicating whether the
* user wants to reject policies that have qualifiers
* @param rootNode the root node of the valid policy tree
* @param curPolicy a String representing the policy being processed
* @param pQuals the policy qualifiers of the policy being processed or an
* empty Set if there are no qualifiers
* @param matchAny a boolean indicating whether a value of ANY_POLICY
* in the expected policy set will be considered a match
* @return a boolean indicating whether a match was found
* @exception CertPathValidatorException Exception thrown if error occurs.
*/
private static boolean processParents(int certIndex,
boolean policiesCritical, boolean rejectPolicyQualifiers,
PolicyNodeImpl rootNode, String curPolicy,
Set<PolicyQualifierInfo> pQuals,
boolean matchAny) throws CertPathValidatorException
{
boolean foundMatch = false;
if (debug != null)
debug.println("PolicyChecker.processParents(): matchAny = "
+ matchAny);
// find matching parents
Set<PolicyNodeImpl> parentNodes =
rootNode.getPolicyNodesExpected(certIndex - 1,
curPolicy, matchAny);
// for each matching parent, extend policy tree
for (PolicyNodeImpl curParent : parentNodes) {
if (debug != null)
debug.println("PolicyChecker.processParents() "
+ "found parent:\n" + curParent.asString());
foundMatch = true;
String curParPolicy = curParent.getValidPolicy();
PolicyNodeImpl curNode = null;
Set<String> curExpPols = null;
if (curPolicy.equals(ANY_POLICY)) {
// do step 2
Set<String> parExpPols = curParent.getExpectedPolicies();
parentExplicitPolicies:
for (String curParExpPol : parExpPols) {
Iterator<PolicyNodeImpl> childIter =
curParent.getChildren();
while (childIter.hasNext()) {
PolicyNodeImpl childNode = childIter.next();
String childPolicy = childNode.getValidPolicy();
if (curParExpPol.equals(childPolicy)) {
if (debug != null)
debug.println(childPolicy + " in parent's "
+ "expected policy set already appears in "
+ "child node");
continue parentExplicitPolicies;
}
}
Set<String> expPols = new HashSet<>();
expPols.add(curParExpPol);
curNode = new PolicyNodeImpl
(curParent, curParExpPol, pQuals,
policiesCritical, expPols, false);
}
} else {
curExpPols = new HashSet<String>();
curExpPols.add(curPolicy);
curNode = new PolicyNodeImpl
(curParent, curPolicy, pQuals,
policiesCritical, curExpPols, false);
}
}
return foundMatch;
}
/**
* Processes policy mappings in the certificate.
*
* @param currCert the Certificate to be processed
* @param certIndex the index of the current certificate
* @param policyMapping an integer which indicates if policy
* mapping is inhibited
* @param rootNode the root node of the valid policy tree
* @param policiesCritical a boolean indicating if the certificate policies
* extension is critical
* @param anyQuals the qualifiers associated with ANY-POLICY, or an empty
* Set if there are no qualifiers associated with ANY-POLICY
* @return the root node of the valid policy tree after modification
* @exception CertPathValidatorException exception thrown if an error
* occurs while processing policy mappings
*/
private static PolicyNodeImpl processPolicyMappings(X509CertImpl currCert,
int certIndex, int policyMapping, PolicyNodeImpl rootNode,
boolean policiesCritical, Set<PolicyQualifierInfo> anyQuals)
throws CertPathValidatorException
{
PolicyMappingsExtension polMappingsExt
= currCert.getPolicyMappingsExtension();
if (polMappingsExt == null)
return rootNode;
if (debug != null)
debug.println("PolicyChecker.processPolicyMappings() "
+ "inside policyMapping check");
List<CertificatePolicyMap> maps = null;
try {
maps = polMappingsExt.get(PolicyMappingsExtension.MAP);
} catch (IOException e) {
if (debug != null) {
debug.println("PolicyChecker.processPolicyMappings() "
+ "mapping exception");
e.printStackTrace();
}
throw new CertPathValidatorException("Exception while checking "
+ "mapping", e);
}
boolean childDeleted = false;
for (CertificatePolicyMap polMap : maps) {
String issuerDomain
= polMap.getIssuerIdentifier().getIdentifier().toString();
String subjectDomain
= polMap.getSubjectIdentifier().getIdentifier().toString();
if (debug != null) {
debug.println("PolicyChecker.processPolicyMappings() "
+ "issuerDomain = " + issuerDomain);
debug.println("PolicyChecker.processPolicyMappings() "
+ "subjectDomain = " + subjectDomain);
}
if (issuerDomain.equals(ANY_POLICY)) {
throw new CertPathValidatorException
("encountered an issuerDomainPolicy of ANY_POLICY",
null, null, -1, PKIXReason.INVALID_POLICY);
}
if (subjectDomain.equals(ANY_POLICY)) {
throw new CertPathValidatorException
("encountered a subjectDomainPolicy of ANY_POLICY",
null, null, -1, PKIXReason.INVALID_POLICY);
}
Set<PolicyNodeImpl> validNodes =
rootNode.getPolicyNodesValid(certIndex, issuerDomain);
if (!validNodes.isEmpty()) {
for (PolicyNodeImpl curNode : validNodes) {
if ((policyMapping > 0) || (policyMapping == -1)) {
curNode.addExpectedPolicy(subjectDomain);
} else if (policyMapping == 0) {
PolicyNodeImpl parentNode =
(PolicyNodeImpl) curNode.getParent();
if (debug != null)
debug.println("PolicyChecker.processPolicyMappings"
+ "() before deleting: policy tree = "
+ rootNode);
parentNode.deleteChild(curNode);
childDeleted = true;
if (debug != null)
debug.println("PolicyChecker.processPolicyMappings"
+ "() after deleting: policy tree = "
+ rootNode);
}
}
} else { // no node of depth i has a valid policy
if ((policyMapping > 0) || (policyMapping == -1)) {
Set<PolicyNodeImpl> validAnyNodes =
rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
for (PolicyNodeImpl curAnyNode : validAnyNodes) {
PolicyNodeImpl curAnyNodeParent =
(PolicyNodeImpl) curAnyNode.getParent();
Set<String> expPols = new HashSet<>();
expPols.add(subjectDomain);
PolicyNodeImpl curNode = new PolicyNodeImpl
(curAnyNodeParent, issuerDomain, anyQuals,
policiesCritical, expPols, true);
}
}
}
}
if (childDeleted) {
rootNode.prune(certIndex);
if (!rootNode.getChildren().hasNext()) {
if (debug != null)
debug.println("setting rootNode to null");
rootNode = null;
}
}
return rootNode;
}
/**
* Removes those nodes which do not intersect with the initial policies
* specified by the user.
*
* @param rootNode the root node of the valid policy tree
* @param certIndex the index of the certificate being processed
* @param initPolicies the Set of policies required by the user
* @param currCertPolicies the CertificatePoliciesExtension of the
* certificate being processed
* @returns the root node of the valid policy tree after modification
* @exception CertPathValidatorException Exception thrown if error occurs.
*/
private static PolicyNodeImpl removeInvalidNodes(PolicyNodeImpl rootNode,
int certIndex, Set<String> initPolicies,
CertificatePoliciesExtension currCertPolicies)
throws CertPathValidatorException
{
List<PolicyInformation> policyInfo = null;
try {
policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
} catch (IOException ioe) {
throw new CertPathValidatorException("Exception while "
+ "retrieving policyOIDs", ioe);
}
boolean childDeleted = false;
for (PolicyInformation curPolInfo : policyInfo) {
String curPolicy =
curPolInfo.getPolicyIdentifier().getIdentifier().toString();
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "processing policy second time: " + curPolicy);
Set<PolicyNodeImpl> validNodes =
rootNode.getPolicyNodesValid(certIndex, curPolicy);
for (PolicyNodeImpl curNode : validNodes) {
PolicyNodeImpl parentNode = (PolicyNodeImpl)curNode.getParent();
if (parentNode.getValidPolicy().equals(ANY_POLICY)) {
if ((!initPolicies.contains(curPolicy)) &&
(!curPolicy.equals(ANY_POLICY))) {
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "before deleting: policy tree = " + rootNode);
parentNode.deleteChild(curNode);
childDeleted = true;
if (debug != null)
debug.println("PolicyChecker.processPolicies() "
+ "after deleting: policy tree = " + rootNode);
}
}
}
}
if (childDeleted) {
rootNode.prune(certIndex);
if (!rootNode.getChildren().hasNext()) {
rootNode = null;
}
}
return rootNode;
}
/**
* Gets the root node of the valid policy tree, or null if the
* valid policy tree is null. Marks each node of the returned tree
* immutable and thread-safe.
*
* @returns the root node of the valid policy tree, or null if
* the valid policy tree is null
*/
PolicyNode getPolicyTree() {
if (rootNode == null)
return null;
else {
PolicyNodeImpl policyTree = rootNode.copyTree();
policyTree.setImmutable();
return policyTree;
}
}
}

View File

@@ -0,0 +1,424 @@
/*
* Copyright (c) 2000, 2014, 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.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.security.cert.*;
/**
* Implements the <code>PolicyNode</code> interface.
* <p>
* This class provides an implementation of the <code>PolicyNode</code>
* interface, and is used internally to build and search Policy Trees.
* While the implementation is mutable during construction, it is immutable
* before returning to a client and no mutable public or protected methods
* are exposed by this implementation, as per the contract of PolicyNode.
*
* @since 1.4
* @author Seth Proctor
* @author Sean Mullan
*/
final class PolicyNodeImpl implements PolicyNode {
/**
* Use to specify the special policy "Any Policy"
*/
private static final String ANY_POLICY = "2.5.29.32.0";
// every node has one parent, and zero or more children
private PolicyNodeImpl mParent;
private HashSet<PolicyNodeImpl> mChildren;
// the 4 fields specified by RFC 5280
private String mValidPolicy;
private HashSet<PolicyQualifierInfo> mQualifierSet;
private boolean mCriticalityIndicator;
private HashSet<String> mExpectedPolicySet;
private boolean mOriginalExpectedPolicySet;
// the tree depth
private int mDepth;
// immutability flag
private boolean isImmutable = false;
/**
* Constructor which takes a <code>PolicyNodeImpl</code> representing the
* parent in the Policy Tree to this node. If null, this is the
* root of the tree. The constructor also takes the associated data
* for this node, as found in the certificate. It also takes a boolean
* argument specifying whether this node is being created as a result
* of policy mapping.
*
* @param parent the PolicyNode above this in the tree, or null if this
* node is the tree's root node
* @param validPolicy a String representing this node's valid policy OID
* @param qualifierSet the Set of qualifiers for this policy
* @param criticalityIndicator a boolean representing whether or not the
* extension is critical
* @param expectedPolicySet a Set of expected policies
* @param generatedByPolicyMapping a boolean indicating whether this
* node was generated by a policy mapping
*/
PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy,
Set<PolicyQualifierInfo> qualifierSet,
boolean criticalityIndicator, Set<String> expectedPolicySet,
boolean generatedByPolicyMapping) {
mParent = parent;
mChildren = new HashSet<PolicyNodeImpl>();
if (validPolicy != null)
mValidPolicy = validPolicy;
else
mValidPolicy = "";
if (qualifierSet != null)
mQualifierSet = new HashSet<PolicyQualifierInfo>(qualifierSet);
else
mQualifierSet = new HashSet<PolicyQualifierInfo>();
mCriticalityIndicator = criticalityIndicator;
if (expectedPolicySet != null)
mExpectedPolicySet = new HashSet<String>(expectedPolicySet);
else
mExpectedPolicySet = new HashSet<String>();
mOriginalExpectedPolicySet = !generatedByPolicyMapping;
// see if we're the root, and act appropriately
if (mParent != null) {
mDepth = mParent.getDepth() + 1;
mParent.addChild(this);
} else {
mDepth = 0;
}
}
/**
* Alternate constructor which makes a new node with the policy data
* in an existing <code>PolicyNodeImpl</code>.
*
* @param parent a PolicyNode that's the new parent of the node, or
* null if this is the root node
* @param node a PolicyNode containing the policy data to copy
*/
PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) {
this(parent, node.mValidPolicy, node.mQualifierSet,
node.mCriticalityIndicator, node.mExpectedPolicySet, false);
}
@Override
public PolicyNode getParent() {
return mParent;
}
@Override
public Iterator<PolicyNodeImpl> getChildren() {
return Collections.unmodifiableSet(mChildren).iterator();
}
@Override
public int getDepth() {
return mDepth;
}
@Override
public String getValidPolicy() {
return mValidPolicy;
}
@Override
public Set<PolicyQualifierInfo> getPolicyQualifiers() {
return Collections.unmodifiableSet(mQualifierSet);
}
@Override
public Set<String> getExpectedPolicies() {
return Collections.unmodifiableSet(mExpectedPolicySet);
}
@Override
public boolean isCritical() {
return mCriticalityIndicator;
}
/**
* Return a printable representation of the PolicyNode.
* Starting at the node on which this method is called,
* it recurses through the tree and prints out each node.
*
* @return a String describing the contents of the Policy Node
*/
@Override
public String toString() {
StringBuilder buffer = new StringBuilder(this.asString());
for (PolicyNodeImpl node : mChildren) {
buffer.append(node);
}
return buffer.toString();
}
// private methods and package private operations
boolean isImmutable() {
return isImmutable;
}
/**
* Sets the immutability flag of this node and all of its children
* to true.
*/
void setImmutable() {
if (isImmutable)
return;
for (PolicyNodeImpl node : mChildren) {
node.setImmutable();
}
isImmutable = true;
}
/**
* Private method sets a child node. This is called from the child's
* constructor.
*
* @param child new <code>PolicyNodeImpl</code> child node
*/
private void addChild(PolicyNodeImpl child) {
if (isImmutable) {
throw new IllegalStateException("PolicyNode is immutable");
}
mChildren.add(child);
}
/**
* Adds an expectedPolicy to the expected policy set.
* If this is the original expected policy set initialized
* by the constructor, then the expected policy set is cleared
* before the expected policy is added.
*
* @param expectedPolicy a String representing an expected policy.
*/
void addExpectedPolicy(String expectedPolicy) {
if (isImmutable) {
throw new IllegalStateException("PolicyNode is immutable");
}
if (mOriginalExpectedPolicySet) {
mExpectedPolicySet.clear();
mOriginalExpectedPolicySet = false;
}
mExpectedPolicySet.add(expectedPolicy);
}
/**
* Removes all paths which don't reach the specified depth.
*
* @param depth an int representing the desired minimum depth of all paths
*/
void prune(int depth) {
if (isImmutable)
throw new IllegalStateException("PolicyNode is immutable");
// if we have no children, we can't prune below us...
if (mChildren.size() == 0)
return;
Iterator<PolicyNodeImpl> it = mChildren.iterator();
while (it.hasNext()) {
PolicyNodeImpl node = it.next();
node.prune(depth);
// now that we've called prune on the child, see if we should
// remove it from the tree
if ((node.mChildren.size() == 0) && (depth > mDepth + 1))
it.remove();
}
}
/**
* Deletes the specified child node of this node, if it exists.
*
* @param childNode the child node to be deleted
*/
void deleteChild(PolicyNode childNode) {
if (isImmutable) {
throw new IllegalStateException("PolicyNode is immutable");
}
mChildren.remove(childNode);
}
/**
* Returns a copy of the tree, without copying the policy-related data,
* rooted at the node on which this was called.
*
* @return a copy of the tree
*/
PolicyNodeImpl copyTree() {
return copyTree(null);
}
private PolicyNodeImpl copyTree(PolicyNodeImpl parent) {
PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this);
for (PolicyNodeImpl node : mChildren) {
node.copyTree(newNode);
}
return newNode;
}
/**
* Returns all nodes at the specified depth in the tree.
*
* @param depth an int representing the depth of the desired nodes
* @return a <code>Set</code> of all nodes at the specified depth
*/
Set<PolicyNodeImpl> getPolicyNodes(int depth) {
Set<PolicyNodeImpl> set = new HashSet<>();
getPolicyNodes(depth, set);
return set;
}
/**
* Add all nodes at depth depth to set and return the Set.
* Internal recursion helper.
*/
private void getPolicyNodes(int depth, Set<PolicyNodeImpl> set) {
// if we've reached the desired depth, then return ourself
if (mDepth == depth) {
set.add(this);
} else {
for (PolicyNodeImpl node : mChildren) {
node.getPolicyNodes(depth, set);
}
}
}
/**
* Finds all nodes at the specified depth whose expected_policy_set
* contains the specified expected OID (if matchAny is false)
* or the special OID "any value" (if matchAny is true).
*
* @param depth an int representing the desired depth
* @param expectedOID a String encoding the valid OID to match
* @param matchAny a boolean indicating whether an expected_policy_set
* containing ANY_POLICY should be considered a match
* @return a Set of matched <code>PolicyNode</code>s
*/
Set<PolicyNodeImpl> getPolicyNodesExpected(int depth,
String expectedOID, boolean matchAny) {
if (expectedOID.equals(ANY_POLICY)) {
return getPolicyNodes(depth);
} else {
return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny);
}
}
private Set<PolicyNodeImpl> getPolicyNodesExpectedHelper(int depth,
String expectedOID, boolean matchAny) {
HashSet<PolicyNodeImpl> set = new HashSet<>();
if (mDepth < depth) {
for (PolicyNodeImpl node : mChildren) {
set.addAll(node.getPolicyNodesExpectedHelper(depth,
expectedOID,
matchAny));
}
} else {
if (matchAny) {
if (mExpectedPolicySet.contains(ANY_POLICY))
set.add(this);
} else {
if (mExpectedPolicySet.contains(expectedOID))
set.add(this);
}
}
return set;
}
/**
* Finds all nodes at the specified depth that contains the
* specified valid OID
*
* @param depth an int representing the desired depth
* @param validOID a String encoding the valid OID to match
* @return a Set of matched <code>PolicyNode</code>s
*/
Set<PolicyNodeImpl> getPolicyNodesValid(int depth, String validOID) {
HashSet<PolicyNodeImpl> set = new HashSet<>();
if (mDepth < depth) {
for (PolicyNodeImpl node : mChildren) {
set.addAll(node.getPolicyNodesValid(depth, validOID));
}
} else {
if (mValidPolicy.equals(validOID))
set.add(this);
}
return set;
}
private static String policyToString(String oid) {
if (oid.equals(ANY_POLICY)) {
return "anyPolicy";
} else {
return oid;
}
}
/**
* Prints out some data on this node.
*/
String asString() {
if (mParent == null) {
return "anyPolicy ROOT\n";
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0, n = getDepth(); i < n; i++) {
sb.append(" ");
}
sb.append(policyToString(getValidPolicy()));
sb.append(" CRIT: ");
sb.append(isCritical());
sb.append(" EP: ");
for (String policy : getExpectedPolicies()) {
sb.append(policyToString(policy));
sb.append(" ");
}
sb.append(" (");
sb.append(getDepth());
sb.append(")\n");
return sb.toString();
}
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (c) 2015, 2017, 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.util.Arrays;
import java.io.IOException;
import java.security.PublicKey;
import javax.security.auth.x500.X500Principal;
import sun.security.x509.KeyIdentifier;
import sun.security.util.DerValue;
/**
* Class for ResponderId entities as described in RFC6960. ResponderId objects
* are used to uniquely identify OCSP responders.
* <p>
* The RFC 6960 defines a ResponderID structure as:
* <pre>
* ResponderID ::= CHOICE {
* byName [1] Name,
* byKey [2] KeyHash }
*
* KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
* (excluding the tag and length fields)
*
* Name is defined in RFC 5280.
* </pre>
*
* @see ResponderId.Type
* @since 9
*/
public final class ResponderId {
/**
* A {@code ResponderId} enumeration describing the accepted forms for a
* {@code ResponderId}.
*
* @see ResponderId
* @since 9
*/
public static enum Type {
/**
* A BY_NAME {@code ResponderId} will be built from a subject name,
* either as an {@code X500Principal} or a DER-encoded byte array.
*/
BY_NAME(1, "byName"),
/**
* A BY_KEY {@code ResponderId} will be built from a public key
* identifier, either derived from a {@code PublicKey} or directly
* from a DER-encoded byte array containing the key identifier.
*/
BY_KEY(2, "byKey");
private final int tagNumber;
private final String ridTypeName;
private Type(int value, String name) {
this.tagNumber = value;
this.ridTypeName = name;
}
public int value() {
return tagNumber;
}
@Override
public String toString() {
return ridTypeName;
}
}
private Type type;
private X500Principal responderName;
private KeyIdentifier responderKeyId;
private byte[] encodedRid;
/**
* Constructs a {@code ResponderId} object using an {@code X500Principal}.
* When encoded in DER this object will use the BY_NAME option.
*
* @param subjectName the subject name of the certificate used
* to sign OCSP responses.
*
* @throws IOException if the internal DER-encoding of the
* {@code X500Principal} fails.
*/
public ResponderId(X500Principal subjectName) throws IOException {
responderName = subjectName;
responderKeyId = null;
encodedRid = principalToBytes();
type = Type.BY_NAME;
}
/**
* Constructs a {@code ResponderId} object using a {@code PublicKey}.
* When encoded in DER this object will use the byKey option, a
* SHA-1 hash of the responder's public key.
*
* @param pubKey the the OCSP responder's public key
*
* @throws IOException if the internal DER-encoding of the
* {@code KeyIdentifier} fails.
*/
public ResponderId(PublicKey pubKey) throws IOException {
responderKeyId = new KeyIdentifier(pubKey);
responderName = null;
encodedRid = keyIdToBytes();
type = Type.BY_KEY;
}
/**
* Constructs a {@code ResponderId} object from its DER-encoding.
*
* @param encodedData the DER-encoded bytes
*
* @throws IOException if the encodedData is not properly DER encoded
*/
public ResponderId(byte[] encodedData) throws IOException {
DerValue outer = new DerValue(encodedData);
if (outer.isContextSpecific((byte)Type.BY_NAME.value())
&& outer.isConstructed()) {
// Use the X500Principal constructor as a way to sanity
// check the incoming data.
responderName = new X500Principal(outer.getDataBytes());
encodedRid = principalToBytes();
type = Type.BY_NAME;
} else if (outer.isContextSpecific((byte)Type.BY_KEY.value())
&& outer.isConstructed()) {
// Use the KeyIdentifier constructor as a way to sanity
// check the incoming data.
responderKeyId =
new KeyIdentifier(new DerValue(outer.getDataBytes()));
encodedRid = keyIdToBytes();
type = Type.BY_KEY;
} else {
throw new IOException("Invalid ResponderId content");
}
}
/**
* Encode a {@code ResponderId} in DER form
*
* @return a byte array containing the DER-encoded representation for this
* {@code ResponderId}
*/
public byte[] getEncoded() {
return encodedRid.clone();
}
/**
* Return the type of {@ResponderId}
*
* @return a number corresponding to the context-specific tag number
* used in the DER-encoding for a {@code ResponderId}
*/
public ResponderId.Type getType() {
return type;
}
/**
* Get the length of the encoded {@code ResponderId} (including the tag and
* length of the explicit tagging from the outer ASN.1 CHOICE).
*
* @return the length of the encoded {@code ResponderId}
*/
public int length() {
return encodedRid.length;
}
/**
* Obtain the underlying {@code X500Principal} from a {@code ResponderId}
*
* @return the {@code X500Principal} for this {@code ResponderId} if it
* is a BY_NAME variant. If the {@code ResponderId} is a BY_KEY
* variant, this routine will return {@code null}.
*/
public X500Principal getResponderName() {
return responderName;
}
/**
* Obtain the underlying key identifier from a {@code ResponderId}
*
* @return the {@code KeyIdentifier} for this {@code ResponderId} if it
* is a BY_KEY variant. If the {@code ResponderId} is a BY_NAME
* variant, this routine will return {@code null}.
*/
public KeyIdentifier getKeyIdentifier() {
return responderKeyId;
}
/**
* Compares the specified object with this {@code ResponderId} for equality.
* A ResponderId will only be considered equivalent if both the type and
* data value are equal. Two ResponderIds initialized by name and
* key ID, respectively, will not be equal even if the
* ResponderId objects are created from the same source certificate.
*
* @param obj the object to be compared against
*
* @return true if the specified object is equal to this {@code Responderid}
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof ResponderId) {
ResponderId respObj = (ResponderId)obj;
return Arrays.equals(encodedRid, respObj.getEncoded());
}
return false;
}
/**
* Returns the hash code value for this {@code ResponderId}
*
* @return the hash code value for this {@code ResponderId}
*/
@Override
public int hashCode() {
return Arrays.hashCode(encodedRid);
}
/**
* Create a String representation of this {@code ResponderId}
*
* @return a String representation of this {@code ResponderId}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
switch (type) {
case BY_NAME:
sb.append(type).append(": ").append(responderName);
break;
case BY_KEY:
sb.append(type).append(": ");
for (byte keyIdByte : responderKeyId.getIdentifier()) {
sb.append(String.format("%02X", keyIdByte));
}
break;
default:
sb.append("Unknown ResponderId Type: ").append(type);
}
return sb.toString();
}
/**
* Convert the responderName data member into its DER-encoded form
*
* @return the DER encoding for a responder ID byName option, including
* explicit context-specific tagging.
*
* @throws IOException if any encoding error occurs
*/
private byte[] principalToBytes() throws IOException {
DerValue dv = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)Type.BY_NAME.value()),
responderName.getEncoded());
return dv.toByteArray();
}
/**
* Convert the responderKeyId data member into its DER-encoded form
*
* @return the DER encoding for a responder ID byKey option, including
* explicit context-specific tagging.
*
* @throws IOException if any encoding error occurs
*/
private byte[] keyIdToBytes() throws IOException {
// Place the KeyIdentifier bytes into an OCTET STRING
DerValue inner = new DerValue(DerValue.tag_OctetString,
responderKeyId.getIdentifier());
// Mark the OCTET STRING-wrapped KeyIdentifier bytes
// as EXPLICIT CONTEXT 2
DerValue outer = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte)Type.BY_KEY.value()), inner.toByteArray());
return outer.toByteArray();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2000, 2023, 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.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.cert.CertPathValidatorException;
/**
* A specification of a PKIX validation state
* which is initialized by each build and updated each time a
* certificate is added to the current path.
*
* @since 1.4
* @author Sean Mullan
* @author Yassir Elley
*/
interface State extends Cloneable {
/**
* Update the state with the next certificate added to the path.
*
* @param cert the certificate which is used to update the state
*/
public void updateState(X509Certificate cert)
throws CertificateException, IOException, CertPathValidatorException;
/**
* Creates and returns a copy of this object
*/
public Object clone();
/**
* Returns a boolean flag indicating if the state is initial
* (just starting)
*
* @return boolean flag indicating if the state is initial (just starting)
*/
public boolean isInitial();
}

View File

@@ -0,0 +1,654 @@
/*
* Copyright (c) 2000, 2023, 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.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.Arrays;
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.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X509CertImpl;
import sun.security.util.Debug;
/**
* This class builds certification paths in the forward direction.
*
* <p> If successful, it returns a certification path which has successfully
* satisfied all the constraints and requirements specified in the
* PKIXBuilderParameters object and has been validated according to the PKIX
* path validation algorithm defined in RFC 5280.
*
* <p> This implementation uses a depth-first search approach to finding
* certification paths. If it comes to a point in which it cannot find
* any more certificates leading to the target OR the path length is too long
* it backtracks to previous paths until the target has been found or
* all possible paths have been exhausted.
*
* <p> This implementation is not thread-safe.
*
* @since 1.4
* @author Sean Mullan
* @author Yassir Elley
*/
public final class SunCertPathBuilder extends CertPathBuilderSpi {
private static final Debug debug = Debug.getInstance("certpath");
/*
* private objects shared by methods
*/
private BuilderParams buildParams;
private CertificateFactory cf;
private boolean pathCompleted = false;
private PolicyNode policyTreeResult;
private TrustAnchor trustAnchor;
private PublicKey finalPublicKey;
/**
* Create an instance of <code>SunCertPathBuilder</code>.
*
* @throws CertPathBuilderException if an error occurs
*/
public SunCertPathBuilder() throws CertPathBuilderException {
try {
cf = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new CertPathBuilderException(e);
}
}
@Override
public CertPathChecker engineGetRevocationChecker() {
return new RevocationChecker();
}
/**
* Attempts to build a certification path using the Sun build
* algorithm from a trusted anchor(s) to a target subject, which must both
* be specified in the input parameter set. This method will
* attempt to build in the forward direction: from the target to the CA.
*
* <p>The certification path that is constructed is validated
* according to the PKIX specification.
*
* @param params the parameter set for building a path. Must be an instance
* of <code>PKIXBuilderParameters</code>.
* @return a certification path builder result.
* @exception CertPathBuilderException Exception thrown if builder is
* unable to build a complete certification path from the trusted anchor(s)
* to the target subject.
* @throws InvalidAlgorithmParameterException if the given parameters are
* inappropriate for this certification path builder.
*/
@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 (buildParams.certStores().size() > 1 || Builder.USE_AIA) {
if (debug != null) {
debug.println("SunCertPathBuilder.engineBuild: 2nd pass; " +
"try building again searching all certstores");
}
// try again
adjList.clear();
result = buildCertPath(true, adjList);
if (result != null) {
return result;
}
}
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
{
// Init shared variables and build certification path
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));
}
// construct SunCertPathBuilderResult
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
// the certPathList
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 build forward method.
*/
private void buildForward(List<List<Vertex>> adjacencyList,
LinkedList<X509Certificate> certPathList,
boolean searchAllCertStores)
throws GeneralSecurityException, IOException
{
if (debug != null) {
debug.println("SunCertPathBuilder.buildForward()...");
}
/* Initialize current state */
ForwardState currentState = new ForwardState();
currentState.initState(buildParams.certPathCheckers());
/* Initialize adjacency list */
adjacencyList.clear();
adjacencyList.add(new LinkedList<Vertex>());
currentState.untrustedChecker = new UntrustedChecker();
depthFirstSearchForward(buildParams.targetSubject(), currentState,
new ForwardBuilder(buildParams,
searchAllCertStores),
adjacencyList, certPathList);
}
/*
* This method performs a depth first search for a certification
* path while building forward which meets the requirements set in
* the parameters object.
* It uses an adjacency list to store all certificates which were
* tried (i.e. at one time added to the path - they may not end up in
* the final path if backtracking occurs). This information can
* be used later to debug or demo the build.
*
* See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
* for an explanation of the DFS algorithm.
*
* @param dN the distinguished name being currently searched for certs
* @param currentState the current PKIX validation state
*/
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() + ")");
}
/*
* Find all the certificates issued to dN which
* satisfy the PKIX certification path constraints.
*/
Collection<X509Certificate> certs =
builder.getMatchingCerts(currentState, buildParams.certStores());
List<Vertex> vertices = addVertices(certs, adjList, cpList);
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 certs with repeated public key and subject.
* Call depthFirstSearchForward() recursively for each good cert.
*/
vertices:
for (Vertex vertex : vertices) {
/**
* Restore state to currentState each time through the loop.
* This is important because some of the user-defined
* checkers modify the state, which MUST be restored if
* the cert eventually fails to lead to the target and
* the next matching cert is tried.
*/
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;
}
/*
* Certificate is good.
* If cert completes the path,
* process userCheckers that don't support forward checking
* and process policies over whole path
* and backtrack appropriately if there is a failure
* else if cert does not complete the path,
* add it to the path
*/
if (builder.isPathCompleted(cert)) {
if (debug != null)
debug.println("SunCertPathBuilder.depthFirstSearchForward()"
+ ": commencing final verification");
List<X509Certificate> appendedCerts = new ArrayList<>(cpList);
/*
* if the trust anchor selected is specified as a trusted
* public key rather than a trusted cert, then verify this
* cert (which is signed by the trusted public key), but
* don't add it yet to the cpList
*/
PublicKey rootKey = cert.getPublicKey();
if (builder.trustAnchor.getTrustedCert() == null) {
appendedCerts.add(0, cert);
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);
// add the basic checker
List<PKIXCertPathChecker> checkers = new ArrayList<>();
BasicChecker basicChecker = new BasicChecker(anchor,
buildParams.date(),
buildParams.sigProvider(),
true);
checkers.add(basicChecker);
Set<String> initExpPolSet =
Collections.singleton(PolicyChecker.ANY_POLICY);
PolicyNodeImpl rootNode = new PolicyNodeImpl(null,
PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);
PolicyChecker policyChecker
= new PolicyChecker(buildParams.initialPolicies(),
appendedCerts.size(),
buildParams.explicitPolicyRequired(),
buildParams.policyMappingInhibited(),
buildParams.anyPolicyInhibited(),
buildParams.policyQualifiersRejected(),
rootNode);
checkers.add(policyChecker);
// add the constraints checker
checkers.add(new ConstraintsChecker(appendedCerts.size()));
// add the algorithm checker
checkers.add(new AlgorithmChecker(builder.trustAnchor,
buildParams.timestamp(), buildParams.variant()));
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 it's our own, initialize it
if (ckr instanceof RevocationChecker) {
((RevocationChecker)ckr).init(builder.trustAnchor,
buildParams);
}
}
}
// only add a RevocationChecker if revocation is enabled and
// a PKIXRevocationChecker has not already been added
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
// able to set the trust anchor until now.
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
// cannot trust it. We can bail out here.
if (buildParams.targetCertConstraints().match(currCert)
&& cpve.getReason() == BasicReason.REVOKED) {
throw cpve;
}
vertex.setThrowable(cpve);
continue vertices;
}
}
}
/*
* Remove extensions from user checkers that support
* forward checking. After this step, we will have
* removed all extensions that all user checkers
* are capable of processing.
*/
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 the user specified a trusted public key rather than
* trusted certs, then add this cert (which is signed by
* the trusted public key) to the cpList
*/
if (builder.trustAnchor.getTrustedCert() == null)
builder.addCertToPath(cert, cpList);
// Save the trust anchor
this.trustAnchor = builder.trustAnchor;
/*
* Extract and save the final target public key
*/
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 {
// If successive certs are self-issued, don't continue search
// on this branch.
if (currentState.selfIssued && X509CertImpl.isSelfIssued(cert)) {
if (debug != null) {
debug.println("Successive certs are self-issued");
}
return;
}
builder.addCertToPath(cert, cpList);
}
/* Update the PKIX state */
nextState.updateState(cert);
/*
* Append an entry for cert in adjacency list and
* set index for current vertex.
*/
adjList.add(new LinkedList<Vertex>());
vertex.setIndex(adjList.size() - 1);
/* recursively search for matching certs at next dN */
depthFirstSearchForward(cert.getIssuerX500Principal(), nextState,
builder, adjList, cpList);
/*
* If path has been completed, return ASAP!
*/
if (pathCompleted) {
return;
} else {
/*
* If we get here, it means we have searched all possible
* certs issued by the dN w/o finding any matching certs.
* This means we have to backtrack to the previous cert in
* the path and try some other paths.
*/
if (debug != null)
debug.println("SunCertPathBuilder.depthFirstSearchForward()"
+ ": backtracking");
builder.removeFinalCertFromPath(cpList);
}
}
}
/*
* Adds a collection of matching certificates to the
* adjacency list.
*/
private static List<Vertex> addVertices(Collection<X509Certificate> certs,
List<List<Vertex>> adjList,
List<X509Certificate> cpList)
{
List<Vertex> l = adjList.get(adjList.size() - 1);
for (X509Certificate cert : certs) {
boolean repeated = false;
for (X509Certificate cpListCert : cpList) {
/*
* Ignore if we encounter the same certificate or a
* certificate with the same public key, subject DN, and
* subjectAltNames as a cert that is already in path.
*/
if (repeated(cpListCert, cert)) {
if (debug != null) {
debug.println("cert with repeated subject, " +
"public key, and subjectAltNames detected");
}
repeated = true;
break;
}
}
if (!repeated) {
l.add(new Vertex(cert));
}
}
return l;
}
/**
* Return true if two certificates are equal or have the same subject,
* public key, and subject alternative names.
*/
private static boolean repeated(
X509Certificate currCert, X509Certificate nextCert) {
if (currCert.equals(nextCert)) {
return true;
}
return (currCert.getSubjectX500Principal().equals(
nextCert.getSubjectX500Principal()) &&
currCert.getPublicKey().equals(nextCert.getPublicKey()) &&
altNamesEqual(currCert, nextCert));
}
/**
* Return true if two certificates have the same subject alternative names.
*/
private static boolean altNamesEqual(
X509Certificate currCert, X509Certificate nextCert) {
X509CertImpl curr, next;
try {
curr = X509CertImpl.toImpl(currCert);
next = X509CertImpl.toImpl(nextCert);
} catch (CertificateException ce) {
return false;
}
SubjectAlternativeNameExtension currAltNameExt =
curr.getSubjectAlternativeNameExtension();
SubjectAlternativeNameExtension nextAltNameExt =
next.getSubjectAlternativeNameExtension();
if (currAltNameExt != null) {
if (nextAltNameExt == null) {
return false;
}
return Arrays.equals(currAltNameExt.getExtensionValue(),
nextAltNameExt.getExtensionValue());
} else {
return (nextAltNameExt == null);
}
}
/**
* Returns true if trust anchor certificate matches specified
* certificate constraints.
*/
private static boolean anchorIsTarget(TrustAnchor anchor,
CertSelector sel)
{
X509Certificate anchorCert = anchor.getTrustedCert();
if (anchorCert != null) {
return sel.match(anchorCert);
}
return false;
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2000, 2004, 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.util.List;
import java.security.cert.CertPathBuilderException;
/**
* This is a subclass of the generic <code>CertPathBuilderException</code>.
* It contains an adjacency list with information regarding the unsuccessful
* paths that the SunCertPathBuilder tried.
*
* @since 1.4
* @author Sean Mullan
* @see CertPathBuilderException
*/
public class SunCertPathBuilderException extends CertPathBuilderException {
private static final long serialVersionUID = -7814288414129264709L;
/**
* @serial
*/
private transient AdjacencyList adjList;
/**
* Constructs a <code>SunCertPathBuilderException</code> with
* <code>null</code> as its detail message.
*/
public SunCertPathBuilderException() {
super();
}
/**
* Constructs a <code>SunCertPathBuilderException</code> with the specified
* detail message. A detail message is a <code>String</code> that
* describes this particular exception.
*
* @param msg the detail message
*/
public SunCertPathBuilderException(String msg) {
super(msg);
}
/**
* Constructs a <code>SunCertPathBuilderException</code> that wraps the
* specified throwable. This allows any exception to be converted into a
* <code>SunCertPathBuilderException</code>, while retaining information
* about the cause, which may be useful for debugging. The detail message is
* set to (<code>cause==null ? null : cause.toString()</code>) (which
* typically contains the class and detail message of cause).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause getCause()} method). (A <code>null</code> value is
* permitted, and indicates that the cause is nonexistent or unknown.)
* root cause.
*/
public SunCertPathBuilderException(Throwable cause) {
super(cause);
}
/**
* Creates a <code>SunCertPathBuilderException</code> with the specified
* detail message and cause.
*
* @param msg the detail message
* @param cause the cause
*/
public SunCertPathBuilderException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Creates a <code>SunCertPathBuilderException</code> withe the specified
* detail message and adjacency list.
*
* @param msg the detail message
* @param adjList the adjacency list
*/
SunCertPathBuilderException(String msg, AdjacencyList adjList) {
this(msg);
this.adjList = adjList;
}
/**
* Creates a <code>SunCertPathBuilderException</code> with the specified
* detail message, cause, and adjacency list.
*
* @param msg the detail message
* @param cause the throwable that occurred
* @param adjList Adjacency list
*/
SunCertPathBuilderException(String msg, Throwable cause,
AdjacencyList adjList)
{
this(msg, cause);
this.adjList = adjList;
}
/**
* Returns the adjacency list containing information about the build.
*
* @return the adjacency list containing information about the build
*/
public AdjacencyList getAdjacencyList() {
return adjList;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2000, 2001, 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 sun.security.util.Debug;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.PolicyNode;
import java.security.cert.TrustAnchor;
/**
* This class represents the result of a SunCertPathBuilder build.
* Since all paths returned by the SunCertPathProvider are PKIX validated
* the result contains the valid policy tree and subject public key returned
* by the algorithm. It also contains the trust anchor and debug information
* represented in the form of an adjacency list.
*
* @see PKIXCertPathBuilderResult
*
* @since 1.4
* @author Sean Mullan
*/
//@@@ Note: this class is not in public API and access to adjacency list is
//@@@ intended for debugging/replay of Sun PKIX CertPathBuilder implementation.
public class SunCertPathBuilderResult extends PKIXCertPathBuilderResult {
private static final Debug debug = Debug.getInstance("certpath");
private AdjacencyList adjList;
/**
* Creates a SunCertPathBuilderResult instance.
*
* @param certPath the validated <code>CertPath</code>
* @param trustAnchor a <code>TrustAnchor</code> describing the CA that
* served as a trust anchor for the certification path
* @param policyTree the valid policy tree, or <code>null</code>
* if there are no valid policies
* @param subjectPublicKey the public key of the subject
* @param adjList an Adjacency list containing debug information
*/
SunCertPathBuilderResult(CertPath certPath,
TrustAnchor trustAnchor, PolicyNode policyTree,
PublicKey subjectPublicKey, AdjacencyList adjList)
{
super(certPath, trustAnchor, policyTree, subjectPublicKey);
this.adjList = adjList;
}
/**
* Returns the adjacency list containing information about the build.
*
* @return The adjacency list containing information about the build.
*/
public AdjacencyList getAdjacencyList() {
return adjList;
}
}

View File

@@ -0,0 +1,491 @@
/*
* Copyright (c) 2006, 2013, 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.InputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URLConnection;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertSelector;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertStoreParameters;
import java.security.cert.CertStoreSpi;
import java.security.cert.CRLException;
import java.security.cert.CRLSelector;
import java.security.cert.X509Certificate;
import java.security.cert.X509CertSelector;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLSelector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import sun.security.action.GetIntegerAction;
import sun.security.x509.AccessDescription;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.URIName;
import sun.security.util.Cache;
import sun.security.util.Debug;
/**
* A <code>CertStore</code> that retrieves <code>Certificates</code> or
* <code>CRL</code>s from a URI, for example, as specified in an X.509
* AuthorityInformationAccess or CRLDistributionPoint extension.
* <p>
* For CRLs, this implementation retrieves a single DER encoded CRL per URI.
* For Certificates, this implementation retrieves a single DER encoded CRL or
* a collection of Certificates encoded as a PKCS#7 "certs-only" CMS message.
* <p>
* This <code>CertStore</code> also implements Certificate/CRL caching.
* Currently, the cache is shared between all applications in the VM and uses a
* hardcoded policy. The cache has a maximum size of 185 entries, which are held
* by SoftReferences. A request will be satisfied from the cache if we last
* checked for an update within CHECK_INTERVAL (last 30 seconds). Otherwise,
* we open an URLConnection to download the Certificate(s)/CRL using an
* If-Modified-Since request (HTTP) if possible. Note that both positive and
* negative responses are cached, i.e. if we are unable to open the connection
* or the Certificate(s)/CRL cannot be parsed, we remember this result and
* additional calls during the CHECK_INTERVAL period do not try to open another
* connection.
* <p>
* The URICertStore is not currently a standard CertStore type. We should
* consider adding a standard "URI" CertStore type.
*
* @author Andreas Sterbenz
* @author Sean Mullan
* @since 7.0
*/
class URICertStore extends CertStoreSpi {
private static final Debug debug = Debug.getInstance("certpath");
// interval between checks for update of cached Certificates/CRLs
// (30 seconds)
private final static int CHECK_INTERVAL = 30 * 1000;
// size of the cache (see Cache class for sizing recommendations)
private final static int CACHE_SIZE = 185;
// X.509 certificate factory instance
private final CertificateFactory factory;
// cached Collection of X509Certificates (may be empty, never null)
private Collection<X509Certificate> certs = Collections.emptySet();
// cached X509CRL (may be null)
private X509CRL crl;
// time we last checked for an update
private long lastChecked;
// time server returned as last modified time stamp
// or 0 if not available
private long lastModified;
// the URI of this CertStore
private URI uri;
// true if URI is ldap
private boolean ldap = false;
private CertStoreHelper ldapHelper;
private CertStore ldapCertStore;
private String ldapPath;
// Default maximum connect timeout in milliseconds (15 seconds)
// allowed when downloading CRLs
private static final int DEFAULT_CRL_CONNECT_TIMEOUT = 15000;
/**
* Integer value indicating the connect timeout, in seconds, to be
* used for the CRL download. A timeout of zero is interpreted as
* an infinite timeout.
*/
private static final int CRL_CONNECT_TIMEOUT = initializeTimeout();
/**
* Initialize the timeout length by getting the CRL timeout
* system property. If the property has not been set, or if its
* value is negative, set the timeout length to the default.
*/
private static int initializeTimeout() {
Integer tmp = java.security.AccessController.doPrivileged(
new GetIntegerAction("com.sun.security.crl.timeout"));
if (tmp == null || tmp < 0) {
return DEFAULT_CRL_CONNECT_TIMEOUT;
}
// Convert to milliseconds, as the system property will be
// specified in seconds
return tmp * 1000;
}
/**
* Creates a URICertStore.
*
* @param parameters specifying the URI
*/
URICertStore(CertStoreParameters params)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
super(params);
if (!(params instanceof URICertStoreParameters)) {
throw new InvalidAlgorithmParameterException
("params must be instanceof URICertStoreParameters");
}
this.uri = ((URICertStoreParameters) params).uri;
// if ldap URI, use an LDAPCertStore to fetch certs and CRLs
if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals("ldap")) {
ldap = true;
ldapHelper = CertStoreHelper.getInstance("LDAP");
ldapCertStore = ldapHelper.getCertStore(uri);
ldapPath = uri.getPath();
// strip off leading '/'
if (ldapPath.charAt(0) == '/') {
ldapPath = ldapPath.substring(1);
}
}
try {
factory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new RuntimeException();
}
}
/**
* Returns a URI CertStore. This method consults a cache of
* CertStores (shared per JVM) using the URI as a key.
*/
private static final Cache<URICertStoreParameters, CertStore>
certStoreCache = Cache.newSoftMemoryCache(CACHE_SIZE);
static synchronized CertStore getInstance(URICertStoreParameters params)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (debug != null) {
debug.println("CertStore URI:" + params.uri);
}
CertStore ucs = certStoreCache.get(params);
if (ucs == null) {
ucs = new UCS(new URICertStore(params), null, "URI", params);
certStoreCache.put(params, ucs);
} else {
if (debug != null) {
debug.println("URICertStore.getInstance: cache hit");
}
}
return ucs;
}
/**
* Creates a CertStore from information included in the AccessDescription
* object of a certificate's Authority Information Access Extension.
*/
static CertStore getInstance(AccessDescription ad) {
if (!ad.getAccessMethod().equals((Object)
AccessDescription.Ad_CAISSUERS_Id)) {
return null;
}
GeneralNameInterface gn = ad.getAccessLocation().getName();
if (!(gn instanceof URIName)) {
return null;
}
URI uri = ((URIName) gn).getURI();
try {
return URICertStore.getInstance
(new URICertStore.URICertStoreParameters(uri));
} catch (Exception ex) {
if (debug != null) {
debug.println("exception creating CertStore: " + ex);
ex.printStackTrace();
}
return null;
}
}
/**
* Returns a <code>Collection</code> of <code>X509Certificate</code>s that
* match the specified selector. If no <code>X509Certificate</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector a <code>CertSelector</code> used to select which
* <code>X509Certificate</code>s should be returned. Specify
* <code>null</code> to return all <code>X509Certificate</code>s.
* @return a <code>Collection</code> of <code>X509Certificate</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
@SuppressWarnings("unchecked")
public synchronized Collection<X509Certificate> engineGetCertificates
(CertSelector selector) throws CertStoreException {
// if ldap URI we wrap the CertSelector in an LDAPCertSelector to
// avoid LDAP DN matching issues (see LDAPCertSelector for more info)
if (ldap) {
X509CertSelector xsel = (X509CertSelector) selector;
try {
xsel = ldapHelper.wrap(xsel, xsel.getSubject(), ldapPath);
} catch (IOException ioe) {
throw new CertStoreException(ioe);
}
// Fetch the certificates via LDAP. LDAPCertStore has its own
// caching mechanism, see the class description for more info.
// Safe cast since xsel is an X509 certificate selector.
return (Collection<X509Certificate>)
ldapCertStore.getCertificates(xsel);
}
// Return the Certificates for this entry. It returns the cached value
// if it is still current and fetches the Certificates otherwise.
// For the caching details, see the top of this class.
long time = System.currentTimeMillis();
if (time - lastChecked < CHECK_INTERVAL) {
if (debug != null) {
debug.println("Returning certificates from cache");
}
return getMatchingCerts(certs, selector);
}
lastChecked = time;
try {
URLConnection connection = uri.toURL().openConnection();
if (lastModified != 0) {
connection.setIfModifiedSince(lastModified);
}
long oldLastModified = lastModified;
try (InputStream in = connection.getInputStream()) {
lastModified = connection.getLastModified();
if (oldLastModified != 0) {
if (oldLastModified == lastModified) {
if (debug != null) {
debug.println("Not modified, using cached copy");
}
return getMatchingCerts(certs, selector);
} else if (connection instanceof HttpURLConnection) {
// some proxy servers omit last modified
HttpURLConnection hconn = (HttpURLConnection)connection;
if (hconn.getResponseCode()
== HttpURLConnection.HTTP_NOT_MODIFIED) {
if (debug != null) {
debug.println("Not modified, using cached copy");
}
return getMatchingCerts(certs, selector);
}
}
}
if (debug != null) {
debug.println("Downloading new certificates...");
}
// Safe cast since factory is an X.509 certificate factory
certs = (Collection<X509Certificate>)
factory.generateCertificates(in);
}
return getMatchingCerts(certs, selector);
} catch (IOException | CertificateException e) {
if (debug != null) {
debug.println("Exception fetching certificates:");
e.printStackTrace();
}
}
// exception, forget previous values
lastModified = 0;
certs = Collections.emptySet();
return certs;
}
/**
* Iterates over the specified Collection of X509Certificates and
* returns only those that match the criteria specified in the
* CertSelector.
*/
private static Collection<X509Certificate> getMatchingCerts
(Collection<X509Certificate> certs, CertSelector selector) {
// if selector not specified, all certs match
if (selector == null) {
return certs;
}
List<X509Certificate> matchedCerts = new ArrayList<>(certs.size());
for (X509Certificate cert : certs) {
if (selector.match(cert)) {
matchedCerts.add(cert);
}
}
return matchedCerts;
}
/**
* Returns a <code>Collection</code> of <code>X509CRL</code>s that
* match the specified selector. If no <code>X509CRL</code>s
* match the selector, an empty <code>Collection</code> will be returned.
*
* @param selector A <code>CRLSelector</code> used to select which
* <code>X509CRL</code>s should be returned. Specify <code>null</code>
* to return all <code>X509CRL</code>s.
* @return A <code>Collection</code> of <code>X509CRL</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
@SuppressWarnings("unchecked")
public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector)
throws CertStoreException {
// if ldap URI we wrap the CRLSelector in an LDAPCRLSelector to
// avoid LDAP DN matching issues (see LDAPCRLSelector for more info)
if (ldap) {
X509CRLSelector xsel = (X509CRLSelector) selector;
try {
xsel = ldapHelper.wrap(xsel, null, ldapPath);
} catch (IOException ioe) {
throw new CertStoreException(ioe);
}
// Fetch the CRLs via LDAP. LDAPCertStore has its own
// caching mechanism, see the class description for more info.
// Safe cast since xsel is an X509 certificate selector.
try {
return (Collection<X509CRL>) ldapCertStore.getCRLs(xsel);
} catch (CertStoreException cse) {
throw new PKIX.CertStoreTypeException("LDAP", cse);
}
}
// Return the CRLs for this entry. It returns the cached value
// if it is still current and fetches the CRLs otherwise.
// For the caching details, see the top of this class.
long time = System.currentTimeMillis();
if (time - lastChecked < CHECK_INTERVAL) {
if (debug != null) {
debug.println("Returning CRL from cache");
}
return getMatchingCRLs(crl, selector);
}
lastChecked = time;
try {
URLConnection connection = uri.toURL().openConnection();
if (lastModified != 0) {
connection.setIfModifiedSince(lastModified);
}
long oldLastModified = lastModified;
connection.setConnectTimeout(CRL_CONNECT_TIMEOUT);
try (InputStream in = connection.getInputStream()) {
lastModified = connection.getLastModified();
if (oldLastModified != 0) {
if (oldLastModified == lastModified) {
if (debug != null) {
debug.println("Not modified, using cached copy");
}
return getMatchingCRLs(crl, selector);
} else if (connection instanceof HttpURLConnection) {
// some proxy servers omit last modified
HttpURLConnection hconn = (HttpURLConnection)connection;
if (hconn.getResponseCode()
== HttpURLConnection.HTTP_NOT_MODIFIED) {
if (debug != null) {
debug.println("Not modified, using cached copy");
}
return getMatchingCRLs(crl, selector);
}
}
}
if (debug != null) {
debug.println("Downloading new CRL...");
}
crl = (X509CRL) factory.generateCRL(in);
}
return getMatchingCRLs(crl, selector);
} catch (IOException | CRLException e) {
if (debug != null) {
debug.println("Exception fetching CRL:");
e.printStackTrace();
}
// exception, forget previous values
lastModified = 0;
crl = null;
throw new PKIX.CertStoreTypeException("URI",
new CertStoreException(e));
}
}
/**
* Checks if the specified X509CRL matches the criteria specified in the
* CRLSelector.
*/
private static Collection<X509CRL> getMatchingCRLs
(X509CRL crl, CRLSelector selector) {
if (selector == null || (crl != null && selector.match(crl))) {
return Collections.singletonList(crl);
} else {
return Collections.emptyList();
}
}
/**
* CertStoreParameters for the URICertStore.
*/
static class URICertStoreParameters implements CertStoreParameters {
private final URI uri;
private volatile int hashCode = 0;
URICertStoreParameters(URI uri) {
this.uri = uri;
}
@Override public boolean equals(Object obj) {
if (!(obj instanceof URICertStoreParameters)) {
return false;
}
URICertStoreParameters params = (URICertStoreParameters) obj;
return uri.equals(params.uri);
}
@Override public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 37*result + uri.hashCode();
hashCode = result;
}
return hashCode;
}
@Override public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
/* Cannot happen */
throw new InternalError(e.toString(), e);
}
}
}
/**
* This class allows the URICertStore to be accessed as a CertStore.
*/
private static class UCS extends CertStore {
protected UCS(CertStoreSpi spi, Provider p, String type,
CertStoreParameters params) {
super(spi, p, type, params);
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 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.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.util.Set;
import java.util.Collection;
import sun.security.util.Debug;
import sun.security.util.UntrustedCertificates;
/**
* A <code>PKIXCertPathChecker</code> implementation to check whether a
* specified certificate is distrusted.
*
* @see PKIXCertPathChecker
* @see PKIXParameters
*/
final public class UntrustedChecker extends PKIXCertPathChecker {
private static final Debug debug = Debug.getInstance("certpath");
/**
* Default Constructor
*/
public UntrustedChecker() {
// blank
}
@Override
public void init(boolean forward) throws CertPathValidatorException {
// Note that this class supports both forward and reverse modes.
}
@Override
public boolean isForwardCheckingSupported() {
// Note that this class supports both forward and reverse modes.
return true;
}
@Override
public Set<String> getSupportedExtensions() {
return null;
}
@Override
public void check(Certificate cert,
Collection<String> unresolvedCritExts)
throws CertPathValidatorException {
X509Certificate currCert = (X509Certificate)cert;
if (UntrustedCertificates.isUntrusted(currCert)) {
if (debug != null) {
debug.println("UntrustedChecker: untrusted certificate " +
currCert.getSubjectX500Principal());
}
throw new CertPathValidatorException(
"Untrusted certificate: " + currCert.getSubjectX500Principal());
}
}
}

View File

@@ -0,0 +1,235 @@
/*
* 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.cert.CertificateException;
import java.security.cert.X509Certificate;
import sun.security.util.Debug;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import sun.security.x509.KeyIdentifier;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.X509CertImpl;
/*
* This class represents a vertex in the adjacency list. A
* vertex in the builder's view is just a distinguished name
* in the directory. The Vertex contains a certificate
* along an attempted certification path, along with a pointer
* to a list of certificates that followed this one in various
* attempted certification paths.
*
* @author Sean Mullan
* @since 1.4
*/
public class Vertex {
private static final Debug debug = Debug.getInstance("certpath");
private X509Certificate cert;
private int index;
private Throwable throwable;
/**
* Constructor; creates vertex with index of -1
* Use setIndex method to set another index.
*
* @param cert X509Certificate associated with vertex
*/
Vertex(X509Certificate cert) {
this.cert = cert;
this.index = -1;
}
/**
* return the certificate for this vertex
*
* @returns X509Certificate
*/
public X509Certificate getCertificate() {
return cert;
}
/**
* get the index for this vertex, where the index is the row of the
* adjacency list that contains certificates that could follow this
* certificate.
*
* @returns int index for this vertex, or -1 if no following certificates.
*/
public int getIndex() {
return index;
}
/**
* set the index for this vertex, where the index is the row of the
* adjacency list that contains certificates that could follow this
* certificate.
*
* @param ndx int index for vertex, or -1 if no following certificates.
*/
void setIndex(int ndx) {
index = ndx;
}
/**
* return the throwable associated with this vertex;
* returns null if none.
*
* @returns Throwable
*/
public Throwable getThrowable() {
return throwable;
}
/**
* set throwable associated with this vertex; default value is null.
*
* @param throwable Throwable associated with this vertex
* (or null)
*/
void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
/**
* Return full string representation of vertex
*
* @returns String representation of vertex
*/
@Override
public String toString() {
return certToString() + throwableToString() + indexToString();
}
/**
* Return string representation of this vertex's
* certificate information.
*
* @returns String representation of certificate info
*/
public String certToString() {
StringBuilder sb = new StringBuilder();
X509CertImpl x509Cert = null;
try {
x509Cert = X509CertImpl.toImpl(cert);
} catch (CertificateException ce) {
if (debug != null) {
debug.println("Vertex.certToString() unexpected exception");
ce.printStackTrace();
}
return sb.toString();
}
sb.append("Issuer: ").append
(x509Cert.getIssuerX500Principal()).append("\n");
sb.append("Subject: ").append
(x509Cert.getSubjectX500Principal()).append("\n");
sb.append("SerialNum: ").append
(x509Cert.getSerialNumber().toString(16)).append("\n");
sb.append("Expires: ").append
(x509Cert.getNotAfter().toString()).append("\n");
boolean[] iUID = x509Cert.getIssuerUniqueID();
if (iUID != null) {
sb.append("IssuerUID: ");
for (boolean b : iUID) {
sb.append(b ? 1 : 0);
}
sb.append("\n");
}
boolean[] sUID = x509Cert.getSubjectUniqueID();
if (sUID != null) {
sb.append("SubjectUID: ");
for (boolean b : sUID) {
sb.append(b ? 1 : 0);
}
sb.append("\n");
}
try {
SubjectKeyIdentifierExtension sKeyID =
x509Cert.getSubjectKeyIdentifierExtension();
if (sKeyID != null) {
KeyIdentifier keyID = sKeyID.get(
SubjectKeyIdentifierExtension.KEY_ID);
sb.append("SubjKeyID: ").append(keyID.toString());
}
AuthorityKeyIdentifierExtension aKeyID =
x509Cert.getAuthorityKeyIdentifierExtension();
if (aKeyID != null) {
KeyIdentifier keyID = (KeyIdentifier)aKeyID.get(
AuthorityKeyIdentifierExtension.KEY_ID);
sb.append("AuthKeyID: ").append(keyID.toString());
}
} catch (IOException e) {
if (debug != null) {
debug.println("Vertex.certToString() unexpected exception");
e.printStackTrace();
}
}
return sb.toString();
}
/**
* return Vertex throwable as String compatible with
* the way toString returns other information
*
* @returns String form of exception (or "none")
*/
public String throwableToString() {
StringBuilder sb = new StringBuilder("Exception: ");
if (throwable != null)
sb.append(throwable.toString());
else
sb.append("null");
sb.append("\n");
return sb.toString();
}
/**
* return Vertex index as String compatible with
* the way other Vertex.xToString() methods display
* information.
*
* @returns String form of index as "Last cert? [Yes/No]
*/
public String moreToString() {
StringBuilder sb = new StringBuilder("Last cert? ");
sb.append((index == -1) ? "Yes" : "No");
sb.append("\n");
return sb.toString();
}
/**
* return Vertex index as String compatible with
* the way other Vertex.xToString() methods displays other information.
*
* @returns String form of index as "Index: [numeric index]"
*/
public String indexToString() {
return "Index: " + index + "\n";
}
}

View File

@@ -0,0 +1,409 @@
/*
* Copyright (c) 2000, 2023, 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.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.util.*;
import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
import sun.security.x509.AlgorithmId;
import sun.security.util.DerValue;
import sun.security.util.DerOutputStream;
import sun.security.util.DerInputStream;
/**
* A {@link java.security.cert.CertPath CertPath} (certification path)
* consisting exclusively of
* {@link java.security.cert.X509Certificate X509Certificate}s.
* <p>
* By convention, X.509 <code>CertPath</code>s are stored from target
* to trust anchor.
* That is, the issuer of one certificate is the subject of the following
* one. However, unvalidated X.509 <code>CertPath</code>s may not follow
* this convention. PKIX <code>CertPathValidator</code>s will detect any
* departure from this convention and throw a
* <code>CertPathValidatorException</code>.
*
* @author Yassir Elley
* @since 1.4
*/
public class X509CertPath extends CertPath {
private static final long serialVersionUID = 4989800333263052980L;
/**
* List of certificates in this chain
*/
private List<X509Certificate> certs;
/**
* The names of our encodings. PkiPath is the default.
*/
private static final String COUNT_ENCODING = "count";
private static final String PKCS7_ENCODING = "PKCS7";
private static final String PKIPATH_ENCODING = "PkiPath";
/**
* List of supported encodings
*/
private static final Collection<String> encodingList;
static {
List<String> list = new ArrayList<>(2);
list.add(PKIPATH_ENCODING);
list.add(PKCS7_ENCODING);
encodingList = Collections.unmodifiableCollection(list);
}
/**
* Creates an <code>X509CertPath</code> from a <code>List</code> of
* <code>X509Certificate</code>s.
* <p>
* The certificates are copied out of the supplied <code>List</code>
* object.
*
* @param certs a <code>List</code> of <code>X509Certificate</code>s
* @exception CertificateException if <code>certs</code> contains an element
* that is not an <code>X509Certificate</code>
*/
@SuppressWarnings("unchecked")
public X509CertPath(List<? extends Certificate> certs) throws CertificateException {
super("X.509");
// Ensure that the List contains only X509Certificates
//
// Note; The certs parameter is not necessarily to be of Certificate
// for some old code. For compatibility, to make sure the exception
// is CertificateException, rather than ClassCastException, please
// don't use
// for (Certificate obj : certs)
for (Object obj : certs) {
if (obj instanceof X509Certificate == false) {
throw new CertificateException
("List is not all X509Certificates: "
+ obj.getClass().getName());
}
}
// Assumes that the resulting List is thread-safe. This is true
// because we ensure that it cannot be modified after construction
// and the methods in the Sun JDK 1.4 implementation of ArrayList that
// allow read-only access are thread-safe.
this.certs = Collections.unmodifiableList(
new ArrayList<X509Certificate>((List<X509Certificate>)certs));
}
/**
* Creates an <code>X509CertPath</code>, reading the encoded form
* from an <code>InputStream</code>. The data is assumed to be in
* the default encoding.
*
* @param is the <code>InputStream</code> to read the data from
* @exception CertificateException if an exception occurs while decoding
*/
public X509CertPath(InputStream is) throws CertificateException {
this(is, PKIPATH_ENCODING);
}
/**
* Creates an <code>X509CertPath</code>, reading the encoded form
* from an InputStream. The data is assumed to be in the specified
* encoding.
*
* @param is the <code>InputStream</code> to read the data from
* @param encoding the encoding used
* @exception CertificateException if an exception occurs while decoding or
* the encoding requested is not supported
*/
public X509CertPath(InputStream is, String encoding)
throws CertificateException {
super("X.509");
switch (encoding) {
case PKIPATH_ENCODING:
certs = parsePKIPATH(is);
break;
case PKCS7_ENCODING:
certs = parsePKCS7(is);
break;
default:
throw new CertificateException("unsupported encoding");
}
}
/**
* Parse a PKIPATH format CertPath from an InputStream. Return an
* unmodifiable List of the certificates.
*
* @param is the <code>InputStream</code> to read the data from
* @return an unmodifiable List of the certificates
* @exception CertificateException if an exception occurs
*/
private static List<X509Certificate> parsePKIPATH(InputStream is)
throws CertificateException {
List<X509Certificate> certList = null;
CertificateFactory certFac = null;
if (is == null) {
throw new CertificateException("input stream is null");
}
try {
DerInputStream dis = new DerInputStream(readAllBytes(is));
DerValue[] seq = dis.getSequence(3);
if (seq.length == 0) {
return Collections.<X509Certificate>emptyList();
}
certFac = CertificateFactory.getInstance("X.509");
certList = new ArrayList<X509Certificate>(seq.length);
// append certs in reverse order (target to trust anchor)
for (int i = seq.length-1; i >= 0; i--) {
certList.add((X509Certificate)certFac.generateCertificate
(new ByteArrayInputStream(seq[i].toByteArray())));
}
return Collections.unmodifiableList(certList);
} catch (IOException ioe) {
throw new CertificateException("IOException parsing PkiPath data: "
+ ioe, ioe);
}
}
/**
* Parse a PKCS#7 format CertPath from an InputStream. Return an
* unmodifiable List of the certificates.
*
* @param is the <code>InputStream</code> to read the data from
* @return an unmodifiable List of the certificates
* @exception CertificateException if an exception occurs
*/
private static List<X509Certificate> parsePKCS7(InputStream is)
throws CertificateException {
List<X509Certificate> certList;
if (is == null) {
throw new CertificateException("input stream is null");
}
try {
if (is.markSupported() == false) {
// Copy the entire input stream into an InputStream that does
// support mark
is = new ByteArrayInputStream(readAllBytes(is));
}
PKCS7 pkcs7 = new PKCS7(is);
X509Certificate[] certArray = pkcs7.getCertificates();
// certs are optional in PKCS #7
if (certArray != null) {
certList = Arrays.asList(certArray);
} else {
// no certs provided
certList = new ArrayList<X509Certificate>(0);
}
} catch (IOException ioe) {
throw new CertificateException("IOException parsing PKCS7 data: " +
ioe);
}
// Assumes that the resulting List is thread-safe. This is true
// because we ensure that it cannot be modified after construction
// and the methods in the Sun JDK 1.4 implementation of ArrayList that
// allow read-only access are thread-safe.
return Collections.unmodifiableList(certList);
}
/*
* Reads the entire contents of an InputStream into a byte array.
*
* @param is the InputStream to read from
* @return the bytes read from the InputStream
*/
private static byte[] readAllBytes(InputStream is) throws IOException {
byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
int n;
while ((n = is.read(buffer)) != -1) {
baos.write(buffer, 0, n);
}
return baos.toByteArray();
}
/**
* Returns the encoded form of this certification path, using the
* default encoding.
*
* @return the encoded bytes
* @exception CertificateEncodingException if an encoding error occurs
*/
@Override
public byte[] getEncoded() throws CertificateEncodingException {
// @@@ Should cache the encoded form
return encodePKIPATH();
}
/**
* Encode the CertPath using PKIPATH format.
*
* @return a byte array containing the binary encoding of the PkiPath object
* @exception CertificateEncodingException if an exception occurs
*/
private byte[] encodePKIPATH() throws CertificateEncodingException {
ListIterator<X509Certificate> li = certs.listIterator(certs.size());
try {
DerOutputStream bytes = new DerOutputStream();
// encode certs in reverse order (trust anchor to target)
// according to PkiPath format
while (li.hasPrevious()) {
X509Certificate cert = li.previous();
// check for duplicate cert
if (certs.lastIndexOf(cert) != certs.indexOf(cert)) {
throw new CertificateEncodingException
("Duplicate Certificate");
}
// get encoded certificates
byte[] encoded = cert.getEncoded();
bytes.write(encoded);
}
// Wrap the data in a SEQUENCE
DerOutputStream derout = new DerOutputStream();
derout.write(DerValue.tag_SequenceOf, bytes);
return derout.toByteArray();
} catch (IOException ioe) {
throw new CertificateEncodingException("IOException encoding " +
"PkiPath data: " + ioe, ioe);
}
}
/**
* Encode the CertPath using PKCS#7 format.
*
* @return a byte array containing the binary encoding of the PKCS#7 object
* @exception CertificateEncodingException if an exception occurs
*/
private byte[] encodePKCS7() throws CertificateEncodingException {
PKCS7 p7 = new PKCS7(new AlgorithmId[0],
new ContentInfo(ContentInfo.DATA_OID, null),
certs.toArray(new X509Certificate[certs.size()]),
new SignerInfo[0]);
DerOutputStream derout = new DerOutputStream();
try {
p7.encodeSignedData(derout);
} catch (IOException ioe) {
throw new CertificateEncodingException(ioe.getMessage());
}
return derout.toByteArray();
}
/**
* Returns the encoded form of this certification path, using the
* specified encoding.
*
* @param encoding the name of the encoding to use
* @return the encoded bytes
* @exception CertificateEncodingException if an encoding error occurs or
* the encoding requested is not supported
*/
@Override
public byte[] getEncoded(String encoding)
throws CertificateEncodingException {
switch (encoding) {
case PKIPATH_ENCODING:
return encodePKIPATH();
case PKCS7_ENCODING:
return encodePKCS7();
default:
throw new CertificateEncodingException("unsupported encoding");
}
}
/**
* Returns the encodings supported by this certification path, with the
* default encoding first.
*
* @return an <code>Iterator</code> over the names of the supported
* encodings (as Strings)
*/
public static Iterator<String> getEncodingsStatic() {
return encodingList.iterator();
}
/**
* Returns an iteration of the encodings supported by this certification
* path, with the default encoding first.
* <p>
* Attempts to modify the returned <code>Iterator</code> via its
* <code>remove</code> method result in an
* <code>UnsupportedOperationException</code>.
*
* @return an <code>Iterator</code> over the names of the supported
* encodings (as Strings)
*/
@Override
public Iterator<String> getEncodings() {
return getEncodingsStatic();
}
/**
* Returns the list of certificates in this certification path.
* The <code>List</code> returned must be immutable and thread-safe.
*
* @return an immutable <code>List</code> of <code>X509Certificate</code>s
* (may be empty, but not null)
*/
@Override
public List<X509Certificate> getCertificates() {
return certs;
}
/**
* Restores the state of this object from the stream.
* <p>
* Deserialization of this object is not supported.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException(
"X509CertPaths are not directly deserializable");
}
}

View File

@@ -0,0 +1,330 @@
/*
* 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.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAPublicKey;
import javax.security.auth.x500.X500Principal;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.Cache;
import sun.security.x509.X509CertImpl;
import sun.security.provider.X509Factory;
/**
* This class represents an X.509 Certificate Pair object, which is primarily
* used to hold a pair of cross certificates issued between Certification
* Authorities. The ASN.1 structure is listed below. The forward certificate
* of the CertificatePair contains a certificate issued to this CA by another
* CA. The reverse certificate of the CertificatePair contains a certificate
* issued by this CA to another CA. When both the forward and the reverse
* certificates are present in the CertificatePair, the issuer name in one
* certificate shall match the subject name in the other and vice versa, and
* the subject public key in one certificate shall be capable of verifying the
* digital signature on the other certificate and vice versa. If a subject
* public key in one certificate does not contain required key algorithm
* parameters, then the signature check involving that key is not done.<p>
*
* The ASN.1 syntax for this object is:
* <pre>
* CertificatePair ::= SEQUENCE {
* forward [0] Certificate OPTIONAL,
* reverse [1] Certificate OPTIONAL
* -- at least one of the pair shall be present -- }
* </pre><p>
*
* This structure uses EXPLICIT tagging. References: Annex A of
* X.509(2000), X.509(1997).
*
* @author Sean Mullan
* @since 1.4
*/
public class X509CertificatePair {
/* ASN.1 explicit tags */
private static final byte TAG_FORWARD = 0;
private static final byte TAG_REVERSE = 1;
private X509Certificate forward;
private X509Certificate reverse;
private byte[] encoded;
private static final Cache<Object, X509CertificatePair> cache
= Cache.newSoftMemoryCache(750);
/**
* Creates an empty instance of X509CertificatePair.
*/
public X509CertificatePair() {}
/**
* Creates an instance of X509CertificatePair. At least one of
* the pair must be non-null.
*
* @param forward The forward component of the certificate pair
* which represents a certificate issued to this CA by other CAs.
* @param reverse The reverse component of the certificate pair
* which represents a certificate issued by this CA to other CAs.
* @throws CertificateException If an exception occurs.
*/
public X509CertificatePair(X509Certificate forward, X509Certificate reverse)
throws CertificateException {
if (forward == null && reverse == null) {
throw new CertificateException("at least one of certificate pair "
+ "must be non-null");
}
this.forward = forward;
this.reverse = reverse;
checkPair();
}
/**
* Create a new X509CertificatePair from its encoding.
*
* For internal use only, external code should use generateCertificatePair.
*/
private X509CertificatePair(byte[] encoded) throws CertificateException {
try {
parse(new DerValue(encoded));
this.encoded = encoded;
} catch (IOException ex) {
throw new CertificateException(ex.toString());
}
checkPair();
}
/**
* Clear the cache for debugging.
*/
public static synchronized void clearCache() {
cache.clear();
}
/**
* Create a X509CertificatePair from its encoding. Uses cache lookup
* if possible.
*/
public static synchronized X509CertificatePair generateCertificatePair
(byte[] encoded) throws CertificateException {
Object key = new Cache.EqualByteArray(encoded);
X509CertificatePair pair = cache.get(key);
if (pair != null) {
return pair;
}
pair = new X509CertificatePair(encoded);
key = new Cache.EqualByteArray(pair.encoded);
cache.put(key, pair);
return pair;
}
/**
* Sets the forward component of the certificate pair.
*/
public void setForward(X509Certificate cert) throws CertificateException {
checkPair();
forward = cert;
}
/**
* Sets the reverse component of the certificate pair.
*/
public void setReverse(X509Certificate cert) throws CertificateException {
checkPair();
reverse = cert;
}
/**
* Returns the forward component of the certificate pair.
*
* @return The forward certificate, or null if not set.
*/
public X509Certificate getForward() {
return forward;
}
/**
* Returns the reverse component of the certificate pair.
*
* @return The reverse certificate, or null if not set.
*/
public X509Certificate getReverse() {
return reverse;
}
/**
* Return the DER encoded form of the certificate pair.
*
* @return The encoded form of the certificate pair.
* @throws CerticateEncodingException If an encoding exception occurs.
*/
public byte[] getEncoded() throws CertificateEncodingException {
try {
if (encoded == null) {
DerOutputStream tmp = new DerOutputStream();
emit(tmp);
encoded = tmp.toByteArray();
}
} catch (IOException ex) {
throw new CertificateEncodingException(ex.toString());
}
return encoded;
}
/**
* Return a printable representation of the certificate pair.
*
* @return A String describing the contents of the pair.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("X.509 Certificate Pair: [\n");
if (forward != null)
sb.append(" Forward: ").append(forward).append("\n");
if (reverse != null)
sb.append(" Reverse: ").append(reverse).append("\n");
sb.append("]");
return sb.toString();
}
/* Parse the encoded bytes */
private void parse(DerValue val)
throws IOException, CertificateException
{
if (val.tag != DerValue.tag_Sequence) {
throw new IOException
("Sequence tag missing for X509CertificatePair");
}
while (val.data != null && val.data.available() != 0) {
DerValue opt = val.data.getDerValue();
short tag = (byte) (opt.tag & 0x01f);
switch (tag) {
case TAG_FORWARD:
if (opt.isContextSpecific() && opt.isConstructed()) {
if (forward != null) {
throw new IOException("Duplicate forward "
+ "certificate in X509CertificatePair");
}
opt = opt.data.getDerValue();
forward = X509Factory.intern
(new X509CertImpl(opt.toByteArray()));
}
break;
case TAG_REVERSE:
if (opt.isContextSpecific() && opt.isConstructed()) {
if (reverse != null) {
throw new IOException("Duplicate reverse "
+ "certificate in X509CertificatePair");
}
opt = opt.data.getDerValue();
reverse = X509Factory.intern
(new X509CertImpl(opt.toByteArray()));
}
break;
default:
throw new IOException("Invalid encoding of "
+ "X509CertificatePair");
}
}
if (forward == null && reverse == null) {
throw new CertificateException("at least one of certificate pair "
+ "must be non-null");
}
}
/* Translate to encoded bytes */
private void emit(DerOutputStream out)
throws IOException, CertificateEncodingException
{
DerOutputStream tagged = new DerOutputStream();
if (forward != null) {
DerOutputStream tmp = new DerOutputStream();
tmp.putDerValue(new DerValue(forward.getEncoded()));
tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, TAG_FORWARD), tmp);
}
if (reverse != null) {
DerOutputStream tmp = new DerOutputStream();
tmp.putDerValue(new DerValue(reverse.getEncoded()));
tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, TAG_REVERSE), tmp);
}
out.write(DerValue.tag_Sequence, tagged);
}
/*
* Check for a valid certificate pair
*/
private void checkPair() throws CertificateException {
/* if either of pair is missing, return w/o error */
if (forward == null || reverse == null) {
return;
}
/*
* If both elements of the pair are present, check that they
* are a valid pair.
*/
X500Principal fwSubject = forward.getSubjectX500Principal();
X500Principal fwIssuer = forward.getIssuerX500Principal();
X500Principal rvSubject = reverse.getSubjectX500Principal();
X500Principal rvIssuer = reverse.getIssuerX500Principal();
if (!fwIssuer.equals(rvSubject) || !rvIssuer.equals(fwSubject)) {
throw new CertificateException("subject and issuer names in "
+ "forward and reverse certificates do not match");
}
/* check signatures unless key parameters are missing */
try {
PublicKey pk = reverse.getPublicKey();
if (!(pk instanceof DSAPublicKey) ||
((DSAPublicKey)pk).getParams() != null) {
forward.verify(pk);
}
pk = forward.getPublicKey();
if (!(pk instanceof DSAPublicKey) ||
((DSAPublicKey)pk).getParams() != null) {
reverse.verify(pk);
}
} catch (GeneralSecurityException e) {
throw new CertificateException("invalid signature: "
+ e.getMessage());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2009, 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.ldap;
import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.X509CertSelector;
import java.security.cert.X509CRLSelector;
import javax.naming.CommunicationException;
import javax.naming.ServiceUnavailableException;
import javax.security.auth.x500.X500Principal;
import sun.security.provider.certpath.CertStoreHelper;
/**
* LDAP implementation of CertStoreHelper.
*/
public final class LDAPCertStoreHelper
extends CertStoreHelper
{
@Override
public CertStore getCertStore(URI uri)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException
{
return LDAPCertStore.getInstance(LDAPCertStore.getParameters(uri));
}
@Override
public X509CertSelector wrap(X509CertSelector selector,
X500Principal certSubject,
String ldapDN)
throws IOException
{
return new LDAPCertStore.LDAPCertSelector(selector, certSubject, ldapDN);
}
@Override
public X509CRLSelector wrap(X509CRLSelector selector,
Collection<X500Principal> certIssuers,
String ldapDN)
throws IOException
{
return new LDAPCertStore.LDAPCRLSelector(selector, certIssuers, ldapDN);
}
@Override
public boolean isCausedByNetworkIssue(CertStoreException e) {
Throwable t = e.getCause();
return (t != null && (t instanceof ServiceUnavailableException ||
t instanceof CommunicationException));
}
}

View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 2011, 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.ssl;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.Provider;
import java.security.cert.CertificateException;
import java.security.cert.CertSelector;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertStoreParameters;
import java.security.cert.CertStoreSpi;
import java.security.cert.CRLSelector;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.net.Socket;
import java.net.URLConnection;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
/**
* A CertStore that retrieves an SSL server's certificate chain.
*/
public final class SSLServerCertStore extends CertStoreSpi {
private final URI uri;
private final static GetChainTrustManager trustManager;
private final static SSLSocketFactory socketFactory;
private final static HostnameVerifier hostnameVerifier;
static {
trustManager = new GetChainTrustManager();
hostnameVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLSocketFactory tempFactory;
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { trustManager }, null);
tempFactory = context.getSocketFactory();
} catch (GeneralSecurityException gse) {
tempFactory = null;
}
socketFactory = tempFactory;
}
SSLServerCertStore(URI uri) throws InvalidAlgorithmParameterException {
super(null);
this.uri = uri;
}
public Collection<X509Certificate> engineGetCertificates
(CertSelector selector) throws CertStoreException {
try {
URLConnection urlConn = uri.toURL().openConnection();
if (urlConn instanceof HttpsURLConnection) {
if (socketFactory == null) {
throw new CertStoreException(
"No initialized SSLSocketFactory");
}
HttpsURLConnection https = (HttpsURLConnection)urlConn;
https.setSSLSocketFactory(socketFactory);
https.setHostnameVerifier(hostnameVerifier);
synchronized (trustManager) {
try {
https.connect();
return getMatchingCerts(
trustManager.serverChain, selector);
} catch (IOException ioe) {
// If the server certificate has already been
// retrieved, don't mind the connection state.
if (trustManager.exchangedServerCerts) {
return getMatchingCerts(
trustManager.serverChain, selector);
}
// otherwise, rethrow the exception
throw ioe;
} finally {
trustManager.cleanup();
}
}
}
} catch (IOException ioe) {
throw new CertStoreException(ioe);
}
return Collections.<X509Certificate>emptySet();
}
private static List<X509Certificate> getMatchingCerts
(List<X509Certificate> certs, CertSelector selector)
{
// if selector not specified, all certs match
if (selector == null) {
return certs;
}
List<X509Certificate> matchedCerts = new ArrayList<>(certs.size());
for (X509Certificate cert : certs) {
if (selector.match(cert)) {
matchedCerts.add(cert);
}
}
return matchedCerts;
}
public Collection<X509CRL> engineGetCRLs(CRLSelector selector)
throws CertStoreException
{
throw new UnsupportedOperationException();
}
static CertStore getInstance(URI uri)
throws InvalidAlgorithmParameterException
{
return new CS(new SSLServerCertStore(uri), null, "SSLServer", null);
}
/*
* An X509ExtendedTrustManager that ignores the server certificate
* validation.
*/
private static class GetChainTrustManager
extends X509ExtendedTrustManager {
private List<X509Certificate> serverChain =
Collections.<X509Certificate>emptyList();
private boolean exchangedServerCerts = false;
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
exchangedServerCerts = true;
this.serverChain = (chain == null)
? Collections.<X509Certificate>emptyList()
: Arrays.<X509Certificate>asList(chain);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
checkServerTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
checkServerTrusted(chain, authType);
}
void cleanup() {
exchangedServerCerts = false;
serverChain = Collections.<X509Certificate>emptyList();
}
}
/**
* This class allows the SSLServerCertStore to be accessed as a CertStore.
*/
private static class CS extends CertStore {
protected CS(CertStoreSpi spi, Provider p, String type,
CertStoreParameters params)
{
super(spi, p, type, params);
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2011, 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.ssl;
import java.io.IOException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.X509CertSelector;
import java.security.cert.X509CRLSelector;
import java.util.Collection;
import javax.security.auth.x500.X500Principal;
import sun.security.provider.certpath.CertStoreHelper;
/**
* SSL implementation of CertStoreHelper.
*/
public final class SSLServerCertStoreHelper extends CertStoreHelper {
@Override
public CertStore getCertStore(URI uri)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException
{
return SSLServerCertStore.getInstance(uri);
}
@Override
public X509CertSelector wrap(X509CertSelector selector,
X500Principal certSubject,
String ldapDN)
throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public X509CRLSelector wrap(X509CRLSelector selector,
Collection<X500Principal> certIssuers,
String ldapDN)
throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public boolean isCausedByNetworkIssue(CertStoreException e) {
Throwable t = e.getCause();
return (t != null && t instanceof IOException);
}
}