|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package sun.security.provider.certpath; |
|
|
|
import java.io.InputStream; |
|
import java.io.IOException; |
|
import java.io.OutputStream; |
|
import java.net.URI; |
|
import java.net.URL; |
|
import java.net.HttpURLConnection; |
|
import java.security.cert.CertificateException; |
|
import java.security.cert.CertPathValidatorException; |
|
import java.security.cert.CertPathValidatorException.BasicReason; |
|
import java.security.cert.CRLReason; |
|
import java.security.cert.Extension; |
|
import java.security.cert.TrustAnchor; |
|
import java.security.cert.X509Certificate; |
|
import java.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.Date; |
|
import java.util.List; |
|
import java.util.Map; |
|
|
|
import sun.security.action.GetIntegerAction; |
|
import sun.security.util.Debug; |
|
import sun.security.validator.Validator; |
|
import sun.security.x509.AccessDescription; |
|
import sun.security.x509.AuthorityInfoAccessExtension; |
|
import sun.security.x509.GeneralName; |
|
import sun.security.x509.GeneralNameInterface; |
|
import sun.security.x509.PKIXExtensions; |
|
import sun.security.x509.URIName; |
|
import sun.security.x509.X509CertImpl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class OCSP { |
|
|
|
private static final Debug debug = Debug.getInstance("certpath"); |
|
|
|
private static final int DEFAULT_CONNECT_TIMEOUT = 15000; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int CONNECT_TIMEOUT = initializeTimeout(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static int initializeTimeout() { |
|
Integer tmp = java.security.AccessController.doPrivileged( |
|
new GetIntegerAction("com.sun.security.ocsp.timeout")); |
|
if (tmp == null || tmp < 0) { |
|
return DEFAULT_CONNECT_TIMEOUT; |
|
} |
|
// Convert to milliseconds, as the system property will be |
|
|
|
return tmp * 1000; |
|
} |
|
|
|
private OCSP() {} |
|
|
|
|
|
/** |
|
* Obtains the revocation status of a certificate using OCSP. |
|
* |
|
* @param cert the certificate to be checked |
|
* @param issuerCert the issuer certificate |
|
* @param responderURI the URI of the OCSP responder |
|
* @param responderCert the OCSP responder's certificate |
|
* @param date the time the validity of the OCSP responder's certificate |
|
* should be checked against. If null, the current time is used. |
|
* @return the RevocationStatus |
|
* @throws IOException if there is an exception connecting to or |
|
* communicating with the OCSP responder |
|
* @throws CertPathValidatorException if an exception occurs while |
|
* encoding the OCSP Request or validating the OCSP Response |
|
*/ |
|
|
|
|
|
public static RevocationStatus check(X509Certificate cert, |
|
X509Certificate issuerCert, |
|
URI responderURI, |
|
X509Certificate responderCert, |
|
Date date) |
|
throws IOException, CertPathValidatorException |
|
{ |
|
return check(cert, issuerCert, responderURI, responderCert, date, |
|
Collections.<Extension>emptyList(), Validator.VAR_GENERIC); |
|
} |
|
|
|
|
|
public static RevocationStatus check(X509Certificate cert, |
|
X509Certificate issuerCert, URI responderURI, |
|
X509Certificate responderCert, Date date, List<Extension> extensions, |
|
String variant) |
|
throws IOException, CertPathValidatorException |
|
{ |
|
return check(cert, responderURI, null, issuerCert, responderCert, date, |
|
extensions, variant); |
|
} |
|
|
|
public static RevocationStatus check(X509Certificate cert, |
|
URI responderURI, TrustAnchor anchor, X509Certificate issuerCert, |
|
X509Certificate responderCert, Date date, |
|
List<Extension> extensions, String variant) |
|
throws IOException, CertPathValidatorException |
|
{ |
|
CertId certId; |
|
try { |
|
X509CertImpl certImpl = X509CertImpl.toImpl(cert); |
|
certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); |
|
} catch (CertificateException | IOException e) { |
|
throw new CertPathValidatorException |
|
("Exception while encoding OCSPRequest", e); |
|
} |
|
OCSPResponse ocspResponse = check(Collections.singletonList(certId), |
|
responderURI, new OCSPResponse.IssuerInfo(anchor, issuerCert), |
|
responderCert, date, extensions, variant); |
|
return (RevocationStatus) ocspResponse.getSingleResponse(certId); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static OCSPResponse check(List<CertId> certIds, URI responderURI, |
|
OCSPResponse.IssuerInfo issuerInfo, |
|
X509Certificate responderCert, Date date, |
|
List<Extension> extensions, String variant) |
|
throws IOException, CertPathValidatorException |
|
{ |
|
byte[] nonce = null; |
|
for (Extension ext : extensions) { |
|
if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { |
|
nonce = ext.getValue(); |
|
} |
|
} |
|
|
|
OCSPResponse ocspResponse = null; |
|
try { |
|
byte[] response = getOCSPBytes(certIds, responderURI, extensions); |
|
ocspResponse = new OCSPResponse(response); |
|
|
|
|
|
ocspResponse.verify(certIds, issuerInfo, responderCert, date, |
|
nonce, variant); |
|
} catch (IOException ioe) { |
|
throw new CertPathValidatorException( |
|
"Unable to determine revocation status due to network error", |
|
ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); |
|
} |
|
|
|
return ocspResponse; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static byte[] getOCSPBytes(List<CertId> certIds, URI responderURI, |
|
List<Extension> extensions) throws IOException { |
|
OCSPRequest request = new OCSPRequest(certIds, extensions); |
|
byte[] bytes = request.encodeBytes(); |
|
|
|
InputStream in = null; |
|
OutputStream out = null; |
|
byte[] response = null; |
|
|
|
try { |
|
URL url = responderURI.toURL(); |
|
if (debug != null) { |
|
debug.println("connecting to OCSP service at: " + url); |
|
} |
|
HttpURLConnection con = (HttpURLConnection)url.openConnection(); |
|
con.setConnectTimeout(CONNECT_TIMEOUT); |
|
con.setReadTimeout(CONNECT_TIMEOUT); |
|
con.setDoOutput(true); |
|
con.setDoInput(true); |
|
con.setRequestMethod("POST"); |
|
con.setRequestProperty |
|
("Content-type", "application/ocsp-request"); |
|
con.setRequestProperty |
|
("Content-length", String.valueOf(bytes.length)); |
|
out = con.getOutputStream(); |
|
out.write(bytes); |
|
out.flush(); |
|
|
|
if (debug != null && |
|
con.getResponseCode() != HttpURLConnection.HTTP_OK) { |
|
debug.println("Received HTTP error: " + con.getResponseCode() |
|
+ " - " + con.getResponseMessage()); |
|
} |
|
in = con.getInputStream(); |
|
int contentLength = con.getContentLength(); |
|
if (contentLength == -1) { |
|
contentLength = Integer.MAX_VALUE; |
|
} |
|
response = new byte[contentLength > 2048 ? 2048 : contentLength]; |
|
int total = 0; |
|
while (total < contentLength) { |
|
int count = in.read(response, total, response.length - total); |
|
if (count < 0) |
|
break; |
|
|
|
total += count; |
|
if (total >= response.length && total < contentLength) { |
|
response = Arrays.copyOf(response, total * 2); |
|
} |
|
} |
|
response = Arrays.copyOf(response, total); |
|
} finally { |
|
if (in != null) { |
|
try { |
|
in.close(); |
|
} catch (IOException ioe) { |
|
throw ioe; |
|
} |
|
} |
|
if (out != null) { |
|
try { |
|
out.close(); |
|
} catch (IOException ioe) { |
|
throw ioe; |
|
} |
|
} |
|
} |
|
return response; |
|
} |
|
|
|
/** |
|
* Returns the URI of the OCSP Responder as specified in the |
|
* certificate's Authority Information Access extension, or null if |
|
* not specified. |
|
* |
|
* @param cert the certificate |
|
* @return the URI of the OCSP Responder, or null if not specified |
|
*/ |
|
|
|
public static URI getResponderURI(X509Certificate cert) { |
|
try { |
|
return getResponderURI(X509CertImpl.toImpl(cert)); |
|
} catch (CertificateException ce) { |
|
|
|
return null; |
|
} |
|
} |
|
|
|
static URI getResponderURI(X509CertImpl certImpl) { |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static interface RevocationStatus { |
|
public enum CertStatus { GOOD, REVOKED, UNKNOWN }; |
|
|
|
|
|
|
|
*/ |
|
CertStatus getCertStatus(); |
|
|
|
|
|
|
|
*/ |
|
Date getRevocationTime(); |
|
|
|
|
|
|
|
*/ |
|
CRLReason getRevocationReason(); |
|
|
|
|
|
|
|
*/ |
|
Map<String, Extension> getSingleExtensions(); |
|
} |
|
} |