8010748: Add PKIXRevocationChecker NO_FALLBACK option and improve SOFT_FAIL option
Reviewed-by: vinnie
--- a/jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/java/security/cert/PKIXRevocationChecker.java Thu Jul 25 10:58:00 2013 -0400
@@ -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 @@
* 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 @@
* 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 @@
*
* @param uri the responder URI
*/
- public void setOCSPResponder(URI uri) {
+ public void setOcspResponder(URI uri) {
this.ocspResponder = uri;
}
@@ -114,7 +126,7 @@
*
* @return the responder URI, or {@code null} if not set
*/
- public URI getOCSPResponder() {
+ public URI getOcspResponder() {
return ocspResponder;
}
@@ -126,7 +138,7 @@
*
* @param cert the responder's certificate
*/
- public void setOCSPResponderCert(X509Certificate cert) {
+ public void setOcspResponderCert(X509Certificate cert) {
this.ocspResponderCert = cert;
}
@@ -140,7 +152,7 @@
*
* @return the responder's certificate, or {@code null} if not set
*/
- public X509Certificate getOCSPResponderCert() {
+ public X509Certificate getOcspResponderCert() {
return ocspResponderCert;
}
@@ -151,7 +163,7 @@
* @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 @@
/**
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
*/
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
}
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Thu Jul 25 10:58:00 2013 -0400
@@ -255,7 +255,9 @@
}
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 @@
*/
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;
- }
- }
}
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Thu Jul 25 10:58:00 2013 -0400
@@ -30,6 +30,7 @@
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 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 @@
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
--- a/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Thu Jul 25 10:58:00 2013 -0400
@@ -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 @@
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
--- a/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Thu Jul 25 10:58:00 2013 -0400
@@ -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.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 @@
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;
}
}
--- a/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java Thu Jul 25 10:58:00 2013 -0400
@@ -38,14 +38,7 @@
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 @@
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 @@
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 @@
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 @@
if (forward) {
throw new
CertPathValidatorException("forward checking not supported");
+ }
+ 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 {
- if (anchor != null) {
- issuerCert = anchor.getTrustedCert();
- prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey()
- : anchor.getCAPublicKey();
- }
- crlSignFlag = true;
+ certIndex = -1;
}
+ softFailExceptions.clear();
}
@Override
@@ -318,27 +326,34 @@
}
@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 @@
if (e.getReason() == BasicReason.REVOKED) {
throw e;
}
- CertPathValidatorException cause = e;
- if (softFail && e instanceof NetworkFailureException) {
- if (mode == Mode.ONLY_CRLS) return;
+ 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;
+ }
}
- // Rethrow the exception if ONLY_CRLS
- if (mode == Mode.ONLY_CRLS) {
- throw e;
- }
+ CertPathValidatorException cause = e;
// Otherwise, failover
if (debug != null) {
debug.println("RevocationChecker.check() " + e.getMessage());
@@ -382,22 +400,35 @@
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 @@
}
prevPubKey = pubKey;
crlSignFlag = certCanSignCrl(cert);
+ if (certIndex > 0) {
+ certIndex--;
+ }
}
// Maximum clock skew in milliseconds (15 minutes) allowed when checking
@@ -446,8 +480,8 @@
" 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
throw new CertPathValidatorException(ce);
}
- URI responderURI = (this.responderURI != null)
- ? this.responderURI : getOCSPServerURI(currCert);
-
X509Certificate respCert = (responderCert == null) ? issuerCert
: responderCert;
@@ -671,23 +706,38 @@
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 @@
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 @@
" 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 @@
}
}
+ @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.
--- a/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Thu Jul 25 10:58:00 2013 -0400
@@ -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 @@
buildParams.anyPolicyInhibited(),
buildParams.policyQualifiersRejected(),
rootNode);
-
checkers.add(policyChecker);
// add the algorithm checker
@@ -455,11 +454,16 @@
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
--- a/jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.java Tue Jun 11 14:09:06 2013 +0100
+++ b/jdk/test/java/security/cert/PKIXRevocationChecker/UnitTest.java Thu Jul 25 10:58:00 2013 -0400
@@ -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.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 @@
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 @@
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 @@
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()");