< prev index next >
src/share/classes/sun/security/provider/certpath/OCSPResponse.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 39,48 ****
--- 39,49 ----
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+ import java.util.Set;
import javax.security.auth.x500.X500Principal;
import sun.misc.HexDumpEncoder;
import sun.security.action.GetIntegerAction;
import sun.security.x509.*;
*** 127,137 ****
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 boolean dump = debug != null && Debug.isOn("ocsp");
private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
--- 128,138 ----
TRY_LATER, // Try again later
UNUSED, // is not used
SIG_REQUIRED, // Must sign the request
UNAUTHORIZED // Request unauthorized
};
! private static final ResponseStatus[] rsvalues = ResponseStatus.values();
private static final Debug debug = Debug.getInstance("certpath");
private static final boolean dump = debug != null && Debug.isOn("ocsp");
private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID =
ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1});
*** 171,197 ****
// specified in seconds
return tmp * 1000;
}
// an array of all of the CRLReasons (used in SingleResponse)
! private static CRLReason[] values = CRLReason.values();
private final ResponseStatus responseStatus;
private final Map<CertId, SingleResponse> singleResponseMap;
private final AlgorithmId sigAlgId;
private final byte[] signature;
private final byte[] tbsResponseData;
private final byte[] responseNonce;
private List<X509CertImpl> certs;
private X509CertImpl signerCert = null;
! private X500Principal responderName = null;
! private KeyIdentifier responderKeyId = null;
/*
* Create an OCSP response from its ASN.1 DER encoding.
*/
! OCSPResponse(byte[] bytes) throws IOException {
if (dump) {
HexDumpEncoder hexEnc = new HexDumpEncoder();
debug.println("OCSPResponse bytes...\n\n" +
hexEnc.encode(bytes) + "\n");
}
--- 172,201 ----
// specified in seconds
return tmp * 1000;
}
// an array of all of the CRLReasons (used in SingleResponse)
! private static final CRLReason[] values = CRLReason.values();
private final ResponseStatus responseStatus;
private final Map<CertId, SingleResponse> singleResponseMap;
private final AlgorithmId sigAlgId;
private final byte[] signature;
private final byte[] tbsResponseData;
private final byte[] responseNonce;
private List<X509CertImpl> certs;
private X509CertImpl signerCert = null;
! private final ResponderId respId;
! private Date producedAtDate = null;
! private final Map<String, java.security.cert.Extension> responseExtensions;
/*
* Create an OCSP response from its ASN.1 DER encoding.
+ *
+ * @param bytes The DER-encoded bytes for an OCSP response
*/
! public OCSPResponse(byte[] bytes) throws IOException {
if (dump) {
HexDumpEncoder hexEnc = new HexDumpEncoder();
debug.println("OCSPResponse bytes...\n\n" +
hexEnc.encode(bytes) + "\n");
}
*** 219,228 ****
--- 223,234 ----
certs = new ArrayList<X509CertImpl>();
sigAlgId = null;
signature = null;
tbsResponseData = null;
responseNonce = null;
+ responseExtensions = Collections.emptyMap();
+ respId = null;
return;
}
// responseBytes
der = derIn.getDerValue();
*** 287,358 ****
seq = seqDerIn.getDerValue();
}
}
// responderID
! short tag = (byte)(seq.tag & 0x1f);
! if (tag == NAME_TAG) {
! responderName = new X500Principal(seq.getData().toByteArray());
! if (debug != null) {
! debug.println("Responder's name: " + responderName);
! }
! } else if (tag == KEY_TAG) {
! responderKeyId = new KeyIdentifier(seq.getData().getOctetString());
if (debug != null) {
! debug.println("Responder's key ID: " +
! Debug.toString(responderKeyId.getIdentifier()));
! }
! } else {
! throw new IOException("Bad encoding in responderID element of " +
! "OCSP response: expected ASN.1 context specific tag 0 or 1");
}
// producedAt
seq = seqDerIn.getDerValue();
if (debug != null) {
- Date producedAtDate = seq.getGeneralizedTime();
debug.println("OCSP response produced at: " + producedAtDate);
}
// responses
DerValue[] singleResponseDer = seqDerIn.getSequence(1);
singleResponseMap = new HashMap<>(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
! byte[] nonce = null;
if (seqDerIn.available() > 0) {
seq = seqDerIn.getDerValue();
if (seq.isContextSpecific((byte)1)) {
! DerValue[] responseExtDer = seq.data.getSequence(3);
! for (int i = 0; i < responseExtDer.length; i++) {
! Extension ext = new Extension(responseExtDer[i]);
! if (debug != null) {
! debug.println("OCSP extension: " + ext);
! }
! // Only the NONCE extension is recognized
! if (ext.getExtensionId().equals((Object)
! OCSP.NONCE_EXTENSION_OID))
! {
! nonce = ext.getExtensionValue();
! } else if (ext.isCritical()) {
! throw new IOException(
! "Unsupported OCSP critical extension: " +
! ext.getExtensionId());
! }
}
}
}
- responseNonce = nonce;
// signatureAlgorithmId
sigAlgId = AlgorithmId.parse(seqTmp[1]);
// signature
--- 293,344 ----
seq = seqDerIn.getDerValue();
}
}
// responderID
! respId = new ResponderId(seq.toByteArray());
if (debug != null) {
! debug.println("Responder ID: " + respId);
}
// producedAt
seq = seqDerIn.getDerValue();
+ producedAtDate = seq.getGeneralizedTime();
if (debug != null) {
debug.println("OCSP response produced at: " + producedAtDate);
}
// responses
DerValue[] singleResponseDer = seqDerIn.getSequence(1);
singleResponseMap = new HashMap<>(singleResponseDer.length);
if (debug != null) {
debug.println("OCSP number of SingleResponses: "
+ singleResponseDer.length);
}
! for (DerValue srDer : singleResponseDer) {
! SingleResponse singleResponse = new SingleResponse(srDer);
singleResponseMap.put(singleResponse.getCertId(), singleResponse);
}
// responseExtensions
! Map<String, java.security.cert.Extension> tmpExtMap = new HashMap<>();
if (seqDerIn.available() > 0) {
seq = seqDerIn.getDerValue();
if (seq.isContextSpecific((byte)1)) {
! tmpExtMap = parseExtensions(seq);
}
}
+ responseExtensions = tmpExtMap;
+
+ // Attach the nonce value if found in the extension map
+ Extension nonceExt = (Extension)tmpExtMap.get(
+ PKIXExtensions.OCSPNonce_Id.toString());
+ responseNonce = (nonceExt != null) ?
+ nonceExt.getExtensionValue() : null;
+ if (debug != null && responseNonce != null) {
+ debug.println("Response nonce: " + Arrays.toString(responseNonce));
}
// signatureAlgorithmId
sigAlgId = AlgorithmId.parse(seqTmp[1]);
// signature
*** 385,396 ****
} else {
certs = new ArrayList<X509CertImpl>();
}
}
! void verify(List<CertId> certIds, X509Certificate issuerCert,
! X509Certificate responderCert, Date date, byte[] nonce)
throws CertPathValidatorException
{
switch (responseStatus) {
case SUCCESSFUL:
break;
--- 371,383 ----
} else {
certs = new ArrayList<X509CertImpl>();
}
}
! void verify(List<CertId> certIds, IssuerInfo issuerInfo,
! X509Certificate responderCert, Date date, byte[] nonce,
! String variant)
throws CertPathValidatorException
{
switch (responseStatus) {
case SUCCESSFUL:
break;
*** 426,458 ****
// Locate the signer cert
if (signerCert == null) {
// Add the Issuing CA cert and/or Trusted Responder cert to the list
// of certs from the OCSP response
try {
! certs.add(X509CertImpl.toImpl(issuerCert));
if (responderCert != null) {
certs.add(X509CertImpl.toImpl(responderCert));
}
} catch (CertificateException ce) {
throw new CertPathValidatorException(
"Invalid issuer or trusted responder certificate", ce);
}
! if (responderName != null) {
for (X509CertImpl cert : certs) {
! if (cert.getSubjectX500Principal().equals(responderName)) {
signerCert = cert;
break;
}
}
! } else if (responderKeyId != null) {
for (X509CertImpl cert : certs) {
// Match responder's key identifier against the cert's SKID
// This will match if the SKID is encoded using the 160-bit
// SHA-1 hash method as defined in RFC 5280.
KeyIdentifier certKeyId = cert.getSubjectKeyId();
! if (certKeyId != null && responderKeyId.equals(certKeyId)) {
signerCert = cert;
break;
} else {
// The certificate does not have a SKID or may have
// been using a different algorithm (ex: see RFC 7093).
--- 413,449 ----
// Locate the signer cert
if (signerCert == null) {
// Add the Issuing CA cert and/or Trusted Responder cert to the list
// of certs from the OCSP response
try {
! if (issuerInfo.getCertificate() != null) {
! certs.add(X509CertImpl.toImpl(issuerInfo.getCertificate()));
! }
if (responderCert != null) {
certs.add(X509CertImpl.toImpl(responderCert));
}
} catch (CertificateException ce) {
throw new CertPathValidatorException(
"Invalid issuer or trusted responder certificate", ce);
}
! if (respId.getType() == ResponderId.Type.BY_NAME) {
! X500Principal rName = respId.getResponderName();
for (X509CertImpl cert : certs) {
! if (cert.getSubjectX500Principal().equals(rName)) {
signerCert = cert;
break;
}
}
! } else if (respId.getType() == ResponderId.Type.BY_KEY) {
! KeyIdentifier ridKeyId = respId.getKeyIdentifier();
for (X509CertImpl cert : certs) {
// Match responder's key identifier against the cert's SKID
// This will match if the SKID is encoded using the 160-bit
// SHA-1 hash method as defined in RFC 5280.
KeyIdentifier certKeyId = cert.getSubjectKeyId();
! if (certKeyId != null && ridKeyId.equals(certKeyId)) {
signerCert = cert;
break;
} else {
// The certificate does not have a SKID or may have
// been using a different algorithm (ex: see RFC 7093).
*** 462,472 ****
try {
certKeyId = new KeyIdentifier(cert.getPublicKey());
} catch (IOException e) {
// ignore
}
! if (responderKeyId.equals(certKeyId)) {
signerCert = cert;
break;
}
}
}
--- 453,463 ----
try {
certKeyId = new KeyIdentifier(cert.getPublicKey());
} catch (IOException e) {
// ignore
}
! if (ridKeyId.equals(certKeyId)) {
signerCert = cert;
break;
}
}
}
*** 474,484 ****
}
// Check whether the signer cert returned by the responder is trusted
if (signerCert != null) {
// Check if the response is signed by the issuing CA
! if (signerCert.equals(issuerCert)) {
if (debug != null) {
debug.println("OCSP response is signed by the target's " +
"Issuing CA");
}
// cert is trusted, now verify the signed response
--- 465,478 ----
}
// Check whether the signer cert returned by the responder is trusted
if (signerCert != null) {
// Check if the response is signed by the issuing CA
! if (signerCert.getSubjectX500Principal().equals(
! issuerInfo.getName()) &&
! signerCert.getPublicKey().equals(
! issuerInfo.getPublicKey())) {
if (debug != null) {
debug.println("OCSP response is signed by the target's " +
"Issuing CA");
}
// cert is trusted, now verify the signed response
*** 491,501 ****
}
// cert is trusted, now verify the signed response
// Check if the response is signed by an authorized responder
} else if (signerCert.getIssuerX500Principal().equals(
! issuerCert.getSubjectX500Principal())) {
// Check for the OCSPSigning key purpose
try {
List<String> keyPurposes = signerCert.getExtendedKeyUsage();
if (keyPurposes == null ||
--- 485,495 ----
}
// cert is trusted, now verify the signed response
// Check if the response is signed by an authorized responder
} else if (signerCert.getIssuerX500Principal().equals(
! issuerInfo.getName())) {
// Check for the OCSPSigning key purpose
try {
List<String> keyPurposes = signerCert.getExtendedKeyUsage();
if (keyPurposes == null ||
*** 511,522 ****
"OCSP responses", cpe);
}
// Check algorithm constraints specified in security property
// "jdk.certpath.disabledAlgorithms".
! AlgorithmChecker algChecker = new AlgorithmChecker(
! new TrustAnchor(issuerCert, null));
algChecker.init(false);
algChecker.check(signerCert, Collections.<String>emptySet());
// check the validity
try {
--- 505,517 ----
"OCSP responses", cpe);
}
// Check algorithm constraints specified in security property
// "jdk.certpath.disabledAlgorithms".
! AlgorithmChecker algChecker =
! new AlgorithmChecker(issuerInfo.getAnchor(), date,
! variant);
algChecker.init(false);
algChecker.check(signerCert, Collections.<String>emptySet());
// check the validity
try {
*** 550,560 ****
// authorized responder in a future update.
}
// verify the signature
try {
! signerCert.verify(issuerCert.getPublicKey());
if (debug != null) {
debug.println("OCSP response is signed by an " +
"Authorized Responder");
}
// cert is trusted, now verify the signed response
--- 545,555 ----
// authorized responder in a future update.
}
// verify the signature
try {
! signerCert.verify(issuerInfo.getPublicKey());
if (debug != null) {
debug.println("OCSP response is signed by an " +
"Authorized Responder");
}
// cert is trusted, now verify the signed response
*** 572,582 ****
// Confirm that the signed response was generated using the public
// key from the trusted responder cert
if (signerCert != null) {
// Check algorithm constraints specified in security property
// "jdk.certpath.disabledAlgorithms".
! AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId);
if (!verifySignature(signerCert)) {
throw new CertPathValidatorException(
"Error verifying OCSP Response's signature");
}
--- 567,577 ----
// Confirm that the signed response was generated using the public
// key from the trusted responder cert
if (signerCert != null) {
// Check algorithm constraints specified in security property
// "jdk.certpath.disabledAlgorithms".
! AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId, variant);
if (!verifySignature(signerCert)) {
throw new CertPathValidatorException(
"Error verifying OCSP Response's signature");
}
*** 591,601 ****
throw new CertPathValidatorException("Nonces don't match");
}
}
// Check freshness of OCSPResponse
-
long now = (date == null) ? System.currentTimeMillis() : date.getTime();
Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW);
Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW);
for (SingleResponse sr : singleResponseMap.values()) {
if (debug != null) {
--- 586,595 ----
*** 623,634 ****
}
}
/**
* Returns the OCSP ResponseStatus.
*/
! ResponseStatus getResponseStatus() {
return responseStatus;
}
/*
* Verify the signature of the OCSP response.
--- 617,630 ----
}
}
/**
* Returns the OCSP ResponseStatus.
+ *
+ * @return the {@code ResponseStatus} for this OCSP response
*/
! public ResponseStatus getResponseStatus() {
return responseStatus;
}
/*
* Verify the signature of the OCSP response.
*** 662,687 ****
}
/**
* Returns the SingleResponse of the specified CertId, or null if
* there is no response for that CertId.
*/
! SingleResponse getSingleResponse(CertId certId) {
return singleResponseMap.get(certId);
}
/*
* Returns the certificate for the authority that signed the OCSP response.
*/
X509Certificate getSignerCertificate() {
return signerCert; // set in verify()
}
/*
* A class representing a single OCSP response.
*/
! final static class SingleResponse implements OCSP.RevocationStatus {
private final CertId certId;
private final CertStatus certStatus;
private final Date thisUpdate;
private final Date nextUpdate;
private final Date revocationTime;
--- 658,775 ----
}
/**
* Returns the SingleResponse of the specified CertId, or null if
* there is no response for that CertId.
+ *
+ * @param certId the {@code CertId} for a {@code SingleResponse} to be
+ * searched for in the OCSP response.
+ *
+ * @return the {@code SingleResponse} for the provided {@code CertId},
+ * or {@code null} if it is not found.
*/
! public SingleResponse getSingleResponse(CertId certId) {
return singleResponseMap.get(certId);
}
+ /**
+ * Return a set of all CertIds in this {@code OCSPResponse}
+ *
+ * @return an unmodifiable set containing every {@code CertId} in this
+ * response.
+ */
+ public Set<CertId> getCertIds() {
+ return Collections.unmodifiableSet(singleResponseMap.keySet());
+ }
+
/*
* Returns the certificate for the authority that signed the OCSP response.
*/
X509Certificate getSignerCertificate() {
return signerCert; // set in verify()
}
+ /**
+ * Get the {@code ResponderId} from this {@code OCSPResponse}
+ *
+ * @return the {@code ResponderId} from this response or {@code null}
+ * if no responder ID is in the body of the response (e.g. a
+ * response with a status other than SUCCESS.
+ */
+ public ResponderId getResponderId() {
+ return respId;
+ }
+
+ /**
+ * Provide a String representation of an OCSPResponse
+ *
+ * @return a human-readable representation of the OCSPResponse
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("OCSP Response:\n");
+ sb.append("Response Status: ").append(responseStatus).append("\n");
+ sb.append("Responder ID: ").append(respId).append("\n");
+ sb.append("Produced at: ").append(producedAtDate).append("\n");
+ int count = singleResponseMap.size();
+ sb.append(count).append(count == 1 ?
+ " response:\n" : " responses:\n");
+ for (SingleResponse sr : singleResponseMap.values()) {
+ sb.append(sr).append("\n");
+ }
+ if (responseExtensions != null && responseExtensions.size() > 0) {
+ count = responseExtensions.size();
+ sb.append(count).append(count == 1 ?
+ " extension:\n" : " extensions:\n");
+ for (String extId : responseExtensions.keySet()) {
+ sb.append(responseExtensions.get(extId)).append("\n");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Build a String-Extension map from DER encoded data.
+ * @param derVal A {@code DerValue} object built from a SEQUENCE of
+ * extensions
+ *
+ * @return a {@code Map} using the OID in string form as the keys. If no
+ * extensions are found or an empty SEQUENCE is passed in, then
+ * an empty {@code Map} will be returned.
+ *
+ * @throws IOException if any decoding errors occur.
+ */
+ private static Map<String, java.security.cert.Extension>
+ parseExtensions(DerValue derVal) throws IOException {
+ DerValue[] extDer = derVal.data.getSequence(3);
+ Map<String, java.security.cert.Extension> extMap =
+ new HashMap<>(extDer.length);
+
+ for (DerValue extDerVal : extDer) {
+ Extension ext = new Extension(extDerVal);
+ if (debug != null) {
+ debug.println("Extension: " + ext);
+ }
+ // We don't support any extensions yet. Therefore, if it
+ // is critical we must throw an exception because we
+ // don't know how to process it.
+ if (ext.isCritical()) {
+ throw new IOException("Unsupported OCSP critical extension: " +
+ ext.getExtensionId());
+ }
+ extMap.put(ext.getId(), ext);
+ }
+
+ return extMap;
+ }
+
/*
* A class representing a single OCSP response.
*/
! public static final class SingleResponse implements OCSP.RevocationStatus {
private final CertId certId;
private final CertStatus certStatus;
private final Date thisUpdate;
private final Date nextUpdate;
private final Date revocationTime;
*** 722,833 ****
debug.println("Revocation time: " + revocationTime);
debug.println("Revocation reason: " + revocationReason);
}
} else {
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();
! if (tmp.available() == 0) {
! // we are done
! nextUpdate = null;
! } else {
! derVal = tmp.getDerValue();
! tag = (byte)(derVal.tag & 0x1f);
! if (tag == 0) {
! // next update
! nextUpdate = derVal.data.getGeneralizedTime();
! if (tmp.available() == 0) {
! // we are done
! } else {
! derVal = tmp.getDerValue();
! tag = (byte)(derVal.tag & 0x1f);
! }
! } else {
! nextUpdate = null;
! }
! }
! // singleExtensions
if (tmp.available() > 0) {
derVal = tmp.getDerValue();
! if (derVal.isContextSpecific((byte)1)) {
! DerValue[] singleExtDer = derVal.data.getSequence(3);
! singleExtensions =
! new HashMap<String, java.security.cert.Extension>
! (singleExtDer.length);
! for (int i = 0; i < singleExtDer.length; i++) {
! Extension ext = new Extension(singleExtDer[i]);
if (debug != null) {
! debug.println("OCSP single extension: " + ext);
}
! // We don't support any extensions yet. Therefore, if it
! // is critical we must throw an exception because we
! // don't know how to process it.
! if (ext.isCritical()) {
! throw new IOException(
! "Unsupported OCSP critical extension: " +
! ext.getExtensionId());
}
! singleExtensions.put(ext.getId(), ext);
}
} else {
! singleExtensions = Collections.emptyMap();
}
- } else {
- singleExtensions = Collections.emptyMap();
}
}
/*
* Return the certificate's revocation status code
*/
! @Override public CertStatus getCertStatus() {
return certStatus;
}
! private CertId getCertId() {
return certId;
}
! @Override public Date getRevocationTime() {
! return (Date) revocationTime.clone();
}
! @Override public CRLReason getRevocationReason() {
return revocationReason;
}
@Override
public Map<String, java.security.cert.Extension> getSingleExtensions() {
return Collections.unmodifiableMap(singleExtensions);
}
/**
* Construct a string representation of a single OCSP response.
*/
@Override public String toString() {
StringBuilder sb = new StringBuilder();
! sb.append("SingleResponse: \n");
sb.append(certId);
! sb.append("\nCertStatus: "+ certStatus + "\n");
if (certStatus == CertStatus.REVOKED) {
! sb.append("revocationTime is " + revocationTime + "\n");
! sb.append("revocationReason is " + revocationReason + "\n");
}
! sb.append("thisUpdate is " + thisUpdate + "\n");
if (nextUpdate != null) {
! sb.append("nextUpdate is " + nextUpdate + "\n");
}
return sb.toString();
}
}
}
--- 810,1074 ----
debug.println("Revocation time: " + revocationTime);
debug.println("Revocation reason: " + revocationReason);
}
} else {
revocationTime = null;
! revocationReason = null;
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();
+ if (debug != null) {
+ debug.println("thisUpdate: " + thisUpdate);
+ }
! // Parse optional fields like nextUpdate and singleExtensions
! Date tmpNextUpdate = null;
! Map<String, java.security.cert.Extension> tmpMap = null;
! // Check for the first optional item, it could be nextUpdate
! // [CONTEXT 0] or singleExtensions [CONTEXT 1]
if (tmp.available() > 0) {
derVal = tmp.getDerValue();
!
! // nextUpdate processing
! if (derVal.isContextSpecific((byte)0)) {
! tmpNextUpdate = derVal.data.getGeneralizedTime();
if (debug != null) {
! debug.println("nextUpdate: " + tmpNextUpdate);
}
!
! // If more data exists in the singleResponse, it
! // can only be singleExtensions. Get this DER value
! // for processing in the next block
! derVal = tmp.available() > 0 ? tmp.getDerValue() : null;
}
!
! // singleExtensions processing
! if (derVal != null) {
! if (derVal.isContextSpecific((byte)1)) {
! tmpMap = parseExtensions(derVal);
!
! // There should not be any other items in the
! // singleResponse at this point.
! if (tmp.available() > 0) {
! throw new IOException(tmp.available() +
! " bytes of additional data in singleResponse");
}
} else {
! // Unknown item in the singleResponse
! throw new IOException("Unsupported singleResponse " +
! "item, tag = " + String.format("%02X", derVal.tag));
! }
! }
! }
!
! nextUpdate = tmpNextUpdate;
! singleExtensions = (tmpMap != null) ? tmpMap :
! Collections.emptyMap();
! if (debug != null) {
! for (java.security.cert.Extension ext :
! singleExtensions.values()) {
! debug.println("singleExtension: " + ext);
}
}
}
/*
* Return the certificate's revocation status code
*/
! @Override
! public CertStatus getCertStatus() {
return certStatus;
}
! /**
! * Get the Cert ID that this SingleResponse is for.
! *
! * @return the {@code CertId} for this {@code SingleResponse}
! */
! public CertId getCertId() {
return certId;
}
! /**
! * Get the {@code thisUpdate} field from this {@code SingleResponse}.
! *
! * @return a {@link Date} object containing the thisUpdate date
! */
! public Date getThisUpdate() {
! return (thisUpdate != null ? (Date) thisUpdate.clone() : null);
! }
!
! /**
! * Get the {@code nextUpdate} field from this {@code SingleResponse}.
! *
! * @return a {@link Date} object containing the nexUpdate date or
! * {@code null} if a nextUpdate field is not present in the response.
! */
! public Date getNextUpdate() {
! return (nextUpdate != null ? (Date) nextUpdate.clone() : null);
! }
!
! /**
! * Get the {@code revocationTime} field from this
! * {@code SingleResponse}.
! *
! * @return a {@link Date} object containing the revocationTime date or
! * {@code null} if the {@code SingleResponse} does not have a status
! * of {@code REVOKED}.
! */
! @Override
! public Date getRevocationTime() {
! return (revocationTime != null ? (Date) revocationTime.clone() :
! null);
}
! /**
! * Get the {@code revocationReason} field for the
! * {@code SingleResponse}.
! *
! * @return a {@link CRLReason} containing the revocation reason, or
! * {@code null} if a revocation reason was not provided or the
! * response status is not {@code REVOKED}.
! */
! @Override
! public CRLReason getRevocationReason() {
return revocationReason;
}
+ /**
+ * Get the {@code singleExtensions} for this {@code SingleResponse}.
+ *
+ * @return a {@link Map} of {@link Extension} objects, keyed by
+ * their OID value in string form.
+ */
@Override
public Map<String, java.security.cert.Extension> getSingleExtensions() {
return Collections.unmodifiableMap(singleExtensions);
}
/**
* Construct a string representation of a single OCSP response.
*/
@Override public String toString() {
StringBuilder sb = new StringBuilder();
! sb.append("SingleResponse:\n");
sb.append(certId);
! sb.append("\nCertStatus: ").append(certStatus).append("\n");
if (certStatus == CertStatus.REVOKED) {
! sb.append("revocationTime is ");
! sb.append(revocationTime).append("\n");
! sb.append("revocationReason is ");
! sb.append(revocationReason).append("\n");
}
! sb.append("thisUpdate is ").append(thisUpdate).append("\n");
if (nextUpdate != null) {
! sb.append("nextUpdate is ").append(nextUpdate).append("\n");
}
+ for (java.security.cert.Extension ext : singleExtensions.values()) {
+ sb.append("singleExtension: ");
+ sb.append(ext.toString()).append("\n");
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Helper class that allows consumers to pass in issuer information. This
+ * will always consist of the issuer's name and public key, but may also
+ * contain a certificate if the originating data is in that form. The
+ * trust anchor for the certificate chain will be included for certpath
+ * disabled algorithm checking.
+ */
+ static final class IssuerInfo {
+ private final TrustAnchor anchor;
+ private final X509Certificate certificate;
+ private final X500Principal name;
+ private final PublicKey pubKey;
+
+ IssuerInfo(TrustAnchor anchor) {
+ this(anchor, (anchor != null) ? anchor.getTrustedCert() : null);
+ }
+
+ IssuerInfo(X509Certificate issuerCert) {
+ this(null, issuerCert);
+ }
+
+ IssuerInfo(TrustAnchor anchor, X509Certificate issuerCert) {
+ if (anchor == null && issuerCert == null) {
+ throw new NullPointerException("TrustAnchor and issuerCert " +
+ "cannot be null");
+ }
+ this.anchor = anchor;
+ if (issuerCert != null) {
+ name = issuerCert.getSubjectX500Principal();
+ pubKey = issuerCert.getPublicKey();
+ certificate = issuerCert;
+ } else {
+ name = anchor.getCA();
+ pubKey = anchor.getCAPublicKey();
+ certificate = anchor.getTrustedCert();
+ }
+ }
+
+ /**
+ * Get the certificate in this IssuerInfo if present.
+ *
+ * @return the {@code X509Certificate} used to create this IssuerInfo
+ * object, or {@code null} if a certificate was not used in its
+ * creation.
+ */
+ X509Certificate getCertificate() {
+ return certificate;
+ }
+
+ /**
+ * Get the name of this issuer.
+ *
+ * @return an {@code X500Principal} corresponding to this issuer's
+ * name. If derived from an issuer's {@code X509Certificate} this
+ * would be equivalent to the certificate subject name.
+ */
+ X500Principal getName() {
+ return name;
+ }
+
+ /**
+ * Get the public key for this issuer.
+ *
+ * @return a {@code PublicKey} for this issuer.
+ */
+ PublicKey getPublicKey() {
+ return pubKey;
+ }
+
+ /**
+ * Get the TrustAnchor for the certificate chain.
+ *
+ * @return a {@code TrustAnchor}.
+ */
+ TrustAnchor getAnchor() {
+ return anchor;
+ }
+
+ /**
+ * Create a string representation of this IssuerInfo.
+ *
+ * @return a {@code String} form of this IssuerInfo object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Issuer Info:\n");
+ sb.append("Name: ").append(name.toString()).append("\n");
+ sb.append("Public Key:\n").append(pubKey.toString()).append("\n");
return sb.toString();
}
}
}
< prev index next >