8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option
Reviewed-by: vinnie
This commit is contained in:
parent
6788137f6f
commit
74a0cc9f37
@ -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 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 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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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()");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user