提交 f59948e7 编写于 作者: M mullan

6745437: Add option to only check revocation of end-entity certificate in a chain of certificates

6869739: Cannot check revocation of single certificate without validating the entire chain
Reviewed-by: xuelei
上级 654e48d2
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.action;
import java.security.Security;
/**
* A convenience class for retrieving the boolean value of a security property
* as a privileged action.
*
* <p>An instance of this class can be used as the argument of
* <code>AccessController.doPrivileged</code>.
*
* <p>The following code retrieves the boolean value of the security
* property named <code>"prop"</code> as a privileged action: <p>
*
* <pre>
* boolean b = java.security.AccessController.doPrivileged
* (new GetBooleanSecurityPropertyAction("prop")).booleanValue();
* </pre>
*
*/
public class GetBooleanSecurityPropertyAction
implements java.security.PrivilegedAction<Boolean> {
private String theProp;
/**
* Constructor that takes the name of the security property whose boolean
* value needs to be determined.
*
* @param theProp the name of the security property
*/
public GetBooleanSecurityPropertyAction(String theProp) {
this.theProp = theProp;
}
/**
* Determines the boolean value of the security property whose name was
* specified in the constructor.
*
* @return the <code>Boolean</code> value of the security property.
*/
public Boolean run() {
boolean b = false;
try {
String value = Security.getProperty(theProp);
b = (value != null) && value.equalsIgnoreCase("true");
} catch (NullPointerException e) {}
return b;
}
}
/* /*
* Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,12 +26,14 @@ ...@@ -26,12 +26,14 @@
package sun.security.provider.certpath; package sun.security.provider.certpath;
import java.io.IOException; import java.io.IOException;
import java.security.AccessController;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.cert.*; import java.security.cert.*;
import java.util.*; import java.util.*;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.action.GetBooleanAction;
import sun.security.util.Debug; import sun.security.util.Debug;
import sun.security.x509.GeneralNames; import sun.security.x509.GeneralNames;
import sun.security.x509.GeneralNameInterface; import sun.security.x509.GeneralNameInterface;
...@@ -64,9 +66,8 @@ public abstract class Builder { ...@@ -64,9 +66,8 @@ public abstract class Builder {
* Authority Information Access extension shall be enabled. Currently * Authority Information Access extension shall be enabled. Currently
* disabled by default for compatibility reasons. * disabled by default for compatibility reasons.
*/ */
final static boolean USE_AIA = final static boolean USE_AIA = AccessController.doPrivileged
DistributionPointFetcher.getBooleanProperty (new GetBooleanAction("com.sun.security.enableAIAcaIssuers"));
("com.sun.security.enableAIAcaIssuers", false);
/** /**
* Initialize the builder with the input parameters. * Initialize the builder with the input parameters.
......
/* /*
* Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,9 +25,11 @@ ...@@ -25,9 +25,11 @@
package sun.security.provider.certpath; package sun.security.provider.certpath;
import java.io.*; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import sun.misc.HexDumpEncoder; import sun.misc.HexDumpEncoder;
import sun.security.x509.*; import sun.security.x509.*;
...@@ -54,21 +56,28 @@ import sun.security.util.*; ...@@ -54,21 +56,28 @@ import sun.security.util.*;
public class CertId { public class CertId {
private static final boolean debug = false; private static final boolean debug = false;
private AlgorithmId hashAlgId; private static final AlgorithmId SHA1_ALGID
private byte[] issuerNameHash; = new AlgorithmId(AlgorithmId.SHA_oid);
private byte[] issuerKeyHash; private final AlgorithmId hashAlgId;
private SerialNumber certSerialNumber; private final byte[] issuerNameHash;
private final byte[] issuerKeyHash;
private final SerialNumber certSerialNumber;
private int myhash = -1; // hashcode for this CertId private int myhash = -1; // hashcode for this CertId
/** /**
* Creates a CertId. The hash algorithm used is SHA-1. * Creates a CertId. The hash algorithm used is SHA-1.
*/ */
public CertId(X509CertImpl issuerCert, SerialNumber serialNumber) public CertId(X509Certificate issuerCert, SerialNumber serialNumber)
throws Exception { throws IOException {
// compute issuerNameHash // compute issuerNameHash
MessageDigest md = MessageDigest.getInstance("SHA1"); MessageDigest md = null;
hashAlgId = AlgorithmId.get("SHA1"); try {
md = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException nsae) {
throw new IOException("Unable to create CertId", nsae);
}
hashAlgId = SHA1_ALGID;
md.update(issuerCert.getSubjectX500Principal().getEncoded()); md.update(issuerCert.getSubjectX500Principal().getEncoded());
issuerNameHash = md.digest(); issuerNameHash = md.digest();
...@@ -90,6 +99,7 @@ public class CertId { ...@@ -90,6 +99,7 @@ public class CertId {
encoder.encode(issuerNameHash)); encoder.encode(issuerNameHash));
System.out.println("issuerKeyHash is " + System.out.println("issuerKeyHash is " +
encoder.encode(issuerKeyHash)); encoder.encode(issuerKeyHash));
System.out.println("SerialNumber is " + serialNumber.getNumber());
} }
} }
...@@ -97,7 +107,6 @@ public class CertId { ...@@ -97,7 +107,6 @@ public class CertId {
* Creates a CertId from its ASN.1 DER encoding. * Creates a CertId from its ASN.1 DER encoding.
*/ */
public CertId(DerInputStream derIn) throws IOException { public CertId(DerInputStream derIn) throws IOException {
hashAlgId = AlgorithmId.parse(derIn.getDerValue()); hashAlgId = AlgorithmId.parse(derIn.getDerValue());
issuerNameHash = derIn.getOctetString(); issuerNameHash = derIn.getOctetString();
issuerKeyHash = derIn.getOctetString(); issuerKeyHash = derIn.getOctetString();
...@@ -157,7 +166,7 @@ public class CertId { ...@@ -157,7 +166,7 @@ public class CertId {
* *
* @return the hashcode value. * @return the hashcode value.
*/ */
public int hashCode() { @Override public int hashCode() {
if (myhash == -1) { if (myhash == -1) {
myhash = hashAlgId.hashCode(); myhash = hashAlgId.hashCode();
for (int i = 0; i < issuerNameHash.length; i++) { for (int i = 0; i < issuerNameHash.length; i++) {
...@@ -180,8 +189,7 @@ public class CertId { ...@@ -180,8 +189,7 @@ public class CertId {
* @param other the object to test for equality with this object. * @param other the object to test for equality with this object.
* @return true if the objects are considered equal, false otherwise. * @return true if the objects are considered equal, false otherwise.
*/ */
public boolean equals(Object other) { @Override public boolean equals(Object other) {
if (this == other) { if (this == other) {
return true; return true;
} }
...@@ -203,7 +211,7 @@ public class CertId { ...@@ -203,7 +211,7 @@ public class CertId {
/** /**
* Create a string representation of the CertId. * Create a string representation of the CertId.
*/ */
public String toString() { @Override public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("CertId \n"); sb.append("CertId \n");
sb.append("Algorithm: " + hashAlgId.toString() +"\n"); sb.append("Algorithm: " + hashAlgId.toString() +"\n");
......
...@@ -80,6 +80,7 @@ class CrlRevocationChecker extends PKIXCertPathChecker { ...@@ -80,6 +80,7 @@ class CrlRevocationChecker extends PKIXCertPathChecker {
{ false, false, false, false, false, false, true }; { false, false, false, false, false, false, true };
private static final boolean[] ALL_REASONS = private static final boolean[] ALL_REASONS =
{true, true, true, true, true, true, true, true, true}; {true, true, true, true, true, true, true, true, true};
private boolean mOnlyEECert = false;
// Maximum clock skew in milliseconds (15 minutes) allowed when checking // Maximum clock skew in milliseconds (15 minutes) allowed when checking
// validity of CRLs // validity of CRLs
...@@ -114,6 +115,12 @@ class CrlRevocationChecker extends PKIXCertPathChecker { ...@@ -114,6 +115,12 @@ class CrlRevocationChecker extends PKIXCertPathChecker {
CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params,
Collection<X509Certificate> certs) throws CertPathValidatorException Collection<X509Certificate> certs) throws CertPathValidatorException
{ {
this(anchor, params, certs, false);
}
CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params,
Collection<X509Certificate> certs, boolean onlyEECert)
throws CertPathValidatorException {
mAnchor = anchor; mAnchor = anchor;
mParams = params; mParams = params;
mStores = new ArrayList<CertStore>(params.getCertStores()); mStores = new ArrayList<CertStore>(params.getCertStores());
...@@ -133,6 +140,7 @@ class CrlRevocationChecker extends PKIXCertPathChecker { ...@@ -133,6 +140,7 @@ class CrlRevocationChecker extends PKIXCertPathChecker {
} }
Date testDate = params.getDate(); Date testDate = params.getDate();
mCurrentTime = (testDate != null ? testDate : new Date()); mCurrentTime = (testDate != null ? testDate : new Date());
mOnlyEECert = onlyEECert;
init(false); init(false);
} }
...@@ -264,6 +272,13 @@ class CrlRevocationChecker extends PKIXCertPathChecker { ...@@ -264,6 +272,13 @@ class CrlRevocationChecker extends PKIXCertPathChecker {
" ---checking " + msg + "..."); " ---checking " + msg + "...");
} }
if (mOnlyEECert && currCert.getBasicConstraints() != -1) {
if (debug != null) {
debug.println("Skipping revocation check, not end entity cert");
}
return;
}
// reject circular dependencies - RFC 3280 is not explicit on how // reject circular dependencies - RFC 3280 is not explicit on how
// to handle this, so we feel it is safest to reject them until // to handle this, so we feel it is safest to reject them until
// the issue is resolved in the PKIX WG. // the issue is resolved in the PKIX WG.
......
...@@ -32,7 +32,7 @@ import java.security.*; ...@@ -32,7 +32,7 @@ import java.security.*;
import java.security.cert.*; import java.security.cert.*;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.action.GetPropertyAction; import sun.security.action.GetBooleanAction;
import sun.security.util.Debug; import sun.security.util.Debug;
import sun.security.util.DerOutputStream; import sun.security.util.DerOutputStream;
import sun.security.x509.*; import sun.security.x509.*;
...@@ -62,28 +62,8 @@ class DistributionPointFetcher { ...@@ -62,28 +62,8 @@ class DistributionPointFetcher {
* extension shall be enabled. Currently disabled by default for * extension shall be enabled. Currently disabled by default for
* compatibility and legal reasons. * compatibility and legal reasons.
*/ */
private final static boolean USE_CRLDP = private final static boolean USE_CRLDP = AccessController.doPrivileged
getBooleanProperty("com.sun.security.enableCRLDP", false); (new GetBooleanAction("com.sun.security.enableCRLDP"));
/**
* Return the value of the boolean System property propName.
*/
public static boolean getBooleanProperty(String propName,
boolean defaultValue) {
// if set, require value of either true or false
String b = AccessController.doPrivileged(
new GetPropertyAction(propName));
if (b == null) {
return defaultValue;
} else if (b.equalsIgnoreCase("false")) {
return false;
} else if (b.equalsIgnoreCase("true")) {
return true;
} else {
throw new RuntimeException("Value of " + propName
+ " must either be 'true' or 'false'");
}
}
// singleton instance // singleton instance
private static final DistributionPointFetcher INSTANCE = private static final DistributionPointFetcher INSTANCE =
......
...@@ -82,6 +82,7 @@ class ForwardBuilder extends Builder { ...@@ -82,6 +82,7 @@ class ForwardBuilder extends Builder {
TrustAnchor trustAnchor; TrustAnchor trustAnchor;
private Comparator<X509Certificate> comparator; private Comparator<X509Certificate> comparator;
private boolean searchAllCertStores = true; private boolean searchAllCertStores = true;
private boolean onlyEECert = false;
/** /**
* Initialize the builder with the input parameters. * Initialize the builder with the input parameters.
...@@ -89,7 +90,8 @@ class ForwardBuilder extends Builder { ...@@ -89,7 +90,8 @@ class ForwardBuilder extends Builder {
* @param params the parameter set used to build a certification path * @param params the parameter set used to build a certification path
*/ */
ForwardBuilder(PKIXBuilderParameters buildParams, ForwardBuilder(PKIXBuilderParameters buildParams,
X500Principal targetSubjectDN, boolean searchAllCertStores) X500Principal targetSubjectDN, boolean searchAllCertStores,
boolean onlyEECert)
{ {
super(buildParams, targetSubjectDN); super(buildParams, targetSubjectDN);
...@@ -108,6 +110,7 @@ class ForwardBuilder extends Builder { ...@@ -108,6 +110,7 @@ class ForwardBuilder extends Builder {
} }
comparator = new PKIXCertComparator(trustedSubjectDNs); comparator = new PKIXCertComparator(trustedSubjectDNs);
this.searchAllCertStores = searchAllCertStores; this.searchAllCertStores = searchAllCertStores;
this.onlyEECert = onlyEECert;
} }
/** /**
...@@ -875,8 +878,8 @@ class ForwardBuilder extends Builder { ...@@ -875,8 +878,8 @@ class ForwardBuilder extends Builder {
/* Check revocation if it is enabled */ /* Check revocation if it is enabled */
if (buildParams.isRevocationEnabled()) { if (buildParams.isRevocationEnabled()) {
try { try {
CrlRevocationChecker crlChecker = CrlRevocationChecker crlChecker = new CrlRevocationChecker
new CrlRevocationChecker(anchor, buildParams); (anchor, buildParams, null, onlyEECert);
crlChecker.check(cert, anchor.getCAPublicKey(), true); crlChecker.check(cert, anchor.getCAPublicKey(), true);
} catch (CertPathValidatorException cpve) { } catch (CertPathValidatorException cpve) {
if (debug != null) { if (debug != null) {
......
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.security.provider.certpath;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.HttpURLConnection;
import java.security.cert.CertificateException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CRLReason;
import java.security.cert.Extension;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static sun.security.provider.certpath.OCSPResponse.*;
import sun.security.util.Debug;
import sun.security.x509.AccessDescription;
import sun.security.x509.AuthorityInfoAccessExtension;
import sun.security.x509.GeneralName;
import sun.security.x509.GeneralNameInterface;
import sun.security.x509.URIName;
import sun.security.x509.X509CertImpl;
/**
* This is a class that checks the revocation status of a certificate(s) using
* OCSP. It is not a PKIXCertPathChecker and therefore can be used outside of
* the CertPathValidator framework. It is useful when you want to
* just check the revocation status of a certificate, and you don't want to
* incur the overhead of validating all of the certificates in the
* associated certificate chain.
*
* @author Sean Mullan
*/
public final class OCSP {
private static final Debug debug = Debug.getInstance("certpath");
private OCSP() {}
/**
* Obtains the revocation status of a certificate using OCSP using the most
* common defaults. The OCSP responder URI is retrieved from the
* certificate's AIA extension. The OCSP responder certificate is assumed
* to be the issuer's certificate (or issued by the issuer CA).
*
* @param cert the certificate to be checked
* @param issuerCert the issuer certificate
* @return the RevocationStatus
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
* @throws CertPathValidatorException if an exception occurs while
* encoding the OCSP Request or validating the OCSP Response
*/
public static RevocationStatus check(X509Certificate cert,
X509Certificate issuerCert)
throws IOException, CertPathValidatorException {
CertId certId = null;
URI responderURI = null;
try {
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
responderURI = getResponderURI(certImpl);
if (responderURI == null) {
throw new CertPathValidatorException
("No OCSP Responder URI in certificate");
}
certId = new CertId(issuerCert, certImpl.getSerialNumberObject());
} catch (CertificateException ce) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", ce);
} catch (IOException ioe) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", ioe);
}
OCSPResponse ocspResponse = check(Collections.singletonList(certId),
responderURI, issuerCert, null);
return (RevocationStatus) ocspResponse.getSingleResponse(certId);
}
/**
* Obtains the revocation status of a certificate using OCSP.
*
* @param cert the certificate to be checked
* @param issuerCert the issuer certificate
* @param responderURI the URI of the OCSP responder
* @param responderCert the OCSP responder's certificate
* @param date the time the validity of the OCSP responder's certificate
* should be checked against. If null, the current time is used.
* @return the RevocationStatus
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
* @throws CertPathValidatorException if an exception occurs while
* encoding the OCSP Request or validating the OCSP Response
*/
public static RevocationStatus check(X509Certificate cert,
X509Certificate issuerCert, URI responderURI, X509Certificate
responderCert, Date date)
throws IOException, CertPathValidatorException {
CertId certId = null;
try {
X509CertImpl certImpl = X509CertImpl.toImpl(cert);
certId = new CertId(issuerCert, certImpl.getSerialNumberObject());
} catch (CertificateException ce) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", ce);
} catch (IOException ioe) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", ioe);
}
OCSPResponse ocspResponse = check(Collections.singletonList(certId),
responderURI, responderCert, date);
return (RevocationStatus) ocspResponse.getSingleResponse(certId);
}
/**
* Checks the revocation status of a list of certificates using OCSP.
*
* @param certs the CertIds to be checked
* @param responderURI the URI of the OCSP responder
* @param responderCert the OCSP responder's certificate
* @param date the time the validity of the OCSP responder's certificate
* should be checked against. If null, the current time is used.
* @return the OCSPResponse
* @throws IOException if there is an exception connecting to or
* communicating with the OCSP responder
* @throws CertPathValidatorException if an exception occurs while
* encoding the OCSP Request or validating the OCSP Response
*/
static OCSPResponse check(List<CertId> certIds, URI responderURI,
X509Certificate responderCert, Date date)
throws IOException, CertPathValidatorException {
byte[] bytes = null;
try {
OCSPRequest request = new OCSPRequest(certIds);
bytes = request.encodeBytes();
} catch (IOException ioe) {
throw new CertPathValidatorException
("Exception while encoding OCSPRequest", ioe);
}
InputStream in = null;
OutputStream out = null;
byte[] response = null;
try {
URL url = responderURI.toURL();
if (debug != null) {
debug.println("connecting to OCSP service at: " + url);
}
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty
("Content-type", "application/ocsp-request");
con.setRequestProperty
("Content-length", String.valueOf(bytes.length));
out = con.getOutputStream();
out.write(bytes);
out.flush();
// Check the response
if (debug != null &&
con.getResponseCode() != HttpURLConnection.HTTP_OK) {
debug.println("Received HTTP error: " + con.getResponseCode()
+ " - " + con.getResponseMessage());
}
in = con.getInputStream();
int contentLength = con.getContentLength();
if (contentLength == -1) {
contentLength = Integer.MAX_VALUE;
}
response = new byte[contentLength > 2048 ? 2048 : contentLength];
int total = 0;
while (total < contentLength) {
int count = in.read(response, total, response.length - total);
if (count < 0)
break;
total += count;
if (total >= response.length && total < contentLength) {
response = Arrays.copyOf(response, total * 2);
}
}
response = Arrays.copyOf(response, total);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
throw ioe;
}
}
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
throw ioe;
}
}
}
OCSPResponse ocspResponse = null;
try {
ocspResponse = new OCSPResponse(response, date, responderCert);
} catch (IOException ioe) {
// response decoding exception
throw new CertPathValidatorException(ioe);
}
if (ocspResponse.getResponseStatus() != ResponseStatus.SUCCESSFUL) {
throw new CertPathValidatorException
("OCSP response error: " + ocspResponse.getResponseStatus());
}
// Check that the response includes a response for all of the
// certs that were supplied in the request
for (CertId certId : certIds) {
SingleResponse sr = ocspResponse.getSingleResponse(certId);
if (sr == null) {
if (debug != null) {
debug.println("No response found for CertId: " + certId);
}
throw new CertPathValidatorException(
"OCSP response does not include a response for a " +
"certificate supplied in the OCSP request");
}
if (debug != null) {
debug.println("Status of certificate (with serial number " +
certId.getSerialNumber() + ") is: " + sr.getCertStatus());
}
}
return ocspResponse;
}
/**
* Returns the URI of the OCSP Responder as specified in the
* certificate's Authority Information Access extension, or null if
* not specified.
*
* @param cert the certificate
* @return the URI of the OCSP Responder, or null if not specified
*/
public static URI getResponderURI(X509Certificate cert) {
try {
return getResponderURI(X509CertImpl.toImpl(cert));
} catch (CertificateException ce) {
// treat this case as if the cert had no extension
return null;
}
}
static URI getResponderURI(X509CertImpl certImpl) {
// Examine the certificate's AuthorityInfoAccess extension
AuthorityInfoAccessExtension aia =
certImpl.getAuthorityInfoAccessExtension();
if (aia == null) {
return null;
}
List<AccessDescription> descriptions = aia.getAccessDescriptions();
for (AccessDescription description : descriptions) {
if (description.getAccessMethod().equals(
AccessDescription.Ad_OCSP_Id)) {
GeneralName generalName = description.getAccessLocation();
if (generalName.getType() == GeneralNameInterface.NAME_URI) {
URIName uri = (URIName) generalName.getName();
return uri.getURI();
}
}
}
return null;
}
/**
* The Revocation Status of a certificate.
*/
public static interface RevocationStatus {
public enum CertStatus { GOOD, REVOKED, UNKNOWN };
/**
* Returns the revocation status.
*/
CertStatus getCertStatus();
/**
* Returns the time when the certificate was revoked, or null
* if it has not been revoked.
*/
Date getRevocationTime();
/**
* Returns the reason the certificate was revoked, or null if it
* has not been revoked.
*/
CRLReason getRevocationReason();
/**
* Returns a Map of additional extensions.
*/
Map<String, Extension> getSingleExtensions();
}
}
...@@ -25,19 +25,20 @@ ...@@ -25,19 +25,20 @@
package sun.security.provider.certpath; package sun.security.provider.certpath;
import java.io.*; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.security.AccessController; import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.security.Security; import java.security.Security;
import java.security.cert.*; import java.security.cert.*;
import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.CertPathValidatorException.BasicReason;
import java.net.*; import java.net.URI;
import java.net.URISyntaxException;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.util.*; import static sun.security.provider.certpath.OCSP.*;
import sun.security.util.Debug;
import sun.security.x509.*; import sun.security.x509.*;
/** /**
...@@ -50,27 +51,18 @@ import sun.security.x509.*; ...@@ -50,27 +51,18 @@ import sun.security.x509.*;
*/ */
class OCSPChecker extends PKIXCertPathChecker { class OCSPChecker extends PKIXCertPathChecker {
public static final String OCSP_ENABLE_PROP = "ocsp.enable"; static final String OCSP_ENABLE_PROP = "ocsp.enable";
public static final String OCSP_URL_PROP = "ocsp.responderURL"; static final String OCSP_URL_PROP = "ocsp.responderURL";
public static final String OCSP_CERT_SUBJECT_PROP = static final String OCSP_CERT_SUBJECT_PROP =
"ocsp.responderCertSubjectName"; "ocsp.responderCertSubjectName";
public static final String OCSP_CERT_ISSUER_PROP = static final String OCSP_CERT_ISSUER_PROP = "ocsp.responderCertIssuerName";
"ocsp.responderCertIssuerName"; static final String OCSP_CERT_NUMBER_PROP =
public static final String OCSP_CERT_NUMBER_PROP =
"ocsp.responderCertSerialNumber"; "ocsp.responderCertSerialNumber";
private static final String HEX_DIGITS = "0123456789ABCDEFabcdef"; private static final String HEX_DIGITS = "0123456789ABCDEFabcdef";
private static final Debug DEBUG = Debug.getInstance("certpath"); private static final Debug DEBUG = Debug.getInstance("certpath");
private static final boolean dump = false; private static final boolean dump = false;
// Supported extensions
private static final int OCSP_NONCE_DATA[] =
{ 1, 3, 6, 1, 5, 5, 7, 48, 1, 2 };
private static final ObjectIdentifier OCSP_NONCE_OID;
static {
OCSP_NONCE_OID = ObjectIdentifier.newInternal(OCSP_NONCE_DATA);
}
private int remainingCerts; private int remainingCerts;
private X509Certificate[] certs; private X509Certificate[] certs;
...@@ -79,19 +71,26 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -79,19 +71,26 @@ class OCSPChecker extends PKIXCertPathChecker {
private PKIXParameters pkixParams; private PKIXParameters pkixParams;
private boolean onlyEECert = false;
/** /**
* Default Constructor * Default Constructor
* *
* @param certPath the X509 certification path * @param certPath the X509 certification path
* @param pkixParams the input PKIX parameter set * @param pkixParams the input PKIX parameter set
* @exception CertPathValidatorException Exception thrown if cert path * @throws CertPathValidatorException if OCSPChecker can not be created
* does not validate.
*/ */
OCSPChecker(CertPath certPath, PKIXParameters pkixParams) OCSPChecker(CertPath certPath, PKIXParameters pkixParams)
throws CertPathValidatorException { throws CertPathValidatorException {
this(certPath, pkixParams, false);
}
OCSPChecker(CertPath certPath, PKIXParameters pkixParams, boolean onlyEECert)
throws CertPathValidatorException {
this.cp = certPath; this.cp = certPath;
this.pkixParams = pkixParams; this.pkixParams = pkixParams;
this.onlyEECert = onlyEECert;
List<? extends Certificate> tmp = cp.getCertificates(); List<? extends Certificate> tmp = cp.getCertificates();
certs = tmp.toArray(new X509Certificate[tmp.size()]); certs = tmp.toArray(new X509Certificate[tmp.size()]);
init(false); init(false);
...@@ -101,6 +100,7 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -101,6 +100,7 @@ class OCSPChecker extends PKIXCertPathChecker {
* Initializes the internal state of the checker from parameters * Initializes the internal state of the checker from parameters
* specified in the constructor * specified in the constructor
*/ */
@Override
public void init(boolean forward) throws CertPathValidatorException { public void init(boolean forward) throws CertPathValidatorException {
if (!forward) { if (!forward) {
remainingCerts = certs.length + 1; remainingCerts = certs.length + 1;
...@@ -110,11 +110,11 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -110,11 +110,11 @@ class OCSPChecker extends PKIXCertPathChecker {
} }
} }
public boolean isForwardCheckingSupported() { @Override public boolean isForwardCheckingSupported() {
return false; return false;
} }
public Set<String> getSupportedExtensions() { @Override public Set<String> getSupportedExtensions() {
return Collections.<String>emptySet(); return Collections.<String>emptySet();
} }
...@@ -127,300 +127,233 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -127,300 +127,233 @@ class OCSPChecker extends PKIXCertPathChecker {
* @exception CertPathValidatorException Exception is thrown if the * @exception CertPathValidatorException Exception is thrown if the
* certificate has been revoked. * certificate has been revoked.
*/ */
@Override
public void check(Certificate cert, Collection<String> unresolvedCritExts) public void check(Certificate cert, Collection<String> unresolvedCritExts)
throws CertPathValidatorException { throws CertPathValidatorException {
InputStream in = null;
OutputStream out = null;
// Decrement the certificate counter // Decrement the certificate counter
remainingCerts--; remainingCerts--;
X509CertImpl currCertImpl = null;
try { try {
X509Certificate responderCert = null; currCertImpl = X509CertImpl.toImpl((X509Certificate)cert);
boolean seekResponderCert = false; } catch (CertificateException ce) {
X500Principal responderSubjectName = null; throw new CertPathValidatorException(ce);
X500Principal responderIssuerName = null; }
BigInteger responderSerialNumber = null;
boolean seekIssuerCert = true;
X509CertImpl issuerCertImpl = null;
X509CertImpl currCertImpl =
X509CertImpl.toImpl((X509Certificate)cert);
/*
* OCSP security property values, in the following order:
* 1. ocsp.responderURL
* 2. ocsp.responderCertSubjectName
* 3. ocsp.responderCertIssuerName
* 4. ocsp.responderCertSerialNumber
*/
String[] properties = getOCSPProperties();
// Check whether OCSP is feasible before seeking cert information
URL url = getOCSPServerURL(currCertImpl, properties);
// When responder's subject name is set then the issuer/serial
// properties are ignored
if (properties[1] != null) {
responderSubjectName = new X500Principal(properties[1]);
} else if (properties[2] != null && properties[3] != null) {
responderIssuerName = new X500Principal(properties[2]);
// remove colon or space separators
String value = stripOutSeparators(properties[3]);
responderSerialNumber = new BigInteger(value, 16);
} else if (properties[2] != null || properties[3] != null) {
throw new CertPathValidatorException(
"Must specify both ocsp.responderCertIssuerName and " +
"ocsp.responderCertSerialNumber properties");
}
// If the OCSP responder cert properties are set then the
// identified cert must be located in the trust anchors or
// in the cert stores.
if (responderSubjectName != null || responderIssuerName != null) {
seekResponderCert = true;
}
// Set the issuer certificate to the next cert in the chain if (onlyEECert && currCertImpl.getBasicConstraints() != -1) {
// (unless we're processing the final cert). if (DEBUG != null) {
if (remainingCerts < certs.length) { DEBUG.println("Skipping revocation check, not end entity cert");
issuerCertImpl = X509CertImpl.toImpl(certs[remainingCerts]);
seekIssuerCert = false; // done
// By default, the OCSP responder's cert is the same as the
// issuer of the cert being validated.
if (! seekResponderCert) {
responderCert = certs[remainingCerts];
if (DEBUG != null) {
DEBUG.println("Responder's certificate is the same " +
"as the issuer of the certificate being validated");
}
}
} }
return;
}
// Check anchor certs for: /*
// - the issuer cert (of the cert being validated) * OCSP security property values, in the following order:
// - the OCSP responder's cert * 1. ocsp.responderURL
if (seekIssuerCert || seekResponderCert) { * 2. ocsp.responderCertSubjectName
* 3. ocsp.responderCertIssuerName
* 4. ocsp.responderCertSerialNumber
*/
// should cache these properties to avoid calling every time?
String[] properties = getOCSPProperties();
// Check whether OCSP is feasible before seeking cert information
URI uri = getOCSPServerURI(currCertImpl, properties[0]);
// When responder's subject name is set then the issuer/serial
// properties are ignored
X500Principal responderSubjectName = null;
X500Principal responderIssuerName = null;
BigInteger responderSerialNumber = null;
if (properties[1] != null) {
responderSubjectName = new X500Principal(properties[1]);
} else if (properties[2] != null && properties[3] != null) {
responderIssuerName = new X500Principal(properties[2]);
// remove colon or space separators
String value = stripOutSeparators(properties[3]);
responderSerialNumber = new BigInteger(value, 16);
} else if (properties[2] != null || properties[3] != null) {
throw new CertPathValidatorException(
"Must specify both ocsp.responderCertIssuerName and " +
"ocsp.responderCertSerialNumber properties");
}
if (DEBUG != null && seekResponderCert) { // If the OCSP responder cert properties are set then the
DEBUG.println("Searching trust anchors for responder's " + // identified cert must be located in the trust anchors or
"certificate"); // in the cert stores.
} boolean seekResponderCert = false;
if (responderSubjectName != null || responderIssuerName != null) {
seekResponderCert = true;
}
// Extract the anchor certs // Set the issuer certificate to the next cert in the chain
Iterator anchors = pkixParams.getTrustAnchors().iterator(); // (unless we're processing the final cert).
if (! anchors.hasNext()) { X509Certificate issuerCert = null;
throw new CertPathValidatorException( boolean seekIssuerCert = true;
"Must specify at least one trust anchor"); X509Certificate responderCert = null;
if (remainingCerts < certs.length) {
issuerCert = certs[remainingCerts];
seekIssuerCert = false; // done
// By default, the OCSP responder's cert is the same as the
// issuer of the cert being validated.
if (!seekResponderCert) {
responderCert = issuerCert;
if (DEBUG != null) {
DEBUG.println("Responder's certificate is the same " +
"as the issuer of the certificate being validated");
} }
}
}
X500Principal certIssuerName = // Check anchor certs for:
currCertImpl.getIssuerX500Principal(); // - the issuer cert (of the cert being validated)
while (anchors.hasNext() && // - the OCSP responder's cert
(seekIssuerCert || seekResponderCert)) { if (seekIssuerCert || seekResponderCert) {
TrustAnchor anchor = (TrustAnchor)anchors.next(); if (DEBUG != null && seekResponderCert) {
X509Certificate anchorCert = anchor.getTrustedCert(); DEBUG.println("Searching trust anchors for responder's " +
X500Principal anchorSubjectName = "certificate");
anchorCert.getSubjectX500Principal(); }
if (dump) { // Extract the anchor certs
System.out.println("Issuer DN is " + certIssuerName); Iterator<TrustAnchor> anchors
System.out.println("Subject DN is " + = pkixParams.getTrustAnchors().iterator();
anchorSubjectName); if (!anchors.hasNext()) {
} throw new CertPathValidatorException(
"Must specify at least one trust anchor");
}
// Check if anchor cert is the issuer cert X500Principal certIssuerName =
if (seekIssuerCert && currCertImpl.getIssuerX500Principal();
certIssuerName.equals(anchorSubjectName)) { while (anchors.hasNext() && (seekIssuerCert || seekResponderCert)) {
issuerCertImpl = X509CertImpl.toImpl(anchorCert); TrustAnchor anchor = anchors.next();
seekIssuerCert = false; // done X509Certificate anchorCert = anchor.getTrustedCert();
X500Principal anchorSubjectName =
anchorCert.getSubjectX500Principal();
// By default, the OCSP responder's cert is the same as if (dump) {
// the issuer of the cert being validated. System.out.println("Issuer DN is " + certIssuerName);
if (! seekResponderCert && responderCert == null) { System.out.println("Subject DN is " + anchorSubjectName);
responderCert = anchorCert; }
if (DEBUG != null) {
DEBUG.println("Responder's certificate is the" +
" same as the issuer of the certificate " +
"being validated");
}
}
}
// Check if anchor cert is the responder cert // Check if anchor cert is the issuer cert
if (seekResponderCert) { if (seekIssuerCert &&
// Satisfy the responder subject name property only, or certIssuerName.equals(anchorSubjectName)) {
// satisfy the responder issuer name and serial number
// properties only issuerCert = anchorCert;
if ((responderSubjectName != null && seekIssuerCert = false; // done
responderSubjectName.equals(anchorSubjectName)) ||
(responderIssuerName != null && // By default, the OCSP responder's cert is the same as
responderSerialNumber != null && // the issuer of the cert being validated.
responderIssuerName.equals( if (!seekResponderCert && responderCert == null) {
anchorCert.getIssuerX500Principal()) && responderCert = anchorCert;
responderSerialNumber.equals( if (DEBUG != null) {
anchorCert.getSerialNumber()))) { DEBUG.println("Responder's certificate is the" +
" same as the issuer of the certificate " +
responderCert = anchorCert; "being validated");
seekResponderCert = false; // done
} }
} }
} }
if (issuerCertImpl == null) {
throw new CertPathValidatorException(
"No trusted certificate for " +
currCertImpl.getIssuerDN());
}
// Check cert stores if responder cert has not yet been found // Check if anchor cert is the responder cert
if (seekResponderCert) { if (seekResponderCert) {
if (DEBUG != null) { // Satisfy the responder subject name property only, or
DEBUG.println("Searching cert stores for responder's " + // satisfy the responder issuer name and serial number
"certificate"); // properties only
} if ((responderSubjectName != null &&
X509CertSelector filter = null; responderSubjectName.equals(anchorSubjectName)) ||
if (responderSubjectName != null) { (responderIssuerName != null &&
filter = new X509CertSelector(); responderSerialNumber != null &&
filter.setSubject(responderSubjectName.getName()); responderIssuerName.equals(
} else if (responderIssuerName != null && anchorCert.getIssuerX500Principal()) &&
responderSerialNumber != null) { responderSerialNumber.equals(
filter = new X509CertSelector(); anchorCert.getSerialNumber()))) {
filter.setIssuer(responderIssuerName.getName());
filter.setSerialNumber(responderSerialNumber); responderCert = anchorCert;
} seekResponderCert = false; // done
if (filter != null) {
List<CertStore> certStores = pkixParams.getCertStores();
for (CertStore certStore : certStores) {
Iterator i =
certStore.getCertificates(filter).iterator();
if (i.hasNext()) {
responderCert = (X509Certificate) i.next();
seekResponderCert = false; // done
break;
}
}
} }
} }
} }
if (issuerCert == null) {
// Could not find the certificate identified in the OCSP properties
if (seekResponderCert) {
throw new CertPathValidatorException( throw new CertPathValidatorException(
"Cannot find the responder's certificate " + "No trusted certificate for " + currCertImpl.getIssuerDN());
"(set using the OCSP security properties).");
} }
// Construct an OCSP Request // Check cert stores if responder cert has not yet been found
OCSPRequest ocspRequest = if (seekResponderCert) {
new OCSPRequest(currCertImpl, issuerCertImpl); if (DEBUG != null) {
DEBUG.println("Searching cert stores for responder's " +
// Use the URL to the OCSP service that was created earlier "certificate");
HttpURLConnection con = (HttpURLConnection)url.openConnection(); }
if (DEBUG != null) { X509CertSelector filter = null;
DEBUG.println("connecting to OCSP service at: " + url); if (responderSubjectName != null) {
} filter = new X509CertSelector();
filter.setSubject(responderSubjectName);
// Indicate that both input and output will be performed, } else if (responderIssuerName != null &&
// that the method is POST, and that the content length is responderSerialNumber != null) {
// the length of the byte array filter = new X509CertSelector();
filter.setIssuer(responderIssuerName);
con.setDoOutput(true); filter.setSerialNumber(responderSerialNumber);
con.setDoInput(true); }
con.setRequestMethod("POST"); if (filter != null) {
con.setRequestProperty("Content-type", "application/ocsp-request"); List<CertStore> certStores = pkixParams.getCertStores();
byte[] bytes = ocspRequest.encodeBytes(); for (CertStore certStore : certStores) {
CertId certId = ocspRequest.getCertId(); Iterator i = null;
try {
con.setRequestProperty("Content-length", i = certStore.getCertificates(filter).iterator();
String.valueOf(bytes.length)); } catch (CertStoreException cse) {
out = con.getOutputStream(); // ignore and try next certStore
out.write(bytes); if (DEBUG != null) {
out.flush(); DEBUG.println("CertStore exception:" + cse);
}
// Check the response continue;
if (DEBUG != null && }
con.getResponseCode() != HttpURLConnection.HTTP_OK) { if (i.hasNext()) {
DEBUG.println("Received HTTP error: " + con.getResponseCode() + responderCert = (X509Certificate) i.next();
" - " + con.getResponseMessage()); seekResponderCert = false; // done
} break;
in = con.getInputStream(); }
}
byte[] response = null;
int total = 0;
int contentLength = con.getContentLength();
if (contentLength != -1) {
response = new byte[contentLength];
} else {
response = new byte[2048];
contentLength = Integer.MAX_VALUE;
}
while (total < contentLength) {
int count = in.read(response, total, response.length - total);
if (count < 0)
break;
total += count;
if (total >= response.length && total < contentLength) {
response = Arrays.copyOf(response, total * 2);
} }
} }
response = Arrays.copyOf(response, total); }
OCSPResponse ocspResponse = new OCSPResponse(response, pkixParams,
responderCert);
// Check that response applies to the cert that was supplied
if (! certId.equals(ocspResponse.getCertId())) {
throw new CertPathValidatorException(
"Certificate in the OCSP response does not match the " +
"certificate supplied in the OCSP request.");
}
SerialNumber serialNumber = currCertImpl.getSerialNumberObject();
int certOCSPStatus = ocspResponse.getCertStatus(serialNumber);
if (DEBUG != null) { // Could not find the certificate identified in the OCSP properties
DEBUG.println("Status of certificate (with serial number " + if (seekResponderCert) {
serialNumber.getNumber() + ") is: " + throw new CertPathValidatorException(
OCSPResponse.certStatusToText(certOCSPStatus)); "Cannot find the responder's certificate " +
} "(set using the OCSP security properties).");
}
if (certOCSPStatus == OCSPResponse.CERT_STATUS_REVOKED) { CertId certId = null;
Throwable t = new CertificateRevokedException( OCSPResponse response = null;
ocspResponse.getRevocationTime(), try {
ocspResponse.getRevocationReason(), certId = new CertId
responderCert.getSubjectX500Principal(), (issuerCert, currCertImpl.getSerialNumberObject());
ocspResponse.getSingleExtensions()); response = OCSP.check(Collections.singletonList(certId), uri,
throw new CertPathValidatorException(t.getMessage(), t, responderCert, pkixParams.getDate());
null, -1, BasicReason.REVOKED); } catch (IOException ioe) {
// should allow this to pass if network failures are acceptable
throw new CertPathValidatorException
("Unable to send OCSP request", ioe);
}
} else if (certOCSPStatus == OCSPResponse.CERT_STATUS_UNKNOWN) { RevocationStatus rs = (RevocationStatus) response.getSingleResponse(certId);
throw new CertPathValidatorException( RevocationStatus.CertStatus certStatus = rs.getCertStatus();
"Certificate's revocation status is unknown", null, cp, if (certStatus == RevocationStatus.CertStatus.REVOKED) {
remainingCerts, BasicReason.UNDETERMINED_REVOCATION_STATUS); Throwable t = new CertificateRevokedException(
} rs.getRevocationTime(), rs.getRevocationReason(),
} catch (Exception e) { responderCert.getSubjectX500Principal(),
throw new CertPathValidatorException(e); rs.getSingleExtensions());
} finally { throw new CertPathValidatorException(t.getMessage(), t,
if (in != null) { null, -1, BasicReason.REVOKED);
try { } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
in.close(); throw new CertPathValidatorException(
} catch (IOException ioe) { "Certificate's revocation status is unknown", null, cp,
throw new CertPathValidatorException(ioe); remainingCerts, BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
}
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
throw new CertPathValidatorException(ioe);
}
}
} }
} }
...@@ -431,20 +364,18 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -431,20 +364,18 @@ class OCSPChecker extends PKIXCertPathChecker {
* 3. ocsp.responderCertIssuerName * 3. ocsp.responderCertIssuerName
* 4. ocsp.responderCertSerialNumber * 4. ocsp.responderCertSerialNumber
*/ */
private static URL getOCSPServerURL(X509CertImpl currCertImpl, private static URI getOCSPServerURI(X509CertImpl currCertImpl,
String[] properties) String responderURL) throws CertPathValidatorException {
throws CertificateParsingException, CertPathValidatorException {
if (responderURL != null) {
if (properties[0] != null) { try {
try { return new URI(responderURL);
return new URL(properties[0]); } catch (URISyntaxException e) {
} catch (java.net.MalformedURLException e) {
throw new CertPathValidatorException(e); throw new CertPathValidatorException(e);
} }
} }
// Examine the certificate's AuthorityInfoAccess extension // Examine the certificate's AuthorityInfoAccess extension
AuthorityInfoAccessExtension aia = AuthorityInfoAccessExtension aia =
currCertImpl.getAuthorityInfoAccessExtension(); currCertImpl.getAuthorityInfoAccessExtension();
if (aia == null) { if (aia == null) {
...@@ -459,13 +390,8 @@ class OCSPChecker extends PKIXCertPathChecker { ...@@ -459,13 +390,8 @@ class OCSPChecker extends PKIXCertPathChecker {
GeneralName generalName = description.getAccessLocation(); GeneralName generalName = description.getAccessLocation();
if (generalName.getType() == GeneralNameInterface.NAME_URI) { if (generalName.getType() == GeneralNameInterface.NAME_URI) {
try { URIName uri = (URIName) generalName.getName();
URIName uri = (URIName) generalName.getName(); return uri.getURI();
return (new URL(uri.getName()));
} catch (java.net.MalformedURLException e) {
throw new CertPathValidatorException(e);
}
} }
} }
} }
......
/* /*
* Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,9 +26,9 @@ ...@@ -26,9 +26,9 @@
package sun.security.provider.certpath; package sun.security.provider.certpath;
import java.io.IOException; import java.io.IOException;
import java.security.cert.CertPathValidatorException; import java.util.Collections;
import java.util.List;
import sun.misc.HexDumpEncoder; import sun.misc.HexDumpEncoder;
import sun.security.x509.*;
import sun.security.util.*; import sun.security.util.*;
/** /**
...@@ -77,47 +77,33 @@ class OCSPRequest { ...@@ -77,47 +77,33 @@ class OCSPRequest {
private static final Debug debug = Debug.getInstance("certpath"); private static final Debug debug = Debug.getInstance("certpath");
private static final boolean dump = false; private static final boolean dump = false;
// Serial number of the certificates to be checked for revocation // List of request CertIds
private SerialNumber serialNumber; private final List<CertId> certIds;
// Issuer's certificate (for computing certId hash values)
private X509CertImpl issuerCert;
// CertId of the certificate to be checked
private CertId certId = null;
/* /*
* Constructs an OCSPRequest. This constructor is used * Constructs an OCSPRequest. This constructor is used
* to construct an unsigned OCSP Request for a single user cert. * to construct an unsigned OCSP Request for a single user cert.
*/ */
// used by OCSPChecker OCSPRequest(CertId certId) {
OCSPRequest(X509CertImpl userCert, X509CertImpl issuerCert) this.certIds = Collections.singletonList(certId);
throws CertPathValidatorException { }
if (issuerCert == null) { OCSPRequest(List<CertId> certIds) {
throw new CertPathValidatorException("Null IssuerCertificate"); this.certIds = certIds;
}
this.issuerCert = issuerCert;
serialNumber = userCert.getSerialNumberObject();
} }
// used by OCSPChecker
byte[] encodeBytes() throws IOException { byte[] encodeBytes() throws IOException {
// encode tbsRequest // encode tbsRequest
DerOutputStream tmp = new DerOutputStream(); DerOutputStream tmp = new DerOutputStream();
DerOutputStream derSingleReqList = new DerOutputStream(); DerOutputStream requestsOut = new DerOutputStream();
SingleRequest singleRequest = null; for (CertId certId : certIds) {
DerOutputStream certIdOut = new DerOutputStream();
try { certId.encode(certIdOut);
singleRequest = new SingleRequest(issuerCert, serialNumber); requestsOut.write(DerValue.tag_Sequence, certIdOut);
} catch (Exception e) {
throw new IOException("Error encoding OCSP request");
} }
certId = singleRequest.getCertId(); tmp.write(DerValue.tag_Sequence, requestsOut);
singleRequest.encode(derSingleReqList);
tmp.write(DerValue.tag_Sequence, derSingleReqList);
// No extensions supported // No extensions supported
DerOutputStream tbsRequest = new DerOutputStream(); DerOutputStream tbsRequest = new DerOutputStream();
tbsRequest.write(DerValue.tag_Sequence, tmp); tbsRequest.write(DerValue.tag_Sequence, tmp);
...@@ -130,35 +116,14 @@ class OCSPRequest { ...@@ -130,35 +116,14 @@ class OCSPRequest {
if (dump) { if (dump) {
HexDumpEncoder hexEnc = new HexDumpEncoder(); HexDumpEncoder hexEnc = new HexDumpEncoder();
System.out.println ("OCSPRequest bytes are... "); System.out.println("OCSPRequest bytes are... ");
System.out.println(hexEnc.encode(bytes)); System.out.println(hexEnc.encode(bytes));
} }
return(bytes); return bytes;
}
// used by OCSPChecker
CertId getCertId() {
return certId;
} }
private static class SingleRequest { List<CertId> getCertIds() {
private CertId certId; return certIds;
// No extensions are set
private SingleRequest(X509CertImpl cert, SerialNumber serialNo) throws Exception {
certId = new CertId(cert, serialNo);
}
private void encode(DerOutputStream out) throws IOException {
DerOutputStream tmp = new DerOutputStream();
certId.encode(tmp);
out.write(DerValue.tag_Sequence, tmp);
}
private CertId getCertId() {
return certId;
}
} }
} }
...@@ -28,17 +28,16 @@ package sun.security.provider.certpath; ...@@ -28,17 +28,16 @@ package sun.security.provider.certpath;
import java.io.*; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.*; import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException;
import java.security.cert.CRLReason; import java.security.cert.CRLReason;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.cert.PKIXParameters; import java.util.Collections;
import javax.security.auth.x500.X500Principal;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.Iterator;
import sun.misc.HexDumpEncoder; import sun.misc.HexDumpEncoder;
import sun.security.x509.*; import sun.security.x509.*;
import sun.security.util.*; import sun.security.util.*;
...@@ -113,32 +112,29 @@ import sun.security.util.*; ...@@ -113,32 +112,29 @@ import sun.security.util.*;
* @author Ram Marti * @author Ram Marti
*/ */
class OCSPResponse { public final class OCSPResponse {
// Certificate status CHOICE public enum ResponseStatus {
public static final int CERT_STATUS_GOOD = 0; SUCCESSFUL, // Response has valid confirmations
public static final int CERT_STATUS_REVOKED = 1; MALFORMED_REQUEST, // Illegal confirmation request
public static final int CERT_STATUS_UNKNOWN = 2; INTERNAL_ERROR, // Internal error in issuer
TRY_LATER, // Try again later
UNUSED, // is not used
SIG_REQUIRED, // Must sign the request
UNAUTHORIZED // Request unauthorized
};
private static ResponseStatus[] rsvalues = ResponseStatus.values();
private static final Debug DEBUG = Debug.getInstance("certpath"); private static final Debug DEBUG = Debug.getInstance("certpath");
private static final boolean dump = false; private static final boolean dump = false;
private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID; private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID; ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
static { private static final ObjectIdentifier OCSP_NONCE_EXTENSION_OID =
ObjectIdentifier tmp1 = null; ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 2});
ObjectIdentifier tmp2 = null;
try {
tmp1 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
tmp2 = new ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
} catch (Exception e) {
// should not happen; log and exit
}
OCSP_BASIC_RESPONSE_OID = tmp1;
OCSP_NONCE_EXTENSION_OID = tmp2;
}
// OCSP response status code private static final int CERT_STATUS_GOOD = 0;
private static final int OCSP_RESPONSE_OK = 0; private static final int CERT_STATUS_REVOKED = 1;
private static final int CERT_STATUS_UNKNOWN = 2;
// ResponderID CHOICE tags // ResponderID CHOICE tags
private static final int NAME_TAG = 1; private static final int NAME_TAG = 1;
...@@ -147,7 +143,8 @@ class OCSPResponse { ...@@ -147,7 +143,8 @@ class OCSPResponse {
// Object identifier for the OCSPSigning key purpose // Object identifier for the OCSPSigning key purpose
private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9";
private SingleResponse singleResponse; private final ResponseStatus responseStatus;
private final Map<CertId, SingleResponse> singleResponseMap;
// Maximum clock skew in milliseconds (15 minutes) allowed when checking // Maximum clock skew in milliseconds (15 minutes) allowed when checking
// validity of OCSP responses // validity of OCSP responses
...@@ -159,289 +156,289 @@ class OCSPResponse { ...@@ -159,289 +156,289 @@ class OCSPResponse {
/* /*
* Create an OCSP response from its ASN.1 DER encoding. * Create an OCSP response from its ASN.1 DER encoding.
*/ */
// used by OCSPChecker OCSPResponse(byte[] bytes, Date dateCheckedAgainst,
OCSPResponse(byte[] bytes, PKIXParameters params,
X509Certificate responderCert) X509Certificate responderCert)
throws IOException, CertPathValidatorException { throws IOException, CertPathValidatorException {
try { // OCSPResponse
int responseStatus; if (dump) {
ObjectIdentifier responseType; HexDumpEncoder hexEnc = new HexDumpEncoder();
int version; System.out.println("OCSPResponse bytes are...");
CertificateIssuerName responderName = null; System.out.println(hexEnc.encode(bytes));
Date producedAtDate; }
AlgorithmId sigAlgId; DerValue der = new DerValue(bytes);
byte[] ocspNonce; if (der.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in OCSP response: " +
// OCSPResponse "expected ASN.1 SEQUENCE tag.");
if (dump) { }
HexDumpEncoder hexEnc = new HexDumpEncoder(); DerInputStream derIn = der.getData();
System.out.println("OCSPResponse bytes are...");
System.out.println(hexEnc.encode(bytes)); // responseStatus
} int status = derIn.getEnumerated();
DerValue der = new DerValue(bytes); if (status >= 0 && status < rsvalues.length) {
if (der.tag != DerValue.tag_Sequence) { responseStatus = rsvalues[status];
throw new IOException("Bad encoding in OCSP response: " + } else {
"expected ASN.1 SEQUENCE tag."); // unspecified responseStatus
} throw new IOException("Unknown OCSPResponse status: " + status);
DerInputStream derIn = der.getData(); }
if (DEBUG != null) {
DEBUG.println("OCSP response status: " + responseStatus);
}
if (responseStatus != ResponseStatus.SUCCESSFUL) {
// no need to continue, responseBytes are not set.
singleResponseMap = Collections.emptyMap();
return;
}
// responseBytes
der = derIn.getDerValue();
if (!der.isContextSpecific((byte)0)) {
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 context specific tag 0.");
}
DerValue tmp = der.data.getDerValue();
if (tmp.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 SEQUENCE tag.");
}
// responseStatus // responseType
responseStatus = derIn.getEnumerated(); derIn = tmp.data;
ObjectIdentifier responseType = derIn.getOID();
if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) {
if (DEBUG != null) { if (DEBUG != null) {
DEBUG.println("OCSP response: " + DEBUG.println("OCSP response type: basic");
responseToText(responseStatus));
} }
if (responseStatus != OCSP_RESPONSE_OK) { } else {
throw new CertPathValidatorException( if (DEBUG != null) {
"OCSP Response Failure: " + DEBUG.println("OCSP response type: " + responseType);
responseToText(responseStatus));
} }
throw new IOException("Unsupported OCSP response type: " +
responseType);
}
// responseBytes // BasicOCSPResponse
der = derIn.getDerValue(); DerInputStream basicOCSPResponse =
if (! der.isContextSpecific((byte)0)) { new DerInputStream(derIn.getOctetString());
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 context specific tag 0.");
};
DerValue tmp = der.data.getDerValue();
if (tmp.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in responseBytes element " +
"of OCSP response: expected ASN.1 SEQUENCE tag.");
}
// responseType DerValue[] seqTmp = basicOCSPResponse.getSequence(2);
derIn = tmp.data; if (seqTmp.length < 3) {
responseType = derIn.getOID(); throw new IOException("Unexpected BasicOCSPResponse value");
if (responseType.equals(OCSP_BASIC_RESPONSE_OID)) { }
if (DEBUG != null) {
DEBUG.println("OCSP response type: basic");
}
} else {
if (DEBUG != null) {
DEBUG.println("OCSP response type: " + responseType);
}
throw new IOException("Unsupported OCSP response type: " +
responseType);
}
// BasicOCSPResponse
DerInputStream basicOCSPResponse =
new DerInputStream(derIn.getOctetString());
DerValue[] seqTmp = basicOCSPResponse.getSequence(2); DerValue responseData = seqTmp[0];
DerValue responseData = seqTmp[0];
// Need the DER encoded ResponseData to verify the signature later // Need the DER encoded ResponseData to verify the signature later
byte[] responseDataDer = seqTmp[0].toByteArray(); byte[] responseDataDer = seqTmp[0].toByteArray();
// tbsResponseData // tbsResponseData
if (responseData.tag != DerValue.tag_Sequence) { if (responseData.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding in tbsResponseData " + throw new IOException("Bad encoding in tbsResponseData " +
" element of OCSP response: expected ASN.1 SEQUENCE tag."); "element of OCSP response: expected ASN.1 SEQUENCE tag.");
} }
DerInputStream seqDerIn = responseData.data; DerInputStream seqDerIn = responseData.data;
DerValue seq = seqDerIn.getDerValue(); DerValue seq = seqDerIn.getDerValue();
// version // version
if (seq.isContextSpecific((byte)0)) { if (seq.isContextSpecific((byte)0)) {
// seq[0] is version // seq[0] is version
if (seq.isConstructed() && seq.isContextSpecific()) { if (seq.isConstructed() && seq.isContextSpecific()) {
//System.out.println ("version is available"); //System.out.println ("version is available");
seq = seq.data.getDerValue(); seq = seq.data.getDerValue();
version = seq.getInteger(); int version = seq.getInteger();
if (seq.data.available() != 0) { if (seq.data.available() != 0) {
throw new IOException("Bad encoding in version " + throw new IOException("Bad encoding in version " +
" element of OCSP response: bad format"); " element of OCSP response: bad format");
}
seq = seqDerIn.getDerValue();
} }
seq = seqDerIn.getDerValue();
} }
}
// responderID // responderID
short tag = (byte)(seq.tag & 0x1f); short tag = (byte)(seq.tag & 0x1f);
if (tag == NAME_TAG) { if (tag == NAME_TAG) {
responderName = new CertificateIssuerName(seq.getData()); if (DEBUG != null) {
if (DEBUG != null) { X500Name responderName = new X500Name(seq.getData());
DEBUG.println("OCSP Responder name: " + responderName); DEBUG.println("OCSP Responder name: " + responderName);
}
} else if (tag == KEY_TAG) {
// Ignore, for now
} else {
throw new IOException("Bad encoding in responderID element " +
"of OCSP response: expected ASN.1 context specific tag 0 " +
"or 1");
} }
} else if (tag == KEY_TAG) {
// Ignore, for now
} else {
throw new IOException("Bad encoding in responderID element of " +
"OCSP response: expected ASN.1 context specific tag 0 or 1");
}
// producedAt // producedAt
seq = seqDerIn.getDerValue(); seq = seqDerIn.getDerValue();
producedAtDate = seq.getGeneralizedTime(); if (DEBUG != null) {
Date producedAtDate = seq.getGeneralizedTime();
DEBUG.println("OCSP response produced at: " + producedAtDate);
}
// responses // responses
DerValue[] singleResponseDer = seqDerIn.getSequence(1); DerValue[] singleResponseDer = seqDerIn.getSequence(1);
// Examine only the first response singleResponseMap
singleResponse = new SingleResponse(singleResponseDer[0]); = new HashMap<CertId, SingleResponse>(singleResponseDer.length);
if (DEBUG != null) {
DEBUG.println("OCSP number of SingleResponses: "
+ singleResponseDer.length);
}
for (int i = 0; i < singleResponseDer.length; i++) {
SingleResponse singleResponse
= new SingleResponse(singleResponseDer[i]);
singleResponseMap.put(singleResponse.getCertId(), singleResponse);
}
// responseExtensions // responseExtensions
if (seqDerIn.available() > 0) { if (seqDerIn.available() > 0) {
seq = seqDerIn.getDerValue(); seq = seqDerIn.getDerValue();
if (seq.isContextSpecific((byte)1)) { if (seq.isContextSpecific((byte)1)) {
DerValue[] responseExtDer = seq.data.getSequence(3); DerValue[] responseExtDer = seq.data.getSequence(3);
Extension[] responseExtension = for (int i = 0; i < responseExtDer.length; i++) {
new Extension[responseExtDer.length]; Extension responseExtension
for (int i = 0; i < responseExtDer.length; i++) { = new Extension(responseExtDer[i]);
responseExtension[i] = new Extension(responseExtDer[i]); if (DEBUG != null) {
if (DEBUG != null) { DEBUG.println("OCSP extension: " + responseExtension);
DEBUG.println("OCSP extension: " + }
responseExtension[i]); if (responseExtension.getExtensionId().equals(
} OCSP_NONCE_EXTENSION_OID)) {
if ((responseExtension[i].getExtensionId()).equals( /*
OCSP_NONCE_EXTENSION_OID)) { ocspNonce =
ocspNonce = responseExtension[i].getExtensionValue();
responseExtension[i].getExtensionValue(); */
} else if (responseExtension.isCritical()) {
} else if (responseExtension[i].isCritical()) { throw new IOException(
throw new IOException( "Unsupported OCSP critical extension: " +
"Unsupported OCSP critical extension: " + responseExtension.getExtensionId());
responseExtension[i].getExtensionId());
}
} }
} }
} }
}
// signatureAlgorithmId // signatureAlgorithmId
sigAlgId = AlgorithmId.parse(seqTmp[1]); AlgorithmId sigAlgId = AlgorithmId.parse(seqTmp[1]);
// signature // signature
byte[] signature = seqTmp[2].getBitString(); byte[] signature = seqTmp[2].getBitString();
X509CertImpl[] x509Certs = null; X509CertImpl[] x509Certs = null;
// if seq[3] is available , then it is a sequence of certificates // if seq[3] is available , then it is a sequence of certificates
if (seqTmp.length > 3) { if (seqTmp.length > 3) {
// certs are available // certs are available
DerValue seqCert = seqTmp[3]; DerValue seqCert = seqTmp[3];
if (! seqCert.isContextSpecific((byte)0)) { if (!seqCert.isContextSpecific((byte)0)) {
throw new IOException("Bad encoding in certs element " + throw new IOException("Bad encoding in certs element of " +
"of OCSP response: expected ASN.1 context specific tag 0."); "OCSP response: expected ASN.1 context specific tag 0.");
} }
DerValue[] certs = (seqCert.getData()).getSequence(3); DerValue[] certs = seqCert.getData().getSequence(3);
x509Certs = new X509CertImpl[certs.length]; x509Certs = new X509CertImpl[certs.length];
try {
for (int i = 0; i < certs.length; i++) { for (int i = 0; i < certs.length; i++) {
x509Certs[i] = new X509CertImpl(certs[i].toByteArray()); x509Certs[i] = new X509CertImpl(certs[i].toByteArray());
} }
} catch (CertificateException ce) {
throw new IOException("Bad encoding in X509 Certificate", ce);
} }
}
// Check whether the cert returned by the responder is trusted // Check whether the cert returned by the responder is trusted
if (x509Certs != null && x509Certs[0] != null) { if (x509Certs != null && x509Certs[0] != null) {
X509CertImpl cert = x509Certs[0]; X509CertImpl cert = x509Certs[0];
// First check if the cert matches the responder cert which // First check if the cert matches the responder cert which
// was set locally. // was set locally.
if (cert.equals(responderCert)) { if (cert.equals(responderCert)) {
// cert is trusted, now verify the signed response // cert is trusted, now verify the signed response
// Next check if the cert was issued by the responder cert // Next check if the cert was issued by the responder cert
// which was set locally. // which was set locally.
} else if (cert.getIssuerX500Principal().equals( } else if (cert.getIssuerX500Principal().equals(
responderCert.getSubjectX500Principal())) { responderCert.getSubjectX500Principal())) {
// Check for the OCSPSigning key purpose // Check for the OCSPSigning key purpose
try {
List<String> keyPurposes = cert.getExtendedKeyUsage(); List<String> keyPurposes = cert.getExtendedKeyUsage();
if (keyPurposes == null || if (keyPurposes == null ||
!keyPurposes.contains(KP_OCSP_SIGNING_OID)) { !keyPurposes.contains(KP_OCSP_SIGNING_OID)) {
if (DEBUG != null) {
DEBUG.println("Responder's certificate is not " +
"valid for signing OCSP responses.");
}
throw new CertPathValidatorException( throw new CertPathValidatorException(
"Responder's certificate not valid for signing " + "Responder's certificate not valid for signing " +
"OCSP responses"); "OCSP responses");
} }
} catch (CertificateParsingException cpe) {
// assume cert is not valid for signing
throw new CertPathValidatorException(
"Responder's certificate not valid for signing " +
"OCSP responses", cpe);
}
// check the validity // check the validity
try { try {
Date dateCheckedAgainst = params.getDate(); if (dateCheckedAgainst == null) {
if (dateCheckedAgainst == null) { cert.checkValidity();
cert.checkValidity();
} else {
cert.checkValidity(dateCheckedAgainst);
}
} catch (GeneralSecurityException e) {
if (DEBUG != null) {
DEBUG.println("Responder's certificate is not " +
"within the validity period.");
}
throw new CertPathValidatorException(
"Responder's certificate not within the " +
"validity period");
}
// check for revocation
//
// A CA may specify that an OCSP client can trust a
// responder for the lifetime of the responder's
// certificate. The CA does so by including the
// extension id-pkix-ocsp-nocheck.
//
Extension noCheck =
cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
if (noCheck != null) {
if (DEBUG != null) {
DEBUG.println("Responder's certificate includes " +
"the extension id-pkix-ocsp-nocheck.");
}
} else { } else {
// we should do the revocating checking of the cert.checkValidity(dateCheckedAgainst);
// authorized responder in a future update.
} }
} catch (GeneralSecurityException e) {
throw new CertPathValidatorException(
"Responder's certificate not within the " +
"validity period", e);
}
// verify the signature // check for revocation
try { //
cert.verify(responderCert.getPublicKey()); // A CA may specify that an OCSP client can trust a
responderCert = cert; // responder for the lifetime of the responder's
// cert is trusted, now verify the signed response // certificate. The CA does so by including the
// extension id-pkix-ocsp-nocheck.
} catch (GeneralSecurityException e) { //
responderCert = null; Extension noCheck =
} cert.getExtension(PKIXExtensions.OCSPNoCheck_Id);
} else { if (noCheck != null) {
if (DEBUG != null) { if (DEBUG != null) {
DEBUG.println("Responder's certificate is not " + DEBUG.println("Responder's certificate includes " +
"authorized to sign OCSP responses."); "the extension id-pkix-ocsp-nocheck.");
} }
throw new CertPathValidatorException( } else {
"Responder's certificate not authorized to sign " + // we should do the revocation checking of the
"OCSP responses"); // authorized responder in a future update.
} }
}
// Confirm that the signed response was generated using the public // verify the signature
// key from the trusted responder cert try {
if (responderCert != null) { cert.verify(responderCert.getPublicKey());
responderCert = cert;
// cert is trusted, now verify the signed response
if (! verifyResponse(responseDataDer, responderCert, } catch (GeneralSecurityException e) {
sigAlgId, signature, params)) { responderCert = null;
if (DEBUG != null) {
DEBUG.println("Error verifying OCSP Responder's " +
"signature");
}
throw new CertPathValidatorException(
"Error verifying OCSP Responder's signature");
} }
} else { } else {
// Need responder's cert in order to verify the signature
if (DEBUG != null) {
DEBUG.println("Unable to verify OCSP Responder's " +
"signature");
}
throw new CertPathValidatorException( throw new CertPathValidatorException(
"Unable to verify OCSP Responder's signature"); "Responder's certificate is not authorized to sign " +
"OCSP responses");
} }
} catch (CertPathValidatorException cpve) {
throw cpve;
} catch (Exception e) {
throw new CertPathValidatorException(e);
} }
// Confirm that the signed response was generated using the public
// key from the trusted responder cert
if (responderCert != null) {
if (!verifyResponse(responseDataDer, responderCert,
sigAlgId, signature)) {
throw new CertPathValidatorException(
"Error verifying OCSP Responder's signature");
}
} else {
// Need responder's cert in order to verify the signature
throw new CertPathValidatorException(
"Unable to verify OCSP Responder's signature");
}
}
/**
* Returns the OCSP ResponseStatus.
*/
ResponseStatus getResponseStatus() {
return responseStatus;
} }
/* /*
...@@ -449,11 +446,10 @@ class OCSPResponse { ...@@ -449,11 +446,10 @@ class OCSPResponse {
* The responder's cert is implicitly trusted. * The responder's cert is implicitly trusted.
*/ */
private boolean verifyResponse(byte[] responseData, X509Certificate cert, private boolean verifyResponse(byte[] responseData, X509Certificate cert,
AlgorithmId sigAlgId, byte[] signBytes, PKIXParameters params) AlgorithmId sigAlgId, byte[] signBytes)
throws SignatureException { throws CertPathValidatorException {
try { try {
Signature respSignature = Signature.getInstance(sigAlgId.getName()); Signature respSignature = Signature.getInstance(sigAlgId.getName());
respSignature.initVerify(cert); respSignature.initVerify(cert);
respSignature.update(responseData); respSignature.update(responseData);
...@@ -472,92 +468,33 @@ class OCSPResponse { ...@@ -472,92 +468,33 @@ class OCSPResponse {
return false; return false;
} }
} catch (InvalidKeyException ike) { } catch (InvalidKeyException ike) {
throw new SignatureException(ike); throw new CertPathValidatorException(ike);
} catch (NoSuchAlgorithmException nsae) { } catch (NoSuchAlgorithmException nsae) {
throw new SignatureException(nsae); throw new CertPathValidatorException(nsae);
} catch (SignatureException se) {
throw new CertPathValidatorException(se);
} }
} }
/* /**
* Return the revocation status code for a given certificate. * Returns the SingleResponse of the specified CertId, or null if
* there is no response for that CertId.
*/ */
// used by OCSPChecker SingleResponse getSingleResponse(CertId certId) {
int getCertStatus(SerialNumber sn) { return singleResponseMap.get(certId);
// ignore serial number for now; if we support multiple
// requests/responses then it will be used
return singleResponse.getStatus();
}
// used by OCSPChecker
CertId getCertId() {
return singleResponse.getCertId();
}
Date getRevocationTime() {
return singleResponse.getRevocationTime();
}
CRLReason getRevocationReason() {
return singleResponse.getRevocationReason();
}
Map<String, java.security.cert.Extension> getSingleExtensions() {
return singleResponse.getSingleExtensions();
}
/*
* Map an OCSP response status code to a string.
*/
static private String responseToText(int status) {
switch (status) {
case 0:
return "Successful";
case 1:
return "Malformed request";
case 2:
return "Internal error";
case 3:
return "Try again later";
case 4:
return "Unused status code";
case 5:
return "Request must be signed";
case 6:
return "Request is unauthorized";
default:
return ("Unknown status code: " + status);
}
}
/*
* Map a certificate's revocation status code to a string.
*/
// used by OCSPChecker
static String certStatusToText(int certStatus) {
switch (certStatus) {
case 0:
return "Good";
case 1:
return "Revoked";
case 2:
return "Unknown";
default:
return ("Unknown certificate status code: " + certStatus);
}
} }
/* /*
* A class representing a single OCSP response. * A class representing a single OCSP response.
*/ */
private class SingleResponse { final static class SingleResponse implements OCSP.RevocationStatus {
private CertId certId; private final CertId certId;
private int certStatus; private final CertStatus certStatus;
private Date thisUpdate; private final Date thisUpdate;
private Date nextUpdate; private final Date nextUpdate;
private Date revocationTime; private final Date revocationTime;
private CRLReason revocationReason = CRLReason.UNSPECIFIED; private final CRLReason revocationReason;
private HashMap<String, java.security.cert.Extension> singleExtensions; private final Map<String, java.security.cert.Extension> singleExtensions;
private SingleResponse(DerValue der) throws IOException { private SingleResponse(DerValue der) throws IOException {
if (der.tag != DerValue.tag_Sequence) { if (der.tag != DerValue.tag_Sequence) {
...@@ -568,35 +505,48 @@ class OCSPResponse { ...@@ -568,35 +505,48 @@ class OCSPResponse {
certId = new CertId(tmp.getDerValue().data); certId = new CertId(tmp.getDerValue().data);
DerValue derVal = tmp.getDerValue(); DerValue derVal = tmp.getDerValue();
short tag = (byte)(derVal.tag & 0x1f); short tag = (byte)(derVal.tag & 0x1f);
if (tag == CERT_STATUS_GOOD) { if (tag == CERT_STATUS_REVOKED) {
certStatus = CERT_STATUS_GOOD; certStatus = CertStatus.REVOKED;
} else if (tag == CERT_STATUS_REVOKED) {
certStatus = CERT_STATUS_REVOKED;
revocationTime = derVal.data.getGeneralizedTime(); revocationTime = derVal.data.getGeneralizedTime();
if (derVal.data.available() != 0) { if (derVal.data.available() != 0) {
int reason = derVal.getEnumerated(); DerValue dv = derVal.data.getDerValue();
// if reason out-of-range just leave as UNSPECIFIED tag = (byte)(dv.tag & 0x1f);
if (reason >= 0 && reason < values.length) { if (tag == 0) {
revocationReason = values[reason]; int reason = dv.data.getEnumerated();
// if reason out-of-range just leave as UNSPECIFIED
if (reason >= 0 && reason < values.length) {
revocationReason = values[reason];
} else {
revocationReason = CRLReason.UNSPECIFIED;
}
} else {
revocationReason = CRLReason.UNSPECIFIED;
} }
} else {
revocationReason = CRLReason.UNSPECIFIED;
} }
// RevokedInfo // RevokedInfo
if (DEBUG != null) { if (DEBUG != null) {
DEBUG.println("Revocation time: " + revocationTime); DEBUG.println("Revocation time: " + revocationTime);
DEBUG.println("Revocation reason: " + revocationReason); DEBUG.println("Revocation reason: " + revocationReason);
} }
} else if (tag == CERT_STATUS_UNKNOWN) {
certStatus = CERT_STATUS_UNKNOWN;
} else { } else {
throw new IOException("Invalid certificate status"); revocationTime = null;
revocationReason = CRLReason.UNSPECIFIED;
if (tag == CERT_STATUS_GOOD) {
certStatus = CertStatus.GOOD;
} else if (tag == CERT_STATUS_UNKNOWN) {
certStatus = CertStatus.UNKNOWN;
} else {
throw new IOException("Invalid certificate status");
}
} }
thisUpdate = tmp.getGeneralizedTime(); thisUpdate = tmp.getGeneralizedTime();
if (tmp.available() == 0) { if (tmp.available() == 0) {
// we are done // we are done
nextUpdate = null;
} else { } else {
derVal = tmp.getDerValue(); derVal = tmp.getDerValue();
tag = (byte)(derVal.tag & 0x1f); tag = (byte)(derVal.tag & 0x1f);
...@@ -610,6 +560,8 @@ class OCSPResponse { ...@@ -610,6 +560,8 @@ class OCSPResponse {
derVal = tmp.getDerValue(); derVal = tmp.getDerValue();
tag = (byte)(derVal.tag & 0x1f); tag = (byte)(derVal.tag & 0x1f);
} }
} else {
nextUpdate = null;
} }
} }
// singleExtensions // singleExtensions
...@@ -627,7 +579,11 @@ class OCSPResponse { ...@@ -627,7 +579,11 @@ class OCSPResponse {
DEBUG.println("OCSP single extension: " + ext); DEBUG.println("OCSP single extension: " + ext);
} }
} }
} else {
singleExtensions = Collections.emptyMap();
} }
} else {
singleExtensions = Collections.emptyMap();
} }
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
...@@ -657,7 +613,7 @@ class OCSPResponse { ...@@ -657,7 +613,7 @@ class OCSPResponse {
/* /*
* Return the certificate's revocation status code * Return the certificate's revocation status code
*/ */
private int getStatus() { @Override public CertStatus getCertStatus() {
return certStatus; return certStatus;
} }
...@@ -665,28 +621,28 @@ class OCSPResponse { ...@@ -665,28 +621,28 @@ class OCSPResponse {
return certId; return certId;
} }
private Date getRevocationTime() { @Override public Date getRevocationTime() {
return revocationTime; return (Date) revocationTime.clone();
} }
private CRLReason getRevocationReason() { @Override public CRLReason getRevocationReason() {
return revocationReason; return revocationReason;
} }
private Map<String, java.security.cert.Extension> getSingleExtensions() { @Override
return singleExtensions; public Map<String, java.security.cert.Extension> getSingleExtensions() {
return Collections.unmodifiableMap(singleExtensions);
} }
/** /**
* Construct a string representation of a single OCSP response. * Construct a string representation of a single OCSP response.
*/ */
public String toString() { @Override public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("SingleResponse: \n"); sb.append("SingleResponse: \n");
sb.append(certId); sb.append(certId);
sb.append("\nCertStatus: "+ certStatusToText(getCertStatus(null)) + sb.append("\nCertStatus: "+ certStatus + "\n");
"\n"); if (certStatus == CertStatus.REVOKED) {
if (certStatus == CERT_STATUS_REVOKED) {
sb.append("revocationTime is " + revocationTime + "\n"); sb.append("revocationTime is " + revocationTime + "\n");
sb.append("revocationReason is " + revocationReason + "\n"); sb.append("revocationReason is " + revocationReason + "\n");
} }
......
/* /*
* Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -28,8 +28,6 @@ package sun.security.provider.certpath; ...@@ -28,8 +28,6 @@ package sun.security.provider.certpath;
import java.io.IOException; import java.io.IOException;
import java.security.AccessController; import java.security.AccessController;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.PrivilegedAction;
import java.security.Security;
import java.security.cert.CertPath; import java.security.cert.CertPath;
import java.security.cert.CertPathParameters; import java.security.cert.CertPathParameters;
import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException;
...@@ -49,6 +47,7 @@ import java.util.ArrayList; ...@@ -49,6 +47,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Set; import java.util.Set;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.action.GetBooleanSecurityPropertyAction;
import sun.security.util.Debug; import sun.security.util.Debug;
/** /**
...@@ -67,7 +66,8 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { ...@@ -67,7 +66,8 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi {
private List<PKIXCertPathChecker> userCheckers; private List<PKIXCertPathChecker> userCheckers;
private String sigProvider; private String sigProvider;
private BasicChecker basicChecker; private BasicChecker basicChecker;
private String ocspProperty; private boolean ocspEnabled = false;
private boolean onlyEECert = false;
/** /**
* Default constructor. * Default constructor.
...@@ -253,13 +253,12 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { ...@@ -253,13 +253,12 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi {
if (pkixParam.isRevocationEnabled()) { if (pkixParam.isRevocationEnabled()) {
// Examine OCSP security property // Examine OCSP security property
ocspProperty = AccessController.doPrivileged( ocspEnabled = AccessController.doPrivileged(
new PrivilegedAction<String>() { new GetBooleanSecurityPropertyAction
public String run() { (OCSPChecker.OCSP_ENABLE_PROP));
return onlyEECert = AccessController.doPrivileged(
Security.getProperty(OCSPChecker.OCSP_ENABLE_PROP); new GetBooleanSecurityPropertyAction
} ("com.sun.security.onlyCheckRevocationOfEECert"));
});
} }
} }
...@@ -301,15 +300,15 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { ...@@ -301,15 +300,15 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi {
if (pkixParam.isRevocationEnabled()) { if (pkixParam.isRevocationEnabled()) {
// Use OCSP if it has been enabled // Use OCSP if it has been enabled
if ("true".equalsIgnoreCase(ocspProperty)) { if (ocspEnabled) {
OCSPChecker ocspChecker = OCSPChecker ocspChecker =
new OCSPChecker(cpOriginal, pkixParam); new OCSPChecker(cpOriginal, pkixParam, onlyEECert);
certPathCheckers.add(ocspChecker); certPathCheckers.add(ocspChecker);
} }
// Always use CRLs // Always use CRLs
CrlRevocationChecker revocationChecker = CrlRevocationChecker revocationChecker = new
new CrlRevocationChecker(anchor, pkixParam, certList); CrlRevocationChecker(anchor, pkixParam, certList, onlyEECert);
certPathCheckers.add(revocationChecker); certPathCheckers.add(revocationChecker);
} }
......
/* /*
* Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package sun.security.provider.certpath; package sun.security.provider.certpath;
import java.io.IOException; import java.io.IOException;
import java.security.AccessController;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.Principal; import java.security.Principal;
...@@ -44,6 +45,7 @@ import java.util.LinkedList; ...@@ -44,6 +45,7 @@ import java.util.LinkedList;
import java.util.Set; import java.util.Set;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import sun.security.action.GetBooleanSecurityPropertyAction;
import sun.security.x509.X500Name; import sun.security.x509.X500Name;
import sun.security.x509.PKIXExtensions; import sun.security.x509.PKIXExtensions;
import sun.security.util.Debug; import sun.security.util.Debug;
...@@ -85,6 +87,7 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -85,6 +87,7 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
private PublicKey finalPublicKey; private PublicKey finalPublicKey;
private X509CertSelector targetSel; private X509CertSelector targetSel;
private List<CertStore> orderedCertStores; private List<CertStore> orderedCertStores;
private boolean onlyEECert = false;
/** /**
* Create an instance of <code>SunCertPathBuilder</code>. * Create an instance of <code>SunCertPathBuilder</code>.
...@@ -97,6 +100,9 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -97,6 +100,9 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
} catch (CertificateException e) { } catch (CertificateException e) {
throw new CertPathBuilderException(e); throw new CertPathBuilderException(e);
} }
onlyEECert = AccessController.doPrivileged(
new GetBooleanSecurityPropertyAction
("com.sun.security.onlyCheckRevocationOfEECert"));
} }
/** /**
...@@ -256,7 +262,6 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -256,7 +262,6 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
/* /*
* Private build reverse method. * Private build reverse method.
*
*/ */
private void buildReverse(List<List<Vertex>> adjacencyList, private void buildReverse(List<List<Vertex>> adjacencyList,
LinkedList<X509Certificate> certPathList) throws Exception LinkedList<X509Certificate> certPathList) throws Exception
...@@ -296,7 +301,7 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -296,7 +301,7 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
currentState.updateState(anchor); currentState.updateState(anchor);
// init the crl checker // init the crl checker
currentState.crlChecker = currentState.crlChecker =
new CrlRevocationChecker(null, buildParams); new CrlRevocationChecker(null, buildParams, null, onlyEECert);
try { try {
depthFirstSearchReverse(null, currentState, depthFirstSearchReverse(null, currentState,
new ReverseBuilder(buildParams, targetSubjectDN), adjacencyList, new ReverseBuilder(buildParams, targetSubjectDN), adjacencyList,
...@@ -341,10 +346,12 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -341,10 +346,12 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
adjacencyList.add(new LinkedList<Vertex>()); adjacencyList.add(new LinkedList<Vertex>());
// init the crl checker // init the crl checker
currentState.crlChecker = new CrlRevocationChecker(null, buildParams); currentState.crlChecker
= new CrlRevocationChecker(null, buildParams, null, onlyEECert);
depthFirstSearchForward(targetSubjectDN, currentState, depthFirstSearchForward(targetSubjectDN, currentState,
new ForwardBuilder(buildParams, targetSubjectDN, searchAllCertStores), new ForwardBuilder
(buildParams, targetSubjectDN, searchAllCertStores, onlyEECert),
adjacencyList, certPathList); adjacencyList, certPathList);
} }
...@@ -486,8 +493,8 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { ...@@ -486,8 +493,8 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi {
userCheckers.add(mustCheck, basicChecker); userCheckers.add(mustCheck, basicChecker);
mustCheck++; mustCheck++;
if (buildParams.isRevocationEnabled()) { if (buildParams.isRevocationEnabled()) {
userCheckers.add(mustCheck, userCheckers.add(mustCheck, new CrlRevocationChecker
new CrlRevocationChecker(anchor, buildParams)); (anchor, buildParams, null, onlyEECert));
mustCheck++; mustCheck++;
} }
} }
......
...@@ -113,7 +113,7 @@ public final class AccessDescription { ...@@ -113,7 +113,7 @@ public final class AccessDescription {
} else { } else {
method = accessMethod.toString(); method = accessMethod.toString();
} }
return ("accessMethod: " + method + return ("\n accessMethod: " + method +
"\n accessLocation: " + accessLocation.toString() + "\n"); "\n accessLocation: " + accessLocation.toString() + "\n");
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册