diff options
Diffstat (limited to 'bcpkix/src/main/java/org/bouncycastle/cert/ocsp')
23 files changed, 2817 insertions, 0 deletions
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPResp.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPResp.java new file mode 100644 index 00000000..82b9f232 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPResp.java @@ -0,0 +1,212 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ocsp.BasicOCSPResponse; +import org.bouncycastle.asn1.ocsp.ResponseData; +import org.bouncycastle.asn1.ocsp.SingleResponse; +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.ContentVerifier; +import org.bouncycastle.operator.ContentVerifierProvider; + +/** + * <pre> + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + * </pre> + */ +public class BasicOCSPResp +{ + private BasicOCSPResponse resp; + private ResponseData data; + private Extensions extensions; + + public BasicOCSPResp( + BasicOCSPResponse resp) + { + this.resp = resp; + this.data = resp.getTbsResponseData(); + this.extensions = Extensions.getInstance(resp.getTbsResponseData().getResponseExtensions()); + } + + /** + * Return the DER encoding of the tbsResponseData field. + * @return DER encoding of tbsResponseData + */ + public byte[] getTBSResponseData() + { + try + { + return resp.getTbsResponseData().getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; + } + } + + public int getVersion() + { + return data.getVersion().getValue().intValue() + 1; + } + + public RespID getResponderId() + { + return new RespID(data.getResponderID()); + } + + public Date getProducedAt() + { + return OCSPUtils.extractDate(data.getProducedAt()); + } + + public SingleResp[] getResponses() + { + ASN1Sequence s = data.getResponses(); + SingleResp[] rs = new SingleResp[s.size()]; + + for (int i = 0; i != rs.length; i++) + { + rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i))); + } + + return rs; + } + + public boolean hasExtensions() + { + return extensions != null; + } + + public Extension getExtension(ASN1ObjectIdentifier oid) + { + if (extensions != null) + { + return extensions.getExtension(oid); + } + + return null; + } + + public List getExtensionOIDs() + { + return OCSPUtils.getExtensionOIDs(extensions); + } + + public Set getCriticalExtensionOIDs() + { + return OCSPUtils.getCriticalExtensionOIDs(extensions); + } + + public Set getNonCriticalExtensionOIDs() + { + return OCSPUtils.getNonCriticalExtensionOIDs(extensions); + } + + + public ASN1ObjectIdentifier getSignatureAlgOID() + { + return resp.getSignatureAlgorithm().getAlgorithm(); + } + + public byte[] getSignature() + { + return resp.getSignature().getBytes(); + } + + public X509CertificateHolder[] getCerts() + { + // + // load the certificates if we have any + // + if (resp.getCerts() != null) + { + ASN1Sequence s = resp.getCerts(); + + if (s != null) + { + X509CertificateHolder[] certs = new X509CertificateHolder[s.size()]; + + for (int i = 0; i != certs.length; i++) + { + certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i))); + } + + return certs; + } + + return OCSPUtils.EMPTY_CERTS; + } + else + { + return OCSPUtils.EMPTY_CERTS; + } + } + + /** + * verify the signature against the tbsResponseData object we contain. + */ + public boolean isSignatureValid( + ContentVerifierProvider verifierProvider) + throws OCSPException + { + try + { + ContentVerifier verifier = verifierProvider.get(resp.getSignatureAlgorithm()); + OutputStream vOut = verifier.getOutputStream(); + + vOut.write(resp.getTbsResponseData().getEncoded(ASN1Encoding.DER)); + vOut.close(); + + return verifier.verify(this.getSignature()); + } + catch (Exception e) + { + throw new OCSPException("exception processing sig: " + e, e); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] getEncoded() + throws IOException + { + return resp.getEncoded(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof BasicOCSPResp)) + { + return false; + } + + BasicOCSPResp r = (BasicOCSPResp)o; + + return resp.equals(r.resp); + } + + public int hashCode() + { + return resp.hashCode(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java new file mode 100644 index 00000000..a57e7d8f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java @@ -0,0 +1,264 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERGeneralizedTime; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.ocsp.BasicOCSPResponse; +import org.bouncycastle.asn1.ocsp.CertStatus; +import org.bouncycastle.asn1.ocsp.ResponseData; +import org.bouncycastle.asn1.ocsp.RevokedInfo; +import org.bouncycastle.asn1.ocsp.SingleResponse; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; + +/** + * Generator for basic OCSP response objects. + */ +public class BasicOCSPRespBuilder +{ + private List list = new ArrayList(); + private Extensions responseExtensions = null; + private RespID responderID; + + private class ResponseObject + { + CertificateID certId; + CertStatus certStatus; + DERGeneralizedTime thisUpdate; + DERGeneralizedTime nextUpdate; + Extensions extensions; + + public ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + Date thisUpdate, + Date nextUpdate, + Extensions extensions) + { + this.certId = certId; + + if (certStatus == null) + { + this.certStatus = new CertStatus(); + } + else if (certStatus instanceof UnknownStatus) + { + this.certStatus = new CertStatus(2, DERNull.INSTANCE); + } + else + { + RevokedStatus rs = (RevokedStatus)certStatus; + + if (rs.hasRevocationReason()) + { + this.certStatus = new CertStatus( + new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), CRLReason.lookup(rs.getRevocationReason()))); + } + else + { + this.certStatus = new CertStatus( + new RevokedInfo(new ASN1GeneralizedTime(rs.getRevocationTime()), null)); + } + } + + this.thisUpdate = new DERGeneralizedTime(thisUpdate); + + if (nextUpdate != null) + { + this.nextUpdate = new DERGeneralizedTime(nextUpdate); + } + else + { + this.nextUpdate = null; + } + + this.extensions = extensions; + } + + public SingleResponse toResponse() + throws Exception + { + return new SingleResponse(certId.toASN1Object(), certStatus, thisUpdate, nextUpdate, extensions); + } + } + + /** + * basic constructor + */ + public BasicOCSPRespBuilder( + RespID responderID) + { + this.responderID = responderID; + } + + /** + * construct with the responderID to be the SHA-1 keyHash of the passed in public key. + * + * @param key the key info of the responder public key. + * @param digCalc a SHA-1 digest calculator + */ + public BasicOCSPRespBuilder( + SubjectPublicKeyInfo key, + DigestCalculator digCalc) + throws OCSPException + { + this.responderID = new RespID(key, digCalc); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + */ + public BasicOCSPRespBuilder addResponse( + CertificateID certID, + CertificateStatus certStatus) + { + list.add(new ResponseObject(certID, certStatus, new Date(), null, null)); + + return this; + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public BasicOCSPRespBuilder addResponse( + CertificateID certID, + CertificateStatus certStatus, + Extensions singleExtensions) + { + list.add(new ResponseObject(certID, certStatus, new Date(), null, singleExtensions)); + + return this; + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public BasicOCSPRespBuilder addResponse( + CertificateID certID, + CertificateStatus certStatus, + Date nextUpdate, + Extensions singleExtensions) + { + list.add(new ResponseObject(certID, certStatus, new Date(), nextUpdate, singleExtensions)); + + return this; + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param thisUpdate date this response was valid on + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public BasicOCSPRespBuilder addResponse( + CertificateID certID, + CertificateStatus certStatus, + Date thisUpdate, + Date nextUpdate, + Extensions singleExtensions) + { + list.add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions)); + + return this; + } + + /** + * Set the extensions for the response. + * + * @param responseExtensions the extension object to carry. + */ + public BasicOCSPRespBuilder setResponseExtensions( + Extensions responseExtensions) + { + this.responseExtensions = responseExtensions; + + return this; + } + + public BasicOCSPResp build( + ContentSigner signer, + X509CertificateHolder[] chain, + Date producedAt) + throws OCSPException + { + Iterator it = list.iterator(); + + ASN1EncodableVector responses = new ASN1EncodableVector(); + + while (it.hasNext()) + { + try + { + responses.add(((ResponseObject)it.next()).toResponse()); + } + catch (Exception e) + { + throw new OCSPException("exception creating Request", e); + } + } + + ResponseData tbsResp = new ResponseData(responderID.toASN1Object(), new ASN1GeneralizedTime(producedAt), new DERSequence(responses), responseExtensions); + DERBitString bitSig; + + try + { + OutputStream sigOut = signer.getOutputStream(); + + sigOut.write(tbsResp.getEncoded(ASN1Encoding.DER)); + sigOut.close(); + + bitSig = new DERBitString(signer.getSignature()); + } + catch (Exception e) + { + throw new OCSPException("exception processing TBSRequest: " + e.getMessage(), e); + } + + AlgorithmIdentifier sigAlgId = signer.getAlgorithmIdentifier(); + + DERSequence chainSeq = null; + if (chain != null && chain.length > 0) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != chain.length; i++) + { + v.add(chain[i].toASN1Structure()); + } + + chainSeq = new DERSequence(v); + } + + return new BasicOCSPResp(new BasicOCSPResponse(tbsResp, sigAlgId, bitSig, chainSeq)); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateID.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateID.java new file mode 100644 index 00000000..c6b09ad8 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateID.java @@ -0,0 +1,156 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.OutputStream; +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.ocsp.CertID; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.DigestCalculatorProvider; +import org.bouncycastle.operator.OperatorCreationException; + +public class CertificateID +{ + public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + + private final CertID id; + + public CertificateID( + CertID id) + { + if (id == null) + { + throw new IllegalArgumentException("'id' cannot be null"); + } + this.id = id; + } + + /** + * create from an issuer certificate and the serial number of the + * certificate it signed. + * + * @param issuerCert issuing certificate + * @param number serial number + * + * @exception OCSPException if any problems occur creating the id fields. + */ + public CertificateID( + DigestCalculator digestCalculator, X509CertificateHolder issuerCert, + BigInteger number) + throws OCSPException + { + this.id = createCertID(digestCalculator, issuerCert, new ASN1Integer(number)); + } + + public ASN1ObjectIdentifier getHashAlgOID() + { + return id.getHashAlgorithm().getAlgorithm(); + } + + public byte[] getIssuerNameHash() + { + return id.getIssuerNameHash().getOctets(); + } + + public byte[] getIssuerKeyHash() + { + return id.getIssuerKeyHash().getOctets(); + } + + /** + * return the serial number for the certificate associated + * with this request. + */ + public BigInteger getSerialNumber() + { + return id.getSerialNumber().getValue(); + } + + public boolean matchesIssuer(X509CertificateHolder issuerCert, DigestCalculatorProvider digCalcProvider) + throws OCSPException + { + try + { + return createCertID(digCalcProvider.get(id.getHashAlgorithm()), issuerCert, id.getSerialNumber()).equals(id); + } + catch (OperatorCreationException e) + { + throw new OCSPException("unable to create digest calculator: " + e.getMessage(), e); + } + } + + public CertID toASN1Object() + { + return id; + } + + public boolean equals( + Object o) + { + if (!(o instanceof CertificateID)) + { + return false; + } + + CertificateID obj = (CertificateID)o; + + return id.toASN1Primitive().equals(obj.id.toASN1Primitive()); + } + + public int hashCode() + { + return id.toASN1Primitive().hashCode(); + } + + /** + * Create a new CertificateID for a new serial number derived from a previous one + * calculated for the same CA certificate. + * + * @param original the previously calculated CertificateID for the CA. + * @param newSerialNumber the serial number for the new certificate of interest. + * + * @return a new CertificateID for newSerialNumber + */ + public static CertificateID deriveCertificateID(CertificateID original, BigInteger newSerialNumber) + { + return new CertificateID(new CertID(original.id.getHashAlgorithm(), original.id.getIssuerNameHash(), original.id.getIssuerKeyHash(), new ASN1Integer(newSerialNumber))); + } + + private static CertID createCertID(DigestCalculator digCalc, X509CertificateHolder issuerCert, ASN1Integer serialNumber) + throws OCSPException + { + try + { + OutputStream dgOut = digCalc.getOutputStream(); + + dgOut.write(issuerCert.toASN1Structure().getSubject().getEncoded(ASN1Encoding.DER)); + dgOut.close(); + + ASN1OctetString issuerNameHash = new DEROctetString(digCalc.getDigest()); + + SubjectPublicKeyInfo info = issuerCert.getSubjectPublicKeyInfo(); + + dgOut = digCalc.getOutputStream(); + + dgOut.write(info.getPublicKeyData().getBytes()); + dgOut.close(); + + ASN1OctetString issuerKeyHash = new DEROctetString(digCalc.getDigest()); + + return new CertID(digCalc.getAlgorithmIdentifier(), issuerNameHash, issuerKeyHash, serialNumber); + } + catch (Exception e) + { + throw new OCSPException("problem creating ID: " + e, e); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateStatus.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateStatus.java new file mode 100644 index 00000000..3aa117df --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateStatus.java @@ -0,0 +1,6 @@ +package org.bouncycastle.cert.ocsp; + +public interface CertificateStatus +{ + public static final CertificateStatus GOOD = null; +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPException.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPException.java new file mode 100644 index 00000000..6489788c --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPException.java @@ -0,0 +1,27 @@ +package org.bouncycastle.cert.ocsp; + +public class OCSPException + extends Exception +{ + private Throwable cause; + + public OCSPException( + String name) + { + super(name); + } + + public OCSPException( + String name, + Throwable cause) + { + super(name); + + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java new file mode 100644 index 00000000..2706c401 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java @@ -0,0 +1,259 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Exception; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OutputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ocsp.OCSPRequest; +import org.bouncycastle.asn1.ocsp.Request; +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.ContentVerifier; +import org.bouncycastle.operator.ContentVerifierProvider; + +/** + * <pre> + * OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + * + * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL} + * + * Version ::= INTEGER { v1(0) } + * + * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * </pre> + */ +public class OCSPReq +{ + private static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0]; + + private OCSPRequest req; + private Extensions extensions; + + public OCSPReq( + OCSPRequest req) + { + this.req = req; + this.extensions = req.getTbsRequest().getRequestExtensions(); + } + + public OCSPReq( + byte[] req) + throws IOException + { + this(new ASN1InputStream(req)); + } + + private OCSPReq( + ASN1InputStream aIn) + throws IOException + { + try + { + this.req = OCSPRequest.getInstance(aIn.readObject()); + if (req == null) + { + throw new CertIOException("malformed request: no request data found"); + } + this.extensions = req.getTbsRequest().getRequestExtensions(); + } + catch (IllegalArgumentException e) + { + throw new CertIOException("malformed request: " + e.getMessage(), e); + } + catch (ClassCastException e) + { + throw new CertIOException("malformed request: " + e.getMessage(), e); + } + catch (ASN1Exception e) + { + throw new CertIOException("malformed request: " + e.getMessage(), e); + } + } + + public int getVersionNumber() + { + return req.getTbsRequest().getVersion().getValue().intValue() + 1; + } + + public GeneralName getRequestorName() + { + return GeneralName.getInstance(req.getTbsRequest().getRequestorName()); + } + + public Req[] getRequestList() + { + ASN1Sequence seq = req.getTbsRequest().getRequestList(); + Req[] requests = new Req[seq.size()]; + + for (int i = 0; i != requests.length; i++) + { + requests[i] = new Req(Request.getInstance(seq.getObjectAt(i))); + } + + return requests; + } + + public boolean hasExtensions() + { + return extensions != null; + } + + public Extension getExtension(ASN1ObjectIdentifier oid) + { + if (extensions != null) + { + return extensions.getExtension(oid); + } + + return null; + } + + public List getExtensionOIDs() + { + return OCSPUtils.getExtensionOIDs(extensions); + } + + public Set getCriticalExtensionOIDs() + { + return OCSPUtils.getCriticalExtensionOIDs(extensions); + } + + public Set getNonCriticalExtensionOIDs() + { + return OCSPUtils.getNonCriticalExtensionOIDs(extensions); + } + + /** + * return the object identifier representing the signature algorithm + */ + public ASN1ObjectIdentifier getSignatureAlgOID() + { + if (!this.isSigned()) + { + return null; + } + + return req.getOptionalSignature().getSignatureAlgorithm().getAlgorithm(); + } + + public byte[] getSignature() + { + if (!this.isSigned()) + { + return null; + } + + return req.getOptionalSignature().getSignature().getBytes(); + } + + public X509CertificateHolder[] getCerts() + { + // + // load the certificates if we have any + // + if (req.getOptionalSignature() != null) + { + ASN1Sequence s = req.getOptionalSignature().getCerts(); + + if (s != null) + { + X509CertificateHolder[] certs = new X509CertificateHolder[s.size()]; + + for (int i = 0; i != certs.length; i++) + { + certs[i] = new X509CertificateHolder(Certificate.getInstance(s.getObjectAt(i))); + } + + return certs; + } + + return EMPTY_CERTS; + } + else + { + return EMPTY_CERTS; + } + } + + /** + * Return whether or not this request is signed. + * + * @return true if signed false otherwise. + */ + public boolean isSigned() + { + return req.getOptionalSignature() != null; + } + + /** + * verify the signature against the TBSRequest object we contain. + */ + public boolean isSignatureValid( + ContentVerifierProvider verifierProvider) + throws OCSPException + { + if (!this.isSigned()) + { + throw new OCSPException("attempt to verify signature on unsigned object"); + } + + try + { + ContentVerifier verifier = verifierProvider.get(req.getOptionalSignature().getSignatureAlgorithm()); + OutputStream sOut = verifier.getOutputStream(); + + sOut.write(req.getTbsRequest().getEncoded(ASN1Encoding.DER)); + + return verifier.verify(this.getSignature()); + } + catch (Exception e) + { + throw new OCSPException("exception processing signature: " + e, e); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + aOut.writeObject(req); + + return bOut.toByteArray(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java new file mode 100644 index 00000000..e7e8e0f5 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java @@ -0,0 +1,199 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.ocsp.OCSPRequest; +import org.bouncycastle.asn1.ocsp.Request; +import org.bouncycastle.asn1.ocsp.Signature; +import org.bouncycastle.asn1.ocsp.TBSRequest; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.operator.ContentSigner; + +public class OCSPReqBuilder +{ + private List list = new ArrayList(); + private GeneralName requestorName = null; + private Extensions requestExtensions = null; + + private class RequestObject + { + CertificateID certId; + Extensions extensions; + + public RequestObject( + CertificateID certId, + Extensions extensions) + { + this.certId = certId; + this.extensions = extensions; + } + + public Request toRequest() + throws Exception + { + return new Request(certId.toASN1Object(), extensions); + } + } + + /** + * Add a request for the given CertificateID. + * + * @param certId certificate ID of interest + */ + public OCSPReqBuilder addRequest( + CertificateID certId) + { + list.add(new RequestObject(certId, null)); + + return this; + } + + /** + * Add a request with extensions + * + * @param certId certificate ID of interest + * @param singleRequestExtensions the extensions to attach to the request + */ + public OCSPReqBuilder addRequest( + CertificateID certId, + Extensions singleRequestExtensions) + { + list.add(new RequestObject(certId, singleRequestExtensions)); + + return this; + } + + /** + * Set the requestor name to the passed in X500Principal + * + * @param requestorName a X500Principal representing the requestor name. + */ + public OCSPReqBuilder setRequestorName( + X500Name requestorName) + { + this.requestorName = new GeneralName(GeneralName.directoryName, requestorName); + + return this; + } + + public OCSPReqBuilder setRequestorName( + GeneralName requestorName) + { + this.requestorName = requestorName; + + return this; + } + + public OCSPReqBuilder setRequestExtensions( + Extensions requestExtensions) + { + this.requestExtensions = requestExtensions; + + return this; + } + + private OCSPReq generateRequest( + ContentSigner contentSigner, + X509CertificateHolder[] chain) + throws OCSPException + { + Iterator it = list.iterator(); + + ASN1EncodableVector requests = new ASN1EncodableVector(); + + while (it.hasNext()) + { + try + { + requests.add(((RequestObject)it.next()).toRequest()); + } + catch (Exception e) + { + throw new OCSPException("exception creating Request", e); + } + } + + TBSRequest tbsReq = new TBSRequest(requestorName, new DERSequence(requests), requestExtensions); + + Signature signature = null; + + if (contentSigner != null) + { + if (requestorName == null) + { + throw new OCSPException("requestorName must be specified if request is signed."); + } + + try + { + OutputStream sOut = contentSigner.getOutputStream(); + + sOut.write(tbsReq.getEncoded(ASN1Encoding.DER)); + + sOut.close(); + } + catch (Exception e) + { + throw new OCSPException("exception processing TBSRequest: " + e, e); + } + + DERBitString bitSig = new DERBitString(contentSigner.getSignature()); + + AlgorithmIdentifier sigAlgId = contentSigner.getAlgorithmIdentifier(); + + if (chain != null && chain.length > 0) + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != chain.length; i++) + { + v.add(chain[i].toASN1Structure()); + } + + signature = new Signature(sigAlgId, bitSig, new DERSequence(v)); + } + else + { + signature = new Signature(sigAlgId, bitSig); + } + } + + return new OCSPReq(new OCSPRequest(tbsReq, signature)); + } + + /** + * Generate an unsigned request + * + * @return the OCSPReq + * @throws org.bouncycastle.ocsp.OCSPException + */ + public OCSPReq build() + throws OCSPException + { + return generateRequest(null, null); + } + + public OCSPReq build( + ContentSigner signer, + X509CertificateHolder[] chain) + throws OCSPException, IllegalArgumentException + { + if (signer == null) + { + throw new IllegalArgumentException("no signer specified"); + } + + return generateRequest(signer, chain); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java new file mode 100644 index 00000000..ed3918ac --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java @@ -0,0 +1,141 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.asn1.ASN1Exception; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ocsp.BasicOCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.ResponseBytes; +import org.bouncycastle.cert.CertIOException; + +public class OCSPResp +{ + public static final int SUCCESSFUL = 0; // Response has valid confirmations + public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request + public static final int INTERNAL_ERROR = 2; // Internal error in issuer + public static final int TRY_LATER = 3; // Try again later + // (4) is not used + public static final int SIG_REQUIRED = 5; // Must sign the request + public static final int UNAUTHORIZED = 6; // Request unauthorized + + private OCSPResponse resp; + + public OCSPResp( + OCSPResponse resp) + { + this.resp = resp; + } + + public OCSPResp( + byte[] resp) + throws IOException + { + this(new ByteArrayInputStream(resp)); + } + + public OCSPResp( + InputStream resp) + throws IOException + { + this(new ASN1InputStream(resp)); + } + + private OCSPResp( + ASN1InputStream aIn) + throws IOException + { + try + { + this.resp = OCSPResponse.getInstance(aIn.readObject()); + } + catch (IllegalArgumentException e) + { + throw new CertIOException("malformed response: " + e.getMessage(), e); + } + catch (ClassCastException e) + { + throw new CertIOException("malformed response: " + e.getMessage(), e); + } + catch (ASN1Exception e) + { + throw new CertIOException("malformed response: " + e.getMessage(), e); + } + + if (resp == null) + { + throw new CertIOException("malformed response: no response data found"); + } + } + + public int getStatus() + { + return this.resp.getResponseStatus().getValue().intValue(); + } + + public Object getResponseObject() + throws OCSPException + { + ResponseBytes rb = this.resp.getResponseBytes(); + + if (rb == null) + { + return null; + } + + if (rb.getResponseType().equals(OCSPObjectIdentifiers.id_pkix_ocsp_basic)) + { + try + { + ASN1Primitive obj = ASN1Primitive.fromByteArray(rb.getResponse().getOctets()); + return new BasicOCSPResp(BasicOCSPResponse.getInstance(obj)); + } + catch (Exception e) + { + throw new OCSPException("problem decoding object: " + e, e); + } + } + + return rb.getResponse(); + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] getEncoded() + throws IOException + { + return resp.getEncoded(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (!(o instanceof OCSPResp)) + { + return false; + } + + OCSPResp r = (OCSPResp)o; + + return resp.equals(r.resp); + } + + public int hashCode() + { + return resp.hashCode(); + } + + public OCSPResponse toASN1Structure() + { + return resp; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java new file mode 100644 index 00000000..c372ebff --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java @@ -0,0 +1,59 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; +import org.bouncycastle.asn1.ocsp.OCSPResponse; +import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; +import org.bouncycastle.asn1.ocsp.ResponseBytes; + +/** + * base generator for an OCSP response - at the moment this only supports the + * generation of responses containing BasicOCSP responses. + */ +public class OCSPRespBuilder +{ + public static final int SUCCESSFUL = 0; // Response has valid confirmations + public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request + public static final int INTERNAL_ERROR = 2; // Internal error in issuer + public static final int TRY_LATER = 3; // Try again later + // (4) is not used + public static final int SIG_REQUIRED = 5; // Must sign the request + public static final int UNAUTHORIZED = 6; // Request unauthorized + + public OCSPResp build( + int status, + Object response) + throws OCSPException + { + if (response == null) + { + return new OCSPResp(new OCSPResponse(new OCSPResponseStatus(status), null)); + } + + if (response instanceof BasicOCSPResp) + { + BasicOCSPResp r = (BasicOCSPResp)response; + ASN1OctetString octs; + + try + { + octs = new DEROctetString(r.getEncoded()); + } + catch (IOException e) + { + throw new OCSPException("can't encode object.", e); + } + + ResponseBytes rb = new ResponseBytes( + OCSPObjectIdentifiers.id_pkix_ocsp_basic, octs); + + return new OCSPResp(new OCSPResponse( + new OCSPResponseStatus(status), rb)); + } + + throw new OCSPException("unknown response object"); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPUtils.java new file mode 100644 index 00000000..a84f409c --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPUtils.java @@ -0,0 +1,64 @@ +package org.bouncycastle.cert.ocsp; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.cert.X509CertificateHolder; + +class OCSPUtils +{ + static final X509CertificateHolder[] EMPTY_CERTS = new X509CertificateHolder[0]; + + static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet()); + static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList()); + + static Date extractDate(ASN1GeneralizedTime time) + { + try + { + return time.getDate(); + } + catch (Exception e) + { + throw new IllegalStateException("exception processing GeneralizedTime: " + e.getMessage()); + } + } + + static Set getCriticalExtensionOIDs(Extensions extensions) + { + if (extensions == null) + { + return EMPTY_SET; + } + + return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getCriticalExtensionOIDs()))); + } + + static Set getNonCriticalExtensionOIDs(Extensions extensions) + { + if (extensions == null) + { + return EMPTY_SET; + } + + // TODO: should probably produce a set that imposes correct ordering + return Collections.unmodifiableSet(new HashSet(Arrays.asList(extensions.getNonCriticalExtensionOIDs()))); + } + + static List getExtensionOIDs(Extensions extensions) + { + if (extensions == null) + { + return EMPTY_LIST; + } + + return Collections.unmodifiableList(Arrays.asList(extensions.getExtensionOIDs())); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/Req.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/Req.java new file mode 100644 index 00000000..6df083c5 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/Req.java @@ -0,0 +1,25 @@ +package org.bouncycastle.cert.ocsp; + +import org.bouncycastle.asn1.ocsp.Request; +import org.bouncycastle.asn1.x509.Extensions; + +public class Req +{ + private Request req; + + public Req( + Request req) + { + this.req = req; + } + + public CertificateID getCertID() + { + return new CertificateID(req.getReqCert()); + } + + public Extensions getSingleRequestExtensions() + { + return req.getSingleRequestExtensions(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespData.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespData.java new file mode 100644 index 00000000..6960fa8f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespData.java @@ -0,0 +1,52 @@ +package org.bouncycastle.cert.ocsp; + +import java.util.Date; + +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ocsp.ResponseData; +import org.bouncycastle.asn1.ocsp.SingleResponse; +import org.bouncycastle.asn1.x509.Extensions; + +public class RespData +{ + private ResponseData data; + + public RespData( + ResponseData data) + { + this.data = data; + } + + public int getVersion() + { + return data.getVersion().getValue().intValue() + 1; + } + + public RespID getResponderId() + { + return new RespID(data.getResponderID()); + } + + public Date getProducedAt() + { + return OCSPUtils.extractDate(data.getProducedAt()); + } + + public SingleResp[] getResponses() + { + ASN1Sequence s = data.getResponses(); + SingleResp[] rs = new SingleResp[s.size()]; + + for (int i = 0; i != rs.length; i++) + { + rs[i] = new SingleResp(SingleResponse.getInstance(s.getObjectAt(i))); + } + + return rs; + } + + public Extensions getResponseExtensions() + { + return data.getResponseExtensions(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespID.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespID.java new file mode 100644 index 00000000..4322ab5b --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespID.java @@ -0,0 +1,89 @@ +package org.bouncycastle.cert.ocsp; + +import java.io.OutputStream; + +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.ocsp.ResponderID; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.operator.DigestCalculator; + +/** + * Carrier for a ResponderID. + */ +public class RespID +{ + public static final AlgorithmIdentifier HASH_SHA1 = new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE); + + ResponderID id; + + public RespID( + ResponderID id) + { + this.id = id; + } + + public RespID( + X500Name name) + { + this.id = new ResponderID(name); + } + + /** + * Calculate a RespID based on the public key of the responder. + * + * @param subjectPublicKeyInfo the info structure for the responder public key. + * @param digCalc a SHA-1 digest calculator. + * @throws OCSPException on exception creating ID. + */ + public RespID( + SubjectPublicKeyInfo subjectPublicKeyInfo, + DigestCalculator digCalc) + throws OCSPException + { + try + { + if (!digCalc.getAlgorithmIdentifier().equals(HASH_SHA1)) + { + throw new IllegalArgumentException("only SHA-1 can be used with RespID"); + } + + OutputStream digOut = digCalc.getOutputStream(); + + digOut.write(subjectPublicKeyInfo.getPublicKeyData().getBytes()); + digOut.close(); + + this.id = new ResponderID(new DEROctetString(digCalc.getDigest())); + } + catch (Exception e) + { + throw new OCSPException("problem creating ID: " + e, e); + } + } + + public ResponderID toASN1Object() + { + return id; + } + + public boolean equals( + Object o) + { + if (!(o instanceof RespID)) + { + return false; + } + + RespID obj = (RespID)o; + + return id.equals(obj.id); + } + + public int hashCode() + { + return id.hashCode(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java new file mode 100644 index 00000000..d349f076 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java @@ -0,0 +1,55 @@ +package org.bouncycastle.cert.ocsp; + +import java.util.Date; + +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.ocsp.RevokedInfo; +import org.bouncycastle.asn1.x509.CRLReason; + +/** + * wrapper for the RevokedInfo object + */ +public class RevokedStatus + implements CertificateStatus +{ + RevokedInfo info; + + public RevokedStatus( + RevokedInfo info) + { + this.info = info; + } + + public RevokedStatus( + Date revocationDate, + int reason) + { + this.info = new RevokedInfo(new ASN1GeneralizedTime(revocationDate), CRLReason.lookup(reason)); + } + + public Date getRevocationTime() + { + return OCSPUtils.extractDate(info.getRevocationTime()); + } + + public boolean hasRevocationReason() + { + return (info.getRevocationReason() != null); + } + + /** + * return the revocation reason. Note: this field is optional, test for it + * with hasRevocationReason() first. + * @return the revocation reason value. + * @exception IllegalStateException if a reason is asked for and none is avaliable + */ + public int getRevocationReason() + { + if (info.getRevocationReason() == null) + { + throw new IllegalStateException("attempt to get a reason where none is available"); + } + + return info.getRevocationReason().getValue().intValue(); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/SingleResp.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/SingleResp.java new file mode 100644 index 00000000..ece7ea2e --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/SingleResp.java @@ -0,0 +1,102 @@ +package org.bouncycastle.cert.ocsp; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ocsp.CertStatus; +import org.bouncycastle.asn1.ocsp.RevokedInfo; +import org.bouncycastle.asn1.ocsp.SingleResponse; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; + +public class SingleResp +{ + private SingleResponse resp; + private Extensions extensions; + + public SingleResp( + SingleResponse resp) + { + this.resp = resp; + this.extensions = resp.getSingleExtensions(); + } + + public CertificateID getCertID() + { + return new CertificateID(resp.getCertID()); + } + + /** + * Return the status object for the response - null indicates good. + * + * @return the status object for the response, null if it is good. + */ + public CertificateStatus getCertStatus() + { + CertStatus s = resp.getCertStatus(); + + if (s.getTagNo() == 0) + { + return null; // good + } + else if (s.getTagNo() == 1) + { + return new RevokedStatus(RevokedInfo.getInstance(s.getStatus())); + } + + return new UnknownStatus(); + } + + public Date getThisUpdate() + { + return OCSPUtils.extractDate(resp.getThisUpdate()); + } + + /** + * return the NextUpdate value - note: this is an optional field so may + * be returned as null. + * + * @return nextUpdate, or null if not present. + */ + public Date getNextUpdate() + { + if (resp.getNextUpdate() == null) + { + return null; + } + + return OCSPUtils.extractDate(resp.getNextUpdate()); + } + + public boolean hasExtensions() + { + return extensions != null; + } + + public Extension getExtension(ASN1ObjectIdentifier oid) + { + if (extensions != null) + { + return extensions.getExtension(oid); + } + + return null; + } + + public List getExtensionOIDs() + { + return OCSPUtils.getExtensionOIDs(extensions); + } + + public Set getCriticalExtensionOIDs() + { + return OCSPUtils.getCriticalExtensionOIDs(extensions); + } + + public Set getNonCriticalExtensionOIDs() + { + return OCSPUtils.getNonCriticalExtensionOIDs(extensions); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/UnknownStatus.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/UnknownStatus.java new file mode 100644 index 00000000..8d60e2ba --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/UnknownStatus.java @@ -0,0 +1,12 @@ +package org.bouncycastle.cert.ocsp; + +/** + * wrapper for the UnknownInfo object + */ +public class UnknownStatus + implements CertificateStatus +{ + public UnknownStatus() + { + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java new file mode 100644 index 00000000..94bf52f0 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java @@ -0,0 +1,18 @@ +package org.bouncycastle.cert.ocsp.jcajce; + +import java.security.PublicKey; + +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.operator.DigestCalculator; + +public class JcaBasicOCSPRespBuilder + extends BasicOCSPRespBuilder +{ + public JcaBasicOCSPRespBuilder(PublicKey key, DigestCalculator digCalc) + throws OCSPException + { + super(SubjectPublicKeyInfo.getInstance(key.getEncoded()), digCalc); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java new file mode 100644 index 00000000..446b38bb --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java @@ -0,0 +1,20 @@ +package org.bouncycastle.cert.ocsp.jcajce; + +import java.math.BigInteger; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.operator.DigestCalculator; + +public class JcaCertificateID + extends CertificateID +{ + public JcaCertificateID(DigestCalculator digestCalculator, X509Certificate issuerCert, BigInteger number) + throws OCSPException, CertificateEncodingException + { + super(digestCalculator, new JcaX509CertificateHolder(issuerCert), number); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java new file mode 100644 index 00000000..8bc9edbd --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java @@ -0,0 +1,26 @@ +package org.bouncycastle.cert.ocsp.jcajce; + +import java.security.PublicKey; + +import javax.security.auth.x500.X500Principal; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.RespID; +import org.bouncycastle.operator.DigestCalculator; + +public class JcaRespID + extends RespID +{ + public JcaRespID(X500Principal name) + { + super(X500Name.getInstance(name.getEncoded())); + } + + public JcaRespID(PublicKey pubKey, DigestCalculator digCalc) + throws OCSPException + { + super(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()), digCalc); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html new file mode 100644 index 00000000..cfe87f22 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<body bgcolor="#ffffff"> +JCA extensions to the OCSP online certificate status package. +</body> +</html>
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html new file mode 100644 index 00000000..234cb327 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<body bgcolor="#ffffff"> +Basic support package for handling and creating OCSP (RFC 2560) online certificate status requests. +</body> +</html>
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/AllTests.java new file mode 100644 index 00000000..1f720de3 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/AllTests.java @@ -0,0 +1,44 @@ +package org.bouncycastle.cert.ocsp.test; + +import java.security.Security; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testOCSP() + { + Security.addProvider(new BouncyCastleProvider()); + + org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new OCSPTest() }; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("OCSP Tests"); + + suite.addTestSuite(AllTests.class); + + return suite; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java new file mode 100644 index 00000000..5df298ae --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/test/OCSPTest.java @@ -0,0 +1,973 @@ +package org.bouncycastle.cert.ocsp.test; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.Security; +import java.util.Date; +import java.util.Random; +import java.util.Set; +import java.util.Vector; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Exception; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.ExtensionsGenerator; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.X509Name; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPReqBuilder; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.OCSPRespBuilder; +import org.bouncycastle.cert.ocsp.Req; +import org.bouncycastle.cert.ocsp.RespID; +import org.bouncycastle.cert.ocsp.SingleResp; +import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; +import org.bouncycastle.jce.X509Principal; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.ocsp.test.OCSPTestUtil; +import org.bouncycastle.operator.DigestCalculatorProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.test.SimpleTest; + +public class OCSPTest + extends SimpleTest +{ + byte[] testResp1 = Base64.decode( + "MIIFnAoBAKCCBZUwggWRBgkrBgEFBQcwAQEEggWCMIIFfjCCARehgZ8wgZwx" + + "CzAJBgNVBAYTAklOMRcwFQYDVQQIEw5BbmRocmEgcHJhZGVzaDESMBAGA1UE" + + "BxMJSHlkZXJhYmFkMQwwCgYDVQQKEwNUQ1MxDDAKBgNVBAsTA0FUQzEeMBwG" + + "A1UEAxMVVENTLUNBIE9DU1AgUmVzcG9uZGVyMSQwIgYJKoZIhvcNAQkBFhVv" + + "Y3NwQHRjcy1jYS50Y3MuY28uaW4YDzIwMDMwNDAyMTIzNDU4WjBiMGAwOjAJ" + + "BgUrDgMCGgUABBRs07IuoCWNmcEl1oHwIak1BPnX8QQUtGyl/iL9WJ1VxjxF" + + "j0hAwJ/s1AcCAQKhERgPMjAwMjA4MjkwNzA5MjZaGA8yMDAzMDQwMjEyMzQ1" + + "OFowDQYJKoZIhvcNAQEFBQADgYEAfbN0TCRFKdhsmvOdUoiJ+qvygGBzDxD/" + + "VWhXYA+16AphHLIWNABR3CgHB3zWtdy2j7DJmQ/R7qKj7dUhWLSqclAiPgFt" + + "QQ1YvSJAYfEIdyHkxv4NP0LSogxrumANcDyC9yt/W9yHjD2ICPBIqCsZLuLk" + + "OHYi5DlwWe9Zm9VFwCGgggPMMIIDyDCCA8QwggKsoAMCAQICAQYwDQYJKoZI" + + "hvcNAQEFBQAwgZQxFDASBgNVBAMTC1RDUy1DQSBPQ1NQMSYwJAYJKoZIhvcN" + + "AQkBFhd0Y3MtY2FAdGNzLWNhLnRjcy5jby5pbjEMMAoGA1UEChMDVENTMQww" + + "CgYDVQQLEwNBVEMxEjAQBgNVBAcTCUh5ZGVyYWJhZDEXMBUGA1UECBMOQW5k" + + "aHJhIHByYWRlc2gxCzAJBgNVBAYTAklOMB4XDTAyMDgyOTA3MTE0M1oXDTAz" + + "MDgyOTA3MTE0M1owgZwxCzAJBgNVBAYTAklOMRcwFQYDVQQIEw5BbmRocmEg" + + "cHJhZGVzaDESMBAGA1UEBxMJSHlkZXJhYmFkMQwwCgYDVQQKEwNUQ1MxDDAK" + + "BgNVBAsTA0FUQzEeMBwGA1UEAxMVVENTLUNBIE9DU1AgUmVzcG9uZGVyMSQw" + + "IgYJKoZIhvcNAQkBFhVvY3NwQHRjcy1jYS50Y3MuY28uaW4wgZ8wDQYJKoZI" + + "hvcNAQEBBQADgY0AMIGJAoGBAM+XWW4caMRv46D7L6Bv8iwtKgmQu0SAybmF" + + "RJiz12qXzdvTLt8C75OdgmUomxp0+gW/4XlTPUqOMQWv463aZRv9Ust4f8MH" + + "EJh4ekP/NS9+d8vEO3P40ntQkmSMcFmtA9E1koUtQ3MSJlcs441JjbgUaVnm" + + "jDmmniQnZY4bU3tVAgMBAAGjgZowgZcwDAYDVR0TAQH/BAIwADALBgNVHQ8E" + + "BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwkwNgYIKwYBBQUHAQEEKjAoMCYG" + + "CCsGAQUFBzABhhpodHRwOi8vMTcyLjE5LjQwLjExMDo3NzAwLzAtBgNVHR8E" + + "JjAkMCKgIKAehhxodHRwOi8vMTcyLjE5LjQwLjExMC9jcmwuY3JsMA0GCSqG" + + "SIb3DQEBBQUAA4IBAQB6FovM3B4VDDZ15o12gnADZsIk9fTAczLlcrmXLNN4" + + "PgmqgnwF0Ymj3bD5SavDOXxbA65AZJ7rBNAguLUo+xVkgxmoBH7R2sBxjTCc" + + "r07NEadxM3HQkt0aX5XYEl8eRoifwqYAI9h0ziZfTNes8elNfb3DoPPjqq6V" + + "mMg0f0iMS4W8LjNPorjRB+kIosa1deAGPhq0eJ8yr0/s2QR2/WFD5P4aXc8I" + + "KWleklnIImS3zqiPrq6tl2Bm8DZj7vXlTOwmraSQxUwzCKwYob1yGvNOUQTq" + + "pG6jxn7jgDawHU1+WjWQe4Q34/pWeGLysxTraMa+Ug9kPe+jy/qRX2xwvKBZ"); + + byte[] testResp2 = Base64.decode( + "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" + + "A5oyOoNv0w+8bbtMNEc1"); + + /** + * extra version number encoding. + */ + private static byte[] irregReq = Base64.decode( + "MIIQpTBUoAMCAQAwTTBLMEkwCQYFKw4DAhoFAAQUIcFvFFVjPem15pKox4cfcnzF" + + "Kf4EFJf8OQzmVmyJ/hc4EhitQbXcqAzDAhB9ePsP19SuP6CsAgFwQuEAoIIQSzCC" + + "EEcwDQYJKoZIhvcNAQEFBQADgYEAlq/Tjl8OtFM8Tib1JYTiaPy9vFDr8UZhqXJI" + + "FyrdgtUyyDt0EcrgnBGacAeRZzF5sokIC6DjXweU7EItGqrpw/RaCUPUWFpPxR6y" + + "HjuzrLmICocTI9MH7dRUXm0qpxoY987sx1PtWB4pSR99ixBtq3OPNdsI0uJ+Qkei" + + "LbEZyvWggg+wMIIPrDCCA5owggKCoAMCAQICEEAxXx/eFe7gm/NX7AkcS68wDQYJ" + + "KoZIhvcNAQEFBQAwgZoxCzAJBgNVBAYTAlNFMTMwMQYDVQQKDCpMw6Ruc2bDtnJz" + + "w6RrcmluZ2FyIEJhbmsgQWt0aWVib2xhZyAocHVibCkxFTATBgNVBAUTDDExMTEx" + + "MTExMTExMTE/MD0GA1UEAww2TMOkbnNmw7Zyc8Oka3JpbmdhciBCYW5rIFB1cmNo" + + "YXNlciBDQTEgZm9yIEJhbmtJRCBURVNUMB4XDTA4MTAwNjIyMDAwMFoXDTEwMTAx" + + "MDIxNTk1OVowgZExCzAJBgNVBAYTAlNFMTMwMQYDVQQKDCpMw6Ruc2bDtnJzw6Rr" + + "cmluZ2FyIEJhbmsgQWt0aWVib2xhZyAocHVibCkxFTATBgNVBAUTDDExMTExMTEx" + + "MTExMTE2MDQGA1UEAwwtTMOkbnNmw7Zyc8Oka3JpbmdhciBCYW5rIE9DU1AgZm9y" + + "IEJhbmtJRCBURVNUMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5e/h6aL2m" + + "DVpWeu5e5p1Ps9kbvuuGeAp9zJDYLbZz7uzT67X+s59HaViroD2+2my/gg7rX7tK" + + "H9VXpJad1W9O19SjfNyxgeAMwVMkrbb4IlrQwu0v/Ub8JPxSWwZZXYiODq5abeXA" + + "abMYIHxSaSkhrsUj1dpSAohHLJRlq707swIDAQABo2cwZTAfBgNVHSMEGDAWgBTR" + + "vcp2QyNdNGZ+q7TjKSrrHZqxmDATBgNVHSAEDDAKMAgGBiqFcDwBBjAOBgNVHQ8B" + + "Af8EBAMCBkAwHQYDVR0OBBYEFF/3557FEvkA8iiPv2XcBclxKnTdMA0GCSqGSIb3" + + "DQEBBQUAA4IBAQAOxRvHO89XJ0v83BZdPFzEBA4B2Tqc1oABUn13S6fAkcGWvOmG" + + "eY61MK16aMnLPNDadZrAqJc6PEtVY57uaywE9acwv9XpHO0bcS94tLwvZZJ2KBt0" + + "Oq96gaI6gnJViUjyWjm+qBZvod0QPOLGv6wUPoiNcCpSid/COTjKpLYpCJj3ZWUV" + + "nsTRWSRVXsdY/xI0gs/A8/c5P1PuTxoi99RTmcruoFxvV4MmhWyX7IGqG4OAtLdo" + + "yefz/90FPGOrmqY9OgEb+gNuTM26YDvSs1dfarPl89d8jjwxHgNbZjh2VHFqKolJ" + + "8TB8ZS5aNvhHPumOOE47y95rTBxrxSmGvKb8MIIENDCCAxygAwIBAgIRAJAFaeOw" + + "7XbxH/DN/Vvhjx8wDQYJKoZIhvcNAQEFBQAwgZUxCzAJBgNVBAYTAlNFMTMwMQYD" + + "VQQKDCpMw6Ruc2bDtnJzw6RrcmluZ2FyIEJhbmsgQWt0aWVib2xhZyAocHVibCkx" + + "FTATBgNVBAUTDDExMTExMTExMTExMTE6MDgGA1UEAwwxTMOkbnNmw7Zyc8Oka3Jp" + + "bmdhciBCYW5rIFJvb3QgQ0ExIGZvciBCYW5rSUQgVEVTVDAeFw0wNzEwMDExMjAw" + + "MzdaFw0yOTA3MDExMjAwMzdaMIGaMQswCQYDVQQGEwJTRTEzMDEGA1UECgwqTMOk" + + "bnNmw7Zyc8Oka3JpbmdhciBCYW5rIEFrdGllYm9sYWcgKHB1YmwpMRUwEwYDVQQF" + + "EwwxMTExMTExMTExMTExPzA9BgNVBAMMNkzDpG5zZsO2cnPDpGtyaW5nYXIgQmFu" + + "ayBQdXJjaGFzZXIgQ0ExIGZvciBCYW5rSUQgVEVTVDCCASIwDQYJKoZIhvcNAQEB" + + "BQADggEPADCCAQoCggEBAMK5WbYojYRX1ZKrbxJBgbd4x503LfMWgr67sVD5L0NY" + + "1RPhZVFJRKJWvawE5/eXJ4oNQwc831h2jiOgINXuKyGXqdAVGBcpFwIxTfzxwT4l" + + "fvztr8pE6wk7mLLwKUvIjbM3EF1IL3zUI3UU/U5ioyGmcb/o4GGN71kMmvV/vrkU" + + "02/s7xicXNxYej4ExLiCkS5+j/+3sR47Uq5cL9e8Yg7t5/6FyLGQjKoS8HU/abYN" + + "4kpx/oyrxzrXMhnMVDiI8QX9NYGJwI8KZ/LU6GDq/NnZ3gG5v4l4UU1GhgUbrk4I" + + "AZPDu99zvwCtkdj9lJN0eDv8jdyEPZ6g1qPBE0pCNqcCAwEAAaN4MHYwDwYDVR0T" + + "AQH/BAUwAwEB/zATBgNVHSAEDDAKMAgGBiqFcDwBBjAOBgNVHQ8BAf8EBAMCAQYw" + + "HwYDVR0jBBgwFoAUnkjp1bkQUOrkRiLgxpxwAe2GQFYwHQYDVR0OBBYEFNG9ynZD" + + "I100Zn6rtOMpKusdmrGYMA0GCSqGSIb3DQEBBQUAA4IBAQAPVSC4HEd+yCtSgL0j" + + "NI19U2hJeP28lAD7OA37bcLP7eNrvfU/2tuqY7rEn1m44fUbifewdgR8x2DzhM0m" + + "fJcA5Z12PYUb85L9z8ewGQdyHLNlMpKSTP+0lebSc/obFbteC4jjuvux60y5KVOp" + + "osXbGw2qyrS6uhZJrTDP1B+bYg/XBttG+i7Qzx0S5Tq//VU9OfAQZWpvejadKAk9" + + "WCcXq6zALiJcxsUwOHZRvvHDxkHuf5eZpPvm1gaqa+G9CtV+oysZMU1eTRasBHsB" + + "NRWYfOSXggsyqRHfIAVieB4VSsB8WhZYm8UgYoLhAQfSJ5Xq5cwBOHkVj33MxAyP" + + "c7Y5MIID/zCCAuegAwIBAgIRAOXEoBcV4gV3Z92gk5AuRgwwDQYJKoZIhvcNAQEF" + + "BQAwZjEkMCIGA1UECgwbRmluYW5zaWVsbCBJRC1UZWtuaWsgQklEIEFCMR8wHQYD" + + "VQQLDBZCYW5rSUQgTWVtYmVyIEJhbmtzIENBMR0wGwYDVQQDDBRCYW5rSUQgUm9v" + + "dCBDQSBURVNUMjAeFw0wNzEwMDExMTQ1NDlaFw0yOTA4MDExMTU4MjVaMIGVMQsw" + + "CQYDVQQGEwJTRTEzMDEGA1UECgwqTMOkbnNmw7Zyc8Oka3JpbmdhciBCYW5rIEFr" + + "dGllYm9sYWcgKHB1YmwpMRUwEwYDVQQFEwwxMTExMTExMTExMTExOjA4BgNVBAMM" + + "MUzDpG5zZsO2cnPDpGtyaW5nYXIgQmFuayBSb290IENBMSBmb3IgQmFua0lEIFRF" + + "U1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBzn7IXIpyOGCCTuzL" + + "DKE/T+pFRTgFh3QgKtifZ4zxdvB2Sd5+90vUEGcGExUhzpgb9gOUrT1eE0XhdiUR" + + "YuYYpJI/nzPQWTsRtEaql7NHBPKnEauoA9oAhCT4pE5gLlqpTfkB8nAsRTI2XqpI" + + "hQ7vTvnTRx20xog21NIbz1GztV8H1kBH2eDvRX7cXGiugp6CXV/le9cB+/4TBNUN" + + "Xqupt79dM49KCoDuYr72W7Hv4BSWw3IInEN2m8T2X6UBpBGkCiGwLQy/+KOmYRK7" + + "1PSFC0rXDwOJ0HJ/8fHwx6vLMxHAQ6s/9vOW10MjgjSQlbVqH/4Pa+TlpWumSV4E" + + "l0z9AgMBAAGjeDB2MA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0gBAwwCjAIBgYqhXA8" + + "AQYwDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFJuTMPljHcYdrRO9sEi1amb4" + + "tE3VMB0GA1UdDgQWBBSeSOnVuRBQ6uRGIuDGnHAB7YZAVjANBgkqhkiG9w0BAQUF" + + "AAOCAQEArnW/9n+G+84JOgv1Wn4tsBBS7QgJp1rdCoiNrZPx2du/7Wz3wQVNKBjL" + + "eMCyLjg0OVHuq4hpCv9MZpUqdcUW8gpp4dLDAAd1uE7xqVuG8g4Ir5qocxbZHQew" + + "fnqSJJDlEZgDeZIzod92OO+htv0MWqKWbr3Mo2Hqhn+t0+UVWsW4k44e7rUw3xQq" + + "r2VdMJv/C68BXUgqh3pplUDjWyXfreiACTT0q3HT6v6WaihKCa2WY9Kd1IkDcLHb" + + "TZk8FqMmGn72SgJw3H5Dvu7AiZijjNAUulMnMpxBEKyFTU2xRBlZZVcp50VJ2F7+" + + "siisxbcYOAX4GztLMlcyq921Ov/ipDCCA88wggK3oAMCAQICEQCmaX+5+m5bF5us" + + "CtyMq41SMA0GCSqGSIb3DQEBBQUAMGYxJDAiBgNVBAoMG0ZpbmFuc2llbGwgSUQt" + + "VGVrbmlrIEJJRCBBQjEfMB0GA1UECwwWQmFua0lEIE1lbWJlciBCYW5rcyBDQTEd" + + "MBsGA1UEAwwUQmFua0lEIFJvb3QgQ0EgVEVTVDIwHhcNMDQwODEzMDcyMDEwWhcN" + + "MjkwODEyMTIwMjQ2WjBmMSQwIgYDVQQKDBtGaW5hbnNpZWxsIElELVRla25payBC" + + "SUQgQUIxHzAdBgNVBAsMFkJhbmtJRCBNZW1iZXIgQmFua3MgQ0ExHTAbBgNVBAMM" + + "FEJhbmtJRCBSb290IENBIFRFU1QyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB" + + "CgKCAQEA25D0f1gipbACk4Bg3t6ODUlCWOU0TWeTkzAHR7IRB5T++yvsVosedMMW" + + "6KYYTbPONeJSt5kydX+wZi9nVNdlhkNULLbDKWfRY7x+B9MR1Q0Kq/e4VR0uRsak" + + "Bv5iwEYZ7cSR63HfBaPTqQsGobq+wtGH5JeTBrmCt4A3kN1UWgX32Dv/I3m7v8bK" + + "iwh4cnvAD9PIOtq6pOmAkSvLvp8jCy3qFLe9KAxm8M/ZAmnxYaRV8DVEg57FGoG6" + + "oiG3Ixx8PSVVdzpFY4kuUFLi4ueMPwjnXFiBhhWJJeOtFG3Lc2aW3zvcDbD/MsDm" + + "rSZNTmtbOOou8xuMKjlNY9PU5MHIaQIDAQABo3gwdjAPBgNVHRMBAf8EBTADAQH/" + + "MBMGA1UdIAQMMAowCAYGKoVwPAEGMA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW" + + "gBSbkzD5Yx3GHa0TvbBItWpm+LRN1TAdBgNVHQ4EFgQUm5Mw+WMdxh2tE72wSLVq" + + "Zvi0TdUwDQYJKoZIhvcNAQEFBQADggEBAIQ4ZBHWssA38pfNzH5A+H3SXpAlI8Jc" + + "LuoMVOIwwbfd1Up0xopCs+Ay41v8FZtcTMFqCVTih2nzVusTgnFBPMPJ2cnTlRue" + + "kAtVRNsiWn2/Ool/OXoYf5YnpgYu8t9jLCBCoDS5YJg714r9V9hCwfey8TCWBU80" + + "vL7EIfjK13nUxf8d49GzZlFMNqGDMjfMp1FYrHBGLZBr8br/G/7em1Cprw7iR8cw" + + "pddz+QXXFIrIz5Y9D/x1RrwoLibPw0kMrSwI2G4aCvoBySfbD6cpnJf6YHRctdSb" + + "755zhdBW7XWTl6ReUVuEt0hTFms4F60kFAi5hIbDRSN1Slv5yP2b0EA="); + + private static byte[] invalidResp = Base64.decode( + "MIIGggoAoIIGfDCCBngGCSsGAQUFBzABAQSCBmkwggZlMIHeoTQwMjELMAkG" + + "A1UEBhMCVVMxDTALBgNVBAoMBGlXYXkxFDASBgNVBAMMC2lXYXkgT3BlbkNB" + + "GA8yMDEyMDEyMzIxMjkxMVowbjBsMEQwCQYFKw4DAhoFAAQUPA5ymcOyHyZJ" + + "d7DAidsEh79Uh6QEFMHnDLGSc/VElMBzr5f0+LQnpN2YAgsA5xIzv2Ln0dAa" + + "94IAGA8yMDEyMDEyMzIxMjkxMVqgERgPMjAxMjAxMjMyMTM0MTFaoSUwIzAh" + + "BgkrBgEFBQcwAQIEFCHEdgCz5w64KgppPIetaRzxewinMA0GCSqGSIb3DQEB" + + "CwUAA4IBAQBsW8cXR4eOLgclY/uRodjso/5xkHIAiJy+DpgqELRrnzKe87HO" + + "Km7DCicz1nwsPJskK14xtIw1rfQ8nzgztriComAUVc/pxJ9wQWGZI3d2dNbW" + + "AmecKb/mG0QrJrt3U5D0+CFTUq5u7NOs1jZRe+df9TDLBr0vIA6a0I6K9M9F" + + "ZOPWU/j5KVjoi0/kv4wnxRzQ2zc4Z3b5gm9T0MXMH5bST3z4yhOs/NRezNTA" + + "fBQvimS60d4fybH0pXcVYUH81y5fm9rCpuwQ6rMt2vi0ZKrfyVom4OIAr/gh" + + "Doj8Yh/LdtI1RvFkAL3pvzs06cfg3qM38b9Uh9w93w4/Hguw14eroIIEbDCC" + + "BGgwggRkMIIDTKADAgECAgEBMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNVBAYT" + + "AlVTMQ0wCwYDVQQKDARpV2F5MRQwEgYDVQQDDAtpV2F5IE9wZW5DQTAeFw0x" + + "MjAxMjAxNTIyMjFaFw0zMjAxMTUxNTIyMjFaMDIxCzAJBgNVBAYTAlVTMQ0w" + + "CwYDVQQKDARpV2F5MRQwEgYDVQQDDAtpV2F5IE9wZW5DQTCCASIwDQYJKoZI" + + "hvcNAQEBBQADggEPADCCAQoCggEBALOnLWYPvGNLxodQQ16tqCKflpEQF2OA" + + "0inZbIeUVxOgph5Qf562XV1Mtbv5Agv+z4/LSLbwuo28NTkhSlEEwf1k9vL9" + + "/wFvpPZ4ecpqXOS6LJ6khmMh53IwK/QpG8CeF9UxTZskjQzD9XgnNGYd2BIj" + + "qVbzU5qWhsPYPRrsAaE2jS6My5+xfiw46/Xj26VZQ/PR/rVURsc40fpCE30y" + + "TyORQeeZfjb/LxXH3e/3wjya04MBACv+uX89n5YXG7OH6zTriMAOn/aiXPfE" + + "E8g834RKvVS7ruELWG/IcZDC+Eoy2qtgG7y1rFlXd3H/6rny+Xd+BZrt0WP/" + + "hfezklVw3asCAwEAAaOCAYMwggF/MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0P" + + "BAQDAgEGMB0GA1UdDgQWBBTB5wyxknP1RJTAc6+X9Pi0J6TdmDAfBgNVHSME" + + "GDAWgBTB5wyxknP1RJTAc6+X9Pi0J6TdmDAjBgNVHREEHDAagRhzdXBwb3J0" + + "QGl3YXlzb2Z0d2FyZS5jb20wIwYDVR0SBBwwGoEYc3VwcG9ydEBpd2F5c29m" + + "dHdhcmUuY29tMIGYBggrBgEFBQcBAQSBizCBiDA5BggrBgEFBQcwAoYtaHR0" + + "cDovL2l3NTRjZW50LXZtMi9wa2kvcHViL2NhY2VydC9jYWNlcnQuY3J0MCUG" + + "CCsGAQUFBzABhhlodHRwOi8vaXc1NGNlbnQtdm0yOjI1NjAvMCQGCCsGAQUF" + + "BzAMhhhodHRwOi8vaXc1NGNlbnQtdm0yOjgzMC8wOgYDVR0fBDMwMTAvoC2g" + + "K4YpaHR0cDovL2l3NTRjZW50LXZtMi9wa2kvcHViL2NybC9jYWNybC5jcmww" + + "DQYJKoZIhvcNAQELBQADggEBAE9wBjQ1c+HAO2gIzT+J5Gqgrcu/m7t4hnHN" + + "m5eyIfwXD1T6wOhovFmzPTaO9BSNsi4G5R7yZxOHeLN4PIY2kwFIbSkg7mwe" + + "5aGp2RPIuK/MtzMZT6pq8uMGhzyHGsqtdkz7p26/G0anU2u59eimcvISdwNE" + + "QXOIp/KNUC+Vx+Pmfw8PuFYDNacZ6YXp5qKoEjyUoBhNicmVINTNfDu0CQhu" + + "pDr2UmDMDT2cdmTSRC0rcTe3BNzWqtsXNmIBFL1oB7B0PZbmFm8Bgvk1azxa" + + "ClrcOKZWKOWa14XJy/DJk6nlOiq5W2AglUt8JVOpa5oVdiNRIT2WoGnpqVV9" + + "tUeoWog="); + + private static final String BC = "BC"; + + public String getName() + { + return "OCSP"; + } + + private void testECDSA() + throws Exception + { + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = OCSPTestUtil.makeECKeyPair(); + X509CertificateHolder testCert = new JcaX509CertificateHolder(OCSPTestUtil.makeECDSACertificate(signKP, signDN, signKP, signDN)); + DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build(); + + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + GeneralName origName = new GeneralName(new X509Name(origDN)); + + // + // general id value for our test issuer cert and a serial number. + // + CertificateID id = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)); + + // + // basic request generation + // + OCSPReqBuilder gen = new OCSPReqBuilder(); + gen.addRequest(id); + + OCSPReq req = gen.build(); + + if (req.isSigned()) + { + fail("signed but shouldn't be"); + } + + X509CertificateHolder[] certs = req.getCerts(); + + if (certs.length != 0) + { + fail("0 certs expected, but not found"); + } + + Req[] requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // request generation with signing + // + X509CertificateHolder[] chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withECDSA").setProvider(BC).build( signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + certs = req.getCerts(); + + if (certs == null) + { + fail("null certs found"); + } + + if (certs.length != 1 || !certs[0].equals(testCert)) + { + fail("incorrect certs found in request"); + } + + // + // encoding test + // + byte[] reqEnc = req.getEncoded(); + + OCSPReq newReq = new OCSPReq(reqEnc); + + if (!newReq.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("newReq signature failed to verify"); + } + + // + // request generation with signing and nonce + // + chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + Vector oids = new Vector(); + Vector values = new Vector(); + byte[] sampleNonce = new byte[16]; + Random rand = new Random(); + + rand.nextBytes(sampleNonce); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + ExtensionsGenerator extGen = new ExtensionsGenerator(); + + extGen.addExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(sampleNonce)); + + gen.setRequestExtensions(extGen.generate()); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withECDSA").setProvider(BC).build(signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + // + // extension check. + // + Set extOids = req.getCriticalExtensionOIDs(); + + if (extOids.size() != 0) + { + fail("wrong number of critical extensions in OCSP request."); + } + + extOids = req.getNonCriticalExtensionOIDs(); + + if (extOids.size() != 1) + { + fail("wrong number of non-critical extensions in OCSP request."); + } + + Extension extValue = req.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce); + + ASN1Encodable extObj = extValue.getParsedValue(); + + if (!(extObj instanceof ASN1OctetString)) + { + fail("wrong extension type found."); + } + + if (!areEqual(((ASN1OctetString)extObj).getOctets(), sampleNonce)) + { + fail("wrong extension value found."); + } + + // + // request list check + // + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // response generation + // + BasicOCSPRespBuilder respGen = new JcaBasicOCSPRespBuilder(signKP.getPublic(), digCalcProv.get(RespID.HASH_SHA1)); + + respGen.addResponse(id, CertificateStatus.GOOD); + + BasicOCSPResp resp = respGen.build(new JcaContentSignerBuilder("SHA1withECDSA").setProvider(BC).build(signKP.getPrivate()), chain, new Date()); + } + + private void testRSA() + throws Exception + { + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = OCSPTestUtil.makeKeyPair(); + X509CertificateHolder testCert = new JcaX509CertificateHolder(OCSPTestUtil.makeCertificate(signKP, signDN, signKP, signDN)); + DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build(); + + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + GeneralName origName = new GeneralName(new X509Name(origDN)); + + // + // general id value for our test issuer cert and a serial number. + // + CertificateID id = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)); + + // + // basic request generation + // + OCSPReqBuilder gen = new OCSPReqBuilder(); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + OCSPReq req = gen.build(); + + if (req.isSigned()) + { + fail("signed but shouldn't be"); + } + + X509CertificateHolder[] certs = req.getCerts(); + + if (certs.length != 0) + { + fail("0 certs expected, but not found"); + } + + Req[] requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // request generation with signing + // + X509CertificateHolder[] chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + certs = req.getCerts(); + + if (certs == null) + { + fail("null certs found"); + } + + if (certs.length != 1 || !certs[0].equals(testCert)) + { + fail("incorrect certs found in request"); + } + + // + // encoding test + // + byte[] reqEnc = req.getEncoded(); + + OCSPReq newReq = new OCSPReq(reqEnc); + + if (!newReq.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("newReq signature failed to verify"); + } + + // + // request generation with signing and nonce + // + chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + byte[] sampleNonce = new byte[16]; + Random rand = new Random(); + + rand.nextBytes(sampleNonce); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + ExtensionsGenerator extGen = new ExtensionsGenerator(); + + extGen.addExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(sampleNonce)); + + gen.setRequestExtensions(extGen.generate()); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + // + // extension check. + // + Set extOids = req.getCriticalExtensionOIDs(); + + if (extOids.size() != 0) + { + fail("wrong number of critical extensions in OCSP request."); + } + + extOids = req.getNonCriticalExtensionOIDs(); + + if (extOids.size() != 1) + { + fail("wrong number of non-critical extensions in OCSP request."); + } + + Extension ext = req.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce); + + ASN1Encodable extObj = ext.getParsedValue(); + + if (!(extObj instanceof ASN1OctetString)) + { + fail("wrong extension type found."); + } + + if (!areEqual(((ASN1OctetString)extObj).getOctets(), sampleNonce)) + { + fail("wrong extension value found."); + } + + // + // request list check + // + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // response generation + // + BasicOCSPRespBuilder respGen = new JcaBasicOCSPRespBuilder(signKP.getPublic(), digCalcProv.get(RespID.HASH_SHA1)); + + respGen.addResponse(id, CertificateStatus.GOOD); + + BasicOCSPResp resp = respGen.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()), chain, new Date()); + OCSPRespBuilder rGen = new OCSPRespBuilder(); + + byte[] enc = rGen.build(OCSPRespBuilder.SUCCESSFUL, resp).getEncoded(); + } + + private void testIrregularVersionReq() + throws Exception + { + OCSPReq ocspRequest = new OCSPReq(irregReq); + X509CertificateHolder cert = ocspRequest.getCerts()[0]; + if (!ocspRequest.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(cert))) + { + fail("extra version encoding test failed"); + } + } + + public void testInvalidResp() + throws Exception + { + try + { + OCSPResp response = new OCSPResp(invalidResp); + } + catch (CertIOException e) + { + if (e.getCause() instanceof ASN1Exception) + { + Throwable c = ((ASN1Exception)e.getCause()).getCause(); + + if (!c.getMessage().equals("ENUMERATED has zero length")) + { + fail("parsing failed, but for wrong reason: " + c.getMessage()); + } + } + else + { + fail("parsing failed, but for wrong reason: " + e.getMessage()); + } + } + + + } + + public void performTest() + throws Exception + { + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = OCSPTestUtil.makeKeyPair(); + X509CertificateHolder testCert = new JcaX509CertificateHolder(OCSPTestUtil.makeCertificate(signKP, signDN, signKP, signDN)); + + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + GeneralName origName = new GeneralName(new X509Name(origDN)); + DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build(); + + // + // general id value for our test issuer cert and a serial number. + // + CertificateID id = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)); + + // + // basic request generation + // + OCSPReqBuilder gen = new OCSPReqBuilder(); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + OCSPReq req = gen.build(); + + if (req.isSigned()) + { + fail("signed but shouldn't be"); + } + + X509CertificateHolder[] certs = req.getCerts(); + + if (certs.length != 0) + { + fail("0 certs expected, but not found"); + } + + Req[] requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // request generation with signing + // + X509CertificateHolder[] chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + certs = req.getCerts(); + + if (certs == null) + { + fail("null certs found"); + } + + if (certs.length != 1 || !certs[0].equals(testCert)) + { + fail("incorrect certs found in request"); + } + + // + // encoding test + // + byte[] reqEnc = req.getEncoded(); + + OCSPReq newReq = new OCSPReq(reqEnc); + + if (!newReq.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("newReq signature failed to verify"); + } + + // + // request generation with signing and nonce + // + chain = new X509CertificateHolder[1]; + + gen = new OCSPReqBuilder(); + + Vector oids = new Vector(); + Vector values = new Vector(); + byte[] sampleNonce = new byte[16]; + Random rand = new Random(); + + rand.nextBytes(sampleNonce); + + gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred"))); + + ExtensionsGenerator extGen = new ExtensionsGenerator(); + + extGen.addExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(sampleNonce)); + + gen.setRequestExtensions(extGen.generate()); + + gen.addRequest( + new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1))); + + chain[0] = testCert; + + req = gen.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()), chain); + + if (!req.isSigned()) + { + fail("not signed but should be"); + } + + if (!req.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) + { + fail("signature failed to verify"); + } + + // + // extension check. + // + Set extOids = req.getCriticalExtensionOIDs(); + + if (extOids.size() != 0) + { + fail("wrong number of critical extensions in OCSP request."); + } + + extOids = req.getNonCriticalExtensionOIDs(); + + if (extOids.size() != 1) + { + fail("wrong number of non-critical extensions in OCSP request."); + } + + Extension ext = req.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce); + + ASN1Encodable extObj = ext.getParsedValue(); + + if (!(extObj instanceof ASN1OctetString)) + { + fail("wrong extension type found."); + } + + if (!areEqual(((ASN1OctetString)extObj).getOctets(), sampleNonce)) + { + fail("wrong extension value found."); + } + + // + // request list check + // + requests = req.getRequestList(); + + if (!requests[0].getCertID().equals(id)) + { + fail("Failed isFor test"); + } + + // + // response parsing - test 1 + // + OCSPResp response = new OCSPResp(testResp1); + + if (response.getStatus() != 0) + { + fail("response status not zero."); + } + + BasicOCSPResp brep = (BasicOCSPResp)response.getResponseObject(); + chain = brep.getCerts(); + + if (!brep.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(chain[0]))) + { + fail("response 1 failed to verify."); + } + + // + // test 2 + // + SingleResp[] singleResp = brep.getResponses(); + + response = new OCSPResp(testResp2); + + if (response.getStatus() != 0) + { + fail("response status not zero."); + } + + brep = (BasicOCSPResp)response.getResponseObject(); + chain = brep.getCerts(); + + if (!brep.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(chain[0]))) + { + fail("response 2 failed to verify."); + } + + singleResp = brep.getResponses(); + + // + // simple response generation + // + OCSPRespBuilder respGen = new OCSPRespBuilder(); + OCSPResp resp = respGen.build(OCSPRespBuilder.SUCCESSFUL, response.getResponseObject()); + + if (!resp.getResponseObject().equals(response.getResponseObject())) + { + fail("response fails to match"); + } + + testECDSA(); + testRSA(); + testIrregularVersionReq(); + testInvalidResp(); + + // + // Empty data test + // + try + { + response = new OCSPResp(new byte[0]); + fail("no exception thrown"); + } + catch (IOException e) + { + if (!e.getMessage().equals("malformed response: no response data found")) + { + fail("wrong exception"); + } + } + + try + { + req = new OCSPReq(new byte[0]); + fail("no exception thrown"); + } + catch (IOException e) + { + if (!e.getMessage().equals("malformed request: no request data found")) + { + fail("wrong exception"); + } + } + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new OCSPTest()); + } +} |