summaryrefslogtreecommitdiff
path: root/bcpkix
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2016-02-12 17:52:48 -0800
committerKenny Root <kroot@google.com>2016-03-04 08:59:00 -0800
commit4eb438010b8024cfa97cdad1906a8e6963a16f5b (patch)
tree46f46c47b22a346f35adc11d3a8e0145ea2b8825 /bcpkix
parentac5a99c3e95beb96bb0264d505b594b99d4fc3b5 (diff)
downloadbouncycastle-4eb438010b8024cfa97cdad1906a8e6963a16f5b.tar.gz
Add OCSP files for testing purposes
Testing OCSP support needs some ASN.1 creation utilities. Bouncycastle has them, but we don't want to bloat up the built-in libraries. Add some new targets that will allow us to enable OCSP testing in the core-tests module without spreading it elsewhere. Change-Id: I4a75fc0d5186c70a764baa751ceab75d1a44539d
Diffstat (limited to 'bcpkix')
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPResp.java225
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java283
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateID.java156
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/CertificateStatus.java6
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPException.java27
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReq.java259
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPReqBuilder.java199
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPResp.java141
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPRespBuilder.java59
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/OCSPUtils.java64
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/Req.java25
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespData.java52
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RespID.java89
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/RevokedStatus.java55
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/SingleResp.java102
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/UnknownStatus.java12
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaBasicOCSPRespBuilder.java18
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaCertificateID.java20
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/JcaRespID.java26
-rw-r--r--bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html7
20 files changed, 1825 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..d74bef00
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPResp.java
@@ -0,0 +1,225 @@
+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.AlgorithmIdentifier;
+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;
+import org.bouncycastle.util.Encodable;
+
+/**
+ * <pre>
+ * BasicOCSPResponse ::= SEQUENCE {
+ * tbsResponseData ResponseData,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ * </pre>
+ */
+public class BasicOCSPResp
+ implements Encodable
+{
+ 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;
+ }
+ }
+
+ /**
+ * Return the algorithm identifier describing the signature used in the response.
+ *
+ * @return an AlgorithmIdentifier
+ */
+ public AlgorithmIdentifier getSignatureAlgorithmID()
+ {
+ return resp.getSignatureAlgorithm();
+ }
+
+ 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().getOctets();
+ }
+
+ 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..56959071
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/BasicOCSPRespBuilder.java
@@ -0,0 +1,283 @@
+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;
+ ASN1GeneralizedTime thisUpdate;
+ ASN1GeneralizedTime 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.toASN1Primitive(), 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)
+ {
+ this.addResponse(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)
+ {
+ this.addResponse(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)
+ {
+ this.addResponse(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
+ */
+ public BasicOCSPRespBuilder addResponse(
+ CertificateID certID,
+ CertificateStatus certStatus,
+ Date thisUpdate,
+ Date nextUpdate)
+ {
+ this.addResponse(certID, certStatus, thisUpdate, nextUpdate, null);
+
+ 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.toASN1Primitive(), 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..aa97f196
--- /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 toASN1Primitive()
+ {
+ 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..437d37be
--- /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().getOctets();
+ }
+
+ 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..b0cfb9ef
--- /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.toASN1Primitive(), 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..a0fd765a
--- /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 toASN1Primitive()
+ {
+ 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