8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option

Reviewed-by: vinnie
This commit is contained in:
Sean Mullan 2013-07-25 10:58:00 -04:00
parent 6788137f6f
commit 74a0cc9f37
8 changed files with 286 additions and 203 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -50,21 +50,26 @@ import java.util.Set;
* status of certificates with OCSP and CRLs. By default, OCSP is the
* preferred mechanism for checking revocation status, with CRLs as the
* fallback mechanism. However, this preference can be switched to CRLs with
* the {@link Option#PREFER_CRLS PREFER_CRLS} option.
* the {@link Option#PREFER_CRLS PREFER_CRLS} option. In addition, the fallback
* mechanism can be disabled with the {@link Option#NO_FALLBACK NO_FALLBACK}
* option.
*
* <p>A {@code PKIXRevocationChecker} is obtained by calling the
* {@link CertPathValidator#getRevocationChecker getRevocationChecker} method
* of a PKIX {@code CertPathValidator}. Additional parameters and options
* specific to revocation can be set (by calling {@link #setOCSPResponder}
* method for instance). The {@code PKIXRevocationChecker} is added to
* a {@code PKIXParameters} object using the
* {@link PKIXParameters#addCertPathChecker addCertPathChecker}
* specific to revocation can be set (by calling the
* {@link #setOcspResponder setOcspResponder} method for instance). The
* {@code PKIXRevocationChecker} is added to a {@code PKIXParameters} object
* using the {@link PKIXParameters#addCertPathChecker addCertPathChecker}
* or {@link PKIXParameters#setCertPathCheckers setCertPathCheckers} method,
* and then the {@code PKIXParameters} is passed along with the {@code CertPath}
* to be validated to the {@link CertPathValidator#validate validate} method
* of a PKIX {@code CertPathValidator}. When supplying a revocation checker in
* this manner, it will be used to check revocation irrespective of the setting
* of the {@link PKIXParameters#isRevocationEnabled RevocationEnabled} flag.
* Similarly, a {@code PKIXRevocationChecker} may be added to a
* {@code PKIXBuilderParameters} object for use with a PKIX
* {@code CertPathBuilder}.
*
* <p>Note that when a {@code PKIXRevocationChecker} is added to
* {@code PKIXParameters}, it clones the {@code PKIXRevocationChecker};
@ -83,6 +88,13 @@ import java.util.Set;
* need not synchronize.
*
* @since 1.8
*
* @see <a href="http://www.ietf.org/rfc/rfc2560.txt"><i>RFC&nbsp;2560: X.509
* Internet Public Key Infrastructure Online Certificate Status Protocol -
* OCSP</i></a>, <br><a
* href="http://www.ietf.org/rfc/rfc5280.txt"><i>RFC&nbsp;5280: Internet X.509
* Public Key Infrastructure Certificate and Certificate Revocation List (CRL)
* Profile</i></a>
*/
public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
private URI ocspResponder;
@ -101,7 +113,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @param uri the responder URI
*/
public void setOCSPResponder(URI uri) {
public void setOcspResponder(URI uri) {
this.ocspResponder = uri;
}
@ -114,7 +126,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @return the responder URI, or {@code null} if not set
*/
public URI getOCSPResponder() {
public URI getOcspResponder() {
return ocspResponder;
}
@ -126,7 +138,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @param cert the responder's certificate
*/
public void setOCSPResponderCert(X509Certificate cert) {
public void setOcspResponderCert(X509Certificate cert) {
this.ocspResponderCert = cert;
}
@ -140,7 +152,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @return the responder's certificate, or {@code null} if not set
*/
public X509Certificate getOCSPResponderCert() {
public X509Certificate getOcspResponderCert() {
return ocspResponderCert;
}
@ -151,7 +163,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* @param extensions a list of extensions. The list is copied to protect
* against subsequent modification.
*/
public void setOCSPExtensions(List<Extension> extensions)
public void setOcspExtensions(List<Extension> extensions)
{
this.ocspExtensions = (extensions == null)
? Collections.<Extension>emptyList()
@ -161,10 +173,10 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
/**
* Gets the optional OCSP request extensions.
*
* @return an unmodifiable list of extensions. Returns an empty list if no
* @return an unmodifiable list of extensions. The list is empty if no
* extensions have been specified.
*/
public List<Extension> getOCSPExtensions() {
public List<Extension> getOcspExtensions() {
return Collections.unmodifiableList(ocspExtensions);
}
@ -177,7 +189,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* DER-encoded OCSP response for that certificate. A deep copy of
* the map is performed to protect against subsequent modification.
*/
public void setOCSPResponses(Map<X509Certificate, byte[]> responses)
public void setOcspResponses(Map<X509Certificate, byte[]> responses)
{
if (responses == null) {
this.ocspResponses = Collections.<X509Certificate, byte[]>emptyMap();
@ -200,7 +212,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* the map is returned to protect against subsequent modification.
* Returns an empty map if no responses have been specified.
*/
public Map<X509Certificate, byte[]> getOCSPResponses() {
public Map<X509Certificate, byte[]> getOcspResponses() {
Map<X509Certificate, byte[]> copy = new HashMap<>(ocspResponses.size());
for (Map.Entry<X509Certificate, byte[]> e : ocspResponses.entrySet()) {
copy.put(e.getKey(), e.getValue().clone());
@ -223,15 +235,31 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
/**
* Gets the revocation options.
*
* @return an unmodifiable set of revocation options, or an empty set if
* none are specified
* @return an unmodifiable set of revocation options. The set is empty if
* no options have been specified.
*/
public Set<Option> getOptions() {
return Collections.unmodifiableSet(options);
}
/**
* Returns a list containing the exceptions that are ignored by the
* revocation checker when the {@link Option#SOFT_FAIL SOFT_FAIL} option
* is set. The list is cleared each time {@link #init init} is called.
* The list is ordered in ascending order according to the certificate
* index returned by {@link CertPathValidatorException#getIndex getIndex}
* method of each entry.
* <p>
* An implementation of {@code PKIXRevocationChecker} is responsible for
* adding the ignored exceptions to the list.
*
* @return an unmodifiable list containing the ignored exceptions. The list
* is empty if no exceptions have been ignored.
*/
public abstract List<CertPathValidatorException> getSoftFailExceptions();
@Override
public Object clone() {
public PKIXRevocationChecker clone() {
PKIXRevocationChecker copy = (PKIXRevocationChecker)super.clone();
copy.ocspExtensions = new ArrayList<>(ocspExtensions);
copy.ocspResponses = new HashMap<>(ocspResponses);
@ -262,9 +290,26 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*/
PREFER_CRLS,
/**
* Ignore network failures. The default behavior is to consider it a
* failure if the revocation status of a certificate cannot be obtained
* due to a network error. This option applies to both OCSP and CRLs.
* Disable the fallback mechanism.
*/
NO_FALLBACK,
/**
* Allow revocation check to succeed if the revocation status cannot be
* determined for one of the following reasons:
* <p><ul>
* <li>The CRL or OCSP response cannot be obtained because of a
* network error.
* <li>The OCSP responder returns one of the following errors
* specified in section 2.3 of RFC 2560: internalError, tryLater,
* or unauthorized.
* </ul><br>
* Note that these conditions apply to both OCSP and CRLs, and unless
* the {@code NO_FALLBACK} option is set, the revocation check is
* allowed to succeed only if both mechanisms fail under one of the
* conditions as stated above.
* Exceptions that cause the network errors are ignored but can be
* later retrieved by calling the
* {@link #getSoftFailExceptions getSoftFailExceptions} method.
*/
SOFT_FAIL
}

View File

@ -255,7 +255,9 @@ public final class OCSP {
}
response = Arrays.copyOf(response, total);
} catch (IOException ioe) {
throw new NetworkFailureException(ioe);
throw new CertPathValidatorException(
"Unable to determine revocation status due to network error",
ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
} finally {
if (in != null) {
try {
@ -355,17 +357,4 @@ public final class OCSP {
*/
Map<String, Extension> getSingleExtensions();
}
static class NetworkFailureException extends CertPathValidatorException {
private static final long serialVersionUID = 0l;
NetworkFailureException(Throwable t) {
super(t);
}
@Override
public CertPathValidatorException.Reason getReason() {
return BasicReason.UNDETERMINED_REVOCATION_STATUS;
}
}
}

View File

@ -30,6 +30,7 @@ import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.CRLReason;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
@ -121,8 +122,8 @@ public final class OCSPResponse {
public enum ResponseStatus {
SUCCESSFUL, // Response has valid confirmations
MALFORMED_REQUEST, // Illegal confirmation request
INTERNAL_ERROR, // Internal error in issuer
MALFORMED_REQUEST, // Illegal request
INTERNAL_ERROR, // Internal error in responder
TRY_LATER, // Try again later
UNUSED, // is not used
SIG_REQUIRED, // Must sign the request
@ -381,9 +382,18 @@ public final class OCSPResponse {
Date date, byte[] nonce)
throws CertPathValidatorException
{
if (responseStatus != ResponseStatus.SUCCESSFUL) {
throw new CertPathValidatorException
("OCSP response error: " + responseStatus);
switch (responseStatus) {
case SUCCESSFUL:
break;
case UNAUTHORIZED:
case TRY_LATER:
case INTERNAL_ERROR:
throw new CertPathValidatorException(
"OCSP response error: " + responseStatus, null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
default:
throw new CertPathValidatorException("OCSP response error: " +
responseStatus);
}
// Check that the response includes a response for all of the

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -193,10 +193,15 @@ public final class PKIXCertPathValidator extends CertPathValidatorSpi {
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)
if (checker instanceof RevocationChecker) {
((RevocationChecker)checker).init(anchor, params);
}
}
}
// only add a RevocationChecker if revocation is enabled and

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -30,6 +30,7 @@ import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@ -235,9 +236,16 @@ class ReverseState implements State {
for (PKIXCertPathChecker checker : userCheckers) {
if (checker instanceof AlgorithmChecker) {
((AlgorithmChecker)checker).trySetTrustAnchor(anchor);
} else if (checker instanceof RevocationChecker) {
((RevocationChecker)checker).init(anchor, buildParams);
((RevocationChecker)checker).init(false);
} else if (checker instanceof PKIXRevocationChecker) {
if (revCheckerAdded) {
throw new CertPathValidatorException(
"Only one PKIXRevocationChecker can be specified");
}
// if it's our own, initialize it
if (checker instanceof RevocationChecker) {
((RevocationChecker)checker).init(anchor, buildParams);
}
((PKIXRevocationChecker)checker).init(false);
revCheckerAdded = true;
}
}

View File

@ -38,14 +38,7 @@ import java.security.Security;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.Extension;
import java.security.cert.*;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import javax.security.auth.x500.X500Principal;
import static sun.security.provider.certpath.OCSP.*;
@ -70,13 +63,16 @@ class RevocationChecker extends PKIXRevocationChecker {
private Map<X509Certificate, byte[]> ocspResponses;
private List<Extension> ocspExtensions;
private boolean legacy;
private LinkedList<CertPathValidatorException> softFailExceptions =
new LinkedList<>();
// state variables
private X509Certificate issuerCert;
private PublicKey prevPubKey;
private boolean crlSignFlag;
private int certIndex;
private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS };
private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP };
private Mode mode = Mode.PREFER_OCSP;
private static class RevocationProperties {
@ -104,9 +100,9 @@ class RevocationChecker extends PKIXRevocationChecker {
throws CertPathValidatorException
{
RevocationProperties rp = getRevocationProperties();
URI uri = getOCSPResponder();
URI uri = getOcspResponder();
responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;
X509Certificate cert = getOCSPResponderCert();
X509Certificate cert = getOcspResponderCert();
responderCert = (cert == null)
? getResponderCert(rp, params.trustAnchors(),
params.certStores())
@ -117,31 +113,38 @@ class RevocationChecker extends PKIXRevocationChecker {
case ONLY_END_ENTITY:
case PREFER_CRLS:
case SOFT_FAIL:
case NO_FALLBACK:
break;
default:
throw new CertPathValidatorException(
"Unrecognized revocation parameter option: " + option);
}
}
softFail = options.contains(Option.SOFT_FAIL);
// set mode, only end entity flag
if (legacy) {
mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS;
onlyEE = rp.onlyEE;
} else {
if (options.contains(Option.PREFER_CRLS)) {
if (options.contains(Option.NO_FALLBACK)) {
if (options.contains(Option.PREFER_CRLS)) {
mode = Mode.ONLY_CRLS;
} else {
mode = Mode.ONLY_OCSP;
}
} else if (options.contains(Option.PREFER_CRLS)) {
mode = Mode.PREFER_CRLS;
}
onlyEE = options.contains(Option.ONLY_END_ENTITY);
}
softFail = options.contains(Option.SOFT_FAIL);
if (legacy) {
crlDP = rp.crlDPEnabled;
} else {
crlDP = true;
}
ocspResponses = getOCSPResponses();
ocspExtensions = getOCSPExtensions();
ocspResponses = getOcspResponses();
ocspExtensions = getOcspExtensions();
this.anchor = anchor;
this.params = params;
@ -297,14 +300,19 @@ class RevocationChecker extends PKIXRevocationChecker {
if (forward) {
throw new
CertPathValidatorException("forward checking not supported");
} else {
if (anchor != null) {
issuerCert = anchor.getTrustedCert();
prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey()
: anchor.getCAPublicKey();
}
crlSignFlag = true;
}
if (anchor != null) {
issuerCert = anchor.getTrustedCert();
prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey()
: anchor.getCAPublicKey();
}
crlSignFlag = true;
if (params.certPath() != null) {
certIndex = params.certPath().getCertificates().size() - 1;
} else {
certIndex = -1;
}
softFailExceptions.clear();
}
@Override
@ -317,28 +325,35 @@ class RevocationChecker extends PKIXRevocationChecker {
return null;
}
@Override
public List<CertPathValidatorException> getSoftFailExceptions() {
return Collections.unmodifiableList(softFailExceptions);
}
@Override
public void check(Certificate cert, Collection<String> unresolvedCritExts)
throws CertPathValidatorException
{
X509Certificate xcert = (X509Certificate)cert;
if (onlyEE && xcert.getBasicConstraints() != -1) {
if (debug != null) {
debug.println("Skipping revocation check, not end entity cert");
}
} else {
check(xcert, unresolvedCritExts, prevPubKey, crlSignFlag);
}
updateState(xcert);
check((X509Certificate)cert, unresolvedCritExts,
prevPubKey, crlSignFlag);
}
void check(X509Certificate xcert, Collection<String> unresolvedCritExts,
PublicKey pubKey, boolean crlSignFlag)
private void check(X509Certificate xcert,
Collection<String> unresolvedCritExts,
PublicKey pubKey, boolean crlSignFlag)
throws CertPathValidatorException
{
try {
if (onlyEE && xcert.getBasicConstraints() != -1) {
if (debug != null) {
debug.println("Skipping revocation check, not end " +
"entity cert");
}
return;
}
switch (mode) {
case PREFER_OCSP:
case ONLY_OCSP:
checkOCSP(xcert, unresolvedCritExts);
break;
case PREFER_CRLS:
@ -351,14 +366,17 @@ class RevocationChecker extends PKIXRevocationChecker {
if (e.getReason() == BasicReason.REVOKED) {
throw e;
}
boolean eSoftFail = isSoftFailException(e);
if (eSoftFail) {
if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
return;
}
} else {
if (mode == Mode.ONLY_OCSP || mode == Mode.ONLY_CRLS) {
throw e;
}
}
CertPathValidatorException cause = e;
if (softFail && e instanceof NetworkFailureException) {
if (mode == Mode.ONLY_CRLS) return;
}
// Rethrow the exception if ONLY_CRLS
if (mode == Mode.ONLY_CRLS) {
throw e;
}
// Otherwise, failover
if (debug != null) {
debug.println("RevocationChecker.check() " + e.getMessage());
@ -382,22 +400,35 @@ class RevocationChecker extends PKIXRevocationChecker {
if (x.getReason() == BasicReason.REVOKED) {
throw x;
}
if (cause != null) {
if (softFail && cause instanceof NetworkFailureException) {
return;
} else {
cause.addSuppressed(x);
if (!isSoftFailException(x)) {
cause.addSuppressed(x);
throw cause;
} else {
// only pass if both exceptions were soft failures
if (!eSoftFail) {
throw cause;
}
}
if (softFail && x instanceof NetworkFailureException) {
return;
}
throw x;
}
} finally {
updateState(xcert);
}
}
private boolean isSoftFailException(CertPathValidatorException e) {
if (softFail &&
e.getReason() == BasicReason.UNDETERMINED_REVOCATION_STATUS)
{
// recreate exception with correct index
CertPathValidatorException e2 = new CertPathValidatorException(
e.getMessage(), e.getCause(), params.certPath(), certIndex,
e.getReason());
softFailExceptions.addFirst(e2);
return true;
}
return false;
}
private void updateState(X509Certificate cert)
throws CertPathValidatorException
{
@ -411,6 +442,9 @@ class RevocationChecker extends PKIXRevocationChecker {
}
prevPubKey = pubKey;
crlSignFlag = certCanSignCrl(cert);
if (certIndex > 0) {
certIndex--;
}
}
// Maximum clock skew in milliseconds (15 minutes) allowed when checking
@ -446,8 +480,8 @@ class RevocationChecker extends PKIXRevocationChecker {
" circular dependency");
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
("Could not determine revocation status", null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
Set<X509CRL> possibleCRLs = new HashSet<>();
@ -457,7 +491,7 @@ class RevocationChecker extends PKIXRevocationChecker {
CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW);
// First, check user-specified CertStores
NetworkFailureException nfe = null;
CertPathValidatorException networkFailureException = null;
for (CertStore store : certStores) {
try {
for (CRL crl : store.getCRLs(sel)) {
@ -468,10 +502,13 @@ class RevocationChecker extends PKIXRevocationChecker {
debug.println("RevocationChecker.checkCRLs() " +
"CertStoreException: " + e.getMessage());
}
if (softFail && nfe == null &&
if (networkFailureException == null &&
CertStoreHelper.isCausedByNetworkIssue(store.getType(),e)) {
// save this exception, we may need to throw it later
nfe = new NetworkFailureException(e);
networkFailureException = new CertPathValidatorException(
"Unable to determine revocation status due to " +
"network error", e, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
}
@ -508,14 +545,17 @@ class RevocationChecker extends PKIXRevocationChecker {
approvedCRLs.addAll(DistributionPointFetcher.getCRLs(
sel, signFlag, prevKey,
params.sigProvider(), certStores,
reasonsMask, anchors, params.date()));
reasonsMask, anchors, null));
}
} catch (CertStoreException e) {
if (softFail && e instanceof CertStoreTypeException) {
if (e instanceof CertStoreTypeException) {
CertStoreTypeException cste = (CertStoreTypeException)e;
if (CertStoreHelper.isCausedByNetworkIssue(cste.getType(),
e)) {
throw new NetworkFailureException(e);
throw new CertPathValidatorException(
"Unable to determine revocation status due to " +
"network error", e, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
throw new CertPathValidatorException(e);
@ -531,26 +571,26 @@ class RevocationChecker extends PKIXRevocationChecker {
stackedCerts);
return;
} catch (CertPathValidatorException cpve) {
if (nfe != null) {
if (networkFailureException != null) {
// if a network issue previously prevented us from
// retrieving a CRL from one of the user-specified
// CertStores and SOFT_FAIL is enabled, throw it now
// so it can be handled appropriately
throw nfe;
// CertStores, throw it now so it can be handled
// appropriately
throw networkFailureException;
}
throw cpve;
}
} else {
if (nfe != null) {
if (networkFailureException != null) {
// if a network issue previously prevented us from
// retrieving a CRL from one of the user-specified
// CertStores and SOFT_FAIL is enabled, throw it now
// so it can be handled appropriately
throw nfe;
// CertStores, throw it now so it can be handled
// appropriately
throw networkFailureException;
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
throw new CertPathValidatorException(
"Could not determine revocation status", null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
}
@ -595,14 +635,9 @@ class RevocationChecker extends PKIXRevocationChecker {
unresCritExts.remove(ReasonCode_Id.toString());
unresCritExts.remove(CertificateIssuer_Id.toString());
if (!unresCritExts.isEmpty()) {
if (debug != null) {
debug.println("Unrecognized "
+ "critical extension(s) in revoked CRL entry: "
+ unresCritExts);
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null,
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
throw new CertPathValidatorException(
"Unrecognized critical extension(s) in revoked " +
"CRL entry");
}
}
@ -610,11 +645,14 @@ class RevocationChecker extends PKIXRevocationChecker {
if (reasonCode == null) {
reasonCode = CRLReason.UNSPECIFIED;
}
Throwable t = new CertificateRevokedException
(entry.getRevocationDate(), reasonCode,
crl.getIssuerX500Principal(), entry.getExtensions());
throw new CertPathValidatorException(t.getMessage(), t,
null, -1, BasicReason.REVOKED);
Date revocationDate = entry.getRevocationDate();
if (revocationDate.before(params.date())) {
Throwable t = new CertificateRevokedException(
revocationDate, reasonCode,
crl.getIssuerX500Principal(), entry.getExtensions());
throw new CertPathValidatorException(
t.getMessage(), t, null, -1, BasicReason.REVOKED);
}
}
}
}
@ -630,9 +668,6 @@ class RevocationChecker extends PKIXRevocationChecker {
throw new CertPathValidatorException(ce);
}
URI responderURI = (this.responderURI != null)
? this.responderURI : getOCSPServerURI(currCert);
X509Certificate respCert = (responderCert == null) ? issuerCert
: responderCert;
@ -671,23 +706,38 @@ class RevocationChecker extends PKIXRevocationChecker {
params.date(), nonce);
} else {
URI responderURI = (this.responderURI != null)
? this.responderURI
: OCSP.getResponderURI(currCert);
if (responderURI == null) {
throw new CertPathValidatorException(
"Certificate does not specify OCSP responder", null,
null, -1);
}
response = OCSP.check(Collections.singletonList(certId),
responderURI, respCert, params.date(),
responderURI, respCert, null,
ocspExtensions);
}
} catch (IOException e) {
throw new CertPathValidatorException(e);
throw new CertPathValidatorException(
"Unable to determine revocation status due to network error",
e, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
RevocationStatus rs =
(RevocationStatus)response.getSingleResponse(certId);
RevocationStatus.CertStatus certStatus = rs.getCertStatus();
if (certStatus == RevocationStatus.CertStatus.REVOKED) {
Throwable t = new CertificateRevokedException(
rs.getRevocationTime(), rs.getRevocationReason(),
respCert.getSubjectX500Principal(), rs.getSingleExtensions());
throw new CertPathValidatorException(t.getMessage(), t, null,
-1, BasicReason.REVOKED);
Date revocationTime = rs.getRevocationTime();
if (revocationTime.before(params.date())) {
Throwable t = new CertificateRevokedException(
revocationTime, rs.getRevocationReason(),
respCert.getSubjectX500Principal(),
rs.getSingleExtensions());
throw new CertPathValidatorException(t.getMessage(), t, null,
-1, BasicReason.REVOKED);
}
} else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
throw new CertPathValidatorException(
"Certificate's revocation status is unknown", null,
@ -711,34 +761,6 @@ class RevocationChecker extends PKIXRevocationChecker {
return hexNumber.toString();
}
private static URI getOCSPServerURI(X509CertImpl cert)
throws CertPathValidatorException
{
// Examine the certificate's AuthorityInfoAccess extension
AuthorityInfoAccessExtension aia =
cert.getAuthorityInfoAccessExtension();
if (aia == null) {
throw new CertPathValidatorException(
"Must specify the location of an OCSP Responder");
}
List<AccessDescription> descriptions = aia.getAccessDescriptions();
for (AccessDescription description : descriptions) {
if (description.getAccessMethod().equals((Object)
AccessDescription.Ad_OCSP_Id)) {
GeneralName generalName = description.getAccessLocation();
if (generalName.getType() == GeneralNameInterface.NAME_URI) {
URIName uri = (URIName)generalName.getName();
return uri.getURI();
}
}
}
throw new CertPathValidatorException(
"Cannot find the location of the OCSP Responder");
}
/**
* Checks that a cert can be used to verify a CRL.
*
@ -870,8 +892,8 @@ class RevocationChecker extends PKIXRevocationChecker {
" circular dependency");
}
throw new CertPathValidatorException
("Could not determine revocation status", null, null,
-1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
("Could not determine revocation status", null, null, -1,
BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
// Try to find another key that might be able to sign
@ -1067,6 +1089,15 @@ class RevocationChecker extends PKIXRevocationChecker {
}
}
@Override
public RevocationChecker clone() {
RevocationChecker copy = (RevocationChecker)super.clone();
// we don't deep-copy the exceptions, but that is ok because they
// are never modified after they are instantiated
copy.softFailExceptions = new LinkedList<>(softFailExceptions);
return copy;
}
/*
* This inner class extends the X509CertSelector to add an additional
* check to make sure the subject public key isn't on a particular list.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
@ -422,7 +422,6 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
buildParams.anyPolicyInhibited(),
buildParams.policyQualifiersRejected(),
rootNode);
checkers.add(policyChecker);
// add the algorithm checker
@ -455,11 +454,16 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
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)
if (ckr instanceof RevocationChecker) {
((RevocationChecker)ckr).init(builder.trustAnchor,
buildParams);
}
}
}
// only add a RevocationChecker if revocation is enabled and

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -23,7 +23,7 @@
/**
* @test
* @bug 6854712 7171570
* @bug 6854712 7171570 8010748
* @summary Basic unit test for PKIXRevocationChecker
*/
@ -32,19 +32,9 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.security.cert.CertificateFactory;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathChecker;
import java.security.cert.CertPathValidator;
import java.security.cert.Extension;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.*;
import java.security.cert.PKIXRevocationChecker.Option;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class UnitTest {
@ -56,22 +46,23 @@ public class UnitTest {
System.out.println("Testing that get methods return null or " +
"empty lists/sets/maps");
requireNull(prc.getOCSPResponder(), "getOCSPResponder()");
requireNull(prc.getOCSPResponderCert(), "getOCSPResponderCert()");
requireEmpty(prc.getOCSPExtensions(), "getOCSPExtensions()");
requireEmpty(prc.getOCSPResponses(), "getOCSPResponses()");
requireNull(prc.getOcspResponder(), "getOcspResponder()");
requireNull(prc.getOcspResponderCert(), "getOcspResponderCert()");
requireEmpty(prc.getOcspExtensions(), "getOcspExtensions()");
requireEmpty(prc.getOcspResponses(), "getOcspResponses()");
requireEmpty(prc.getOptions(), "getOptions()");
requireEmpty(prc.getSoftFailExceptions(), "getSoftFailExceptions()");
System.out.println("Testing that get methods return same parameters " +
"that are passed to set methods");
URI uri = new URI("http://localhost");
prc.setOCSPResponder(uri);
requireEquals(uri, prc.getOCSPResponder(), "getOCSPResponder()");
prc.setOcspResponder(uri);
requireEquals(uri, prc.getOcspResponder(), "getOcspResponder()");
X509Certificate cert = getCert();
prc.setOCSPResponderCert(cert);
requireEquals(cert, prc.getOCSPResponderCert(),
"getOCSPResponderCert()");
prc.setOcspResponderCert(cert);
requireEquals(cert, prc.getOcspResponderCert(),
"getOcspResponderCert()");
List<Extension> exts = new ArrayList<>();
for (String oid : cert.getNonCriticalExtensionOIDs()) {
@ -79,8 +70,8 @@ public class UnitTest {
exts.add(new ExtensionImpl(oid,
cert.getExtensionValue(oid), false));
}
prc.setOCSPExtensions(exts);
requireEquals(exts, prc.getOCSPExtensions(), "getOCSPExtensions()");
prc.setOcspExtensions(exts);
requireEquals(exts, prc.getOcspExtensions(), "getOcspExtensions()");
Set<Option> options = EnumSet.of(Option.ONLY_END_ENTITY);
prc.setOptions(options);
@ -88,14 +79,14 @@ public class UnitTest {
System.out.println("Testing that parameters are re-initialized to " +
"default values if null is passed to set methods");
prc.setOCSPResponder(null);
requireNull(prc.getOCSPResponder(), "getOCSPResponder()");
prc.setOCSPResponderCert(null);
requireNull(prc.getOCSPResponderCert(), "getOCSPResponderCert()");
prc.setOCSPExtensions(null);
requireEmpty(prc.getOCSPExtensions(), "getOCSPExtensions()");
prc.setOCSPResponses(null);
requireEmpty(prc.getOCSPResponses(), "getOCSPResponses()");
prc.setOcspResponder(null);
requireNull(prc.getOcspResponder(), "getOcspResponder()");
prc.setOcspResponderCert(null);
requireNull(prc.getOcspResponderCert(), "getOcspResponderCert()");
prc.setOcspExtensions(null);
requireEmpty(prc.getOcspExtensions(), "getOcspExtensions()");
prc.setOcspResponses(null);
requireEmpty(prc.getOcspResponses(), "getOcspResponses()");
prc.setOptions(null);
requireEmpty(prc.getOptions(), "getOptions()");