001: package org.bouncycastle.ocsp;
002:
003: import java.io.ByteArrayInputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.security.InvalidAlgorithmParameterException;
008: import java.security.NoSuchAlgorithmException;
009: import java.security.NoSuchProviderException;
010: import java.security.PublicKey;
011: import java.security.cert.CertStore;
012: import java.security.cert.CertificateException;
013: import java.security.cert.CertificateFactory;
014: import java.security.cert.CollectionCertStoreParameters;
015: import java.security.cert.X509Certificate;
016: import java.util.ArrayList;
017: import java.util.Enumeration;
018: import java.util.HashSet;
019: import java.util.List;
020: import java.util.Set;
021:
022: import org.bouncycastle.asn1.ASN1InputStream;
023: import org.bouncycastle.asn1.ASN1OutputStream;
024: import org.bouncycastle.asn1.ASN1Sequence;
025: import org.bouncycastle.asn1.DERObjectIdentifier;
026: import org.bouncycastle.asn1.DEROutputStream;
027: import org.bouncycastle.asn1.ocsp.OCSPRequest;
028: import org.bouncycastle.asn1.ocsp.Request;
029: import org.bouncycastle.asn1.x509.GeneralName;
030: import org.bouncycastle.asn1.x509.X509Extension;
031: import org.bouncycastle.asn1.x509.X509Extensions;
032:
033: /**
034: * <pre>
035: * OCSPRequest ::= SEQUENCE {
036: * tbsRequest TBSRequest,
037: * optionalSignature [0] EXPLICIT Signature OPTIONAL }
038: *
039: * TBSRequest ::= SEQUENCE {
040: * version [0] EXPLICIT Version DEFAULT v1,
041: * requestorName [1] EXPLICIT GeneralName OPTIONAL,
042: * requestList SEQUENCE OF Request,
043: * requestExtensions [2] EXPLICIT Extensions OPTIONAL }
044: *
045: * Signature ::= SEQUENCE {
046: * signatureAlgorithm AlgorithmIdentifier,
047: * signature BIT STRING,
048: * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL}
049: *
050: * Version ::= INTEGER { v1(0) }
051: *
052: * Request ::= SEQUENCE {
053: * reqCert CertID,
054: * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
055: *
056: * CertID ::= SEQUENCE {
057: * hashAlgorithm AlgorithmIdentifier,
058: * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
059: * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
060: * serialNumber CertificateSerialNumber }
061: * </pre>
062: */
063: public class OCSPReq implements java.security.cert.X509Extension {
064: private OCSPRequest req;
065:
066: public OCSPReq(OCSPRequest req) {
067: this .req = req;
068: }
069:
070: public OCSPReq(byte[] req) throws IOException {
071: this (new ASN1InputStream(req));
072: }
073:
074: public OCSPReq(InputStream in) throws IOException {
075: this (new ASN1InputStream(in));
076: }
077:
078: private OCSPReq(ASN1InputStream aIn) throws IOException {
079: try {
080: this .req = OCSPRequest.getInstance(aIn.readObject());
081: } catch (IllegalArgumentException e) {
082: throw new IOException("malformed request: "
083: + e.getMessage());
084: } catch (ClassCastException e) {
085: throw new IOException("malformed request: "
086: + e.getMessage());
087: }
088: }
089:
090: /**
091: * Return the DER encoding of the tbsRequest field.
092: * @return DER encoding of tbsRequest
093: * @throws OCSPException in the event of an encoding error.
094: */
095: public byte[] getTBSRequest() throws OCSPException {
096: try {
097: return req.getTbsRequest().getEncoded();
098: } catch (IOException e) {
099: throw new OCSPException("problem encoding tbsRequest", e);
100: }
101: }
102:
103: public int getVersion() {
104: return req.getTbsRequest().getVersion().getValue().intValue() + 1;
105: }
106:
107: public GeneralName getRequestorName() {
108: return GeneralName.getInstance(req.getTbsRequest()
109: .getRequestorName());
110: }
111:
112: public Req[] getRequestList() {
113: ASN1Sequence seq = req.getTbsRequest().getRequestList();
114: Req[] requests = new Req[seq.size()];
115:
116: for (int i = 0; i != requests.length; i++) {
117: requests[i] = new Req(Request.getInstance(seq
118: .getObjectAt(i)));
119: }
120:
121: return requests;
122: }
123:
124: public X509Extensions getRequestExtensions() {
125: return X509Extensions.getInstance(req.getTbsRequest()
126: .getRequestExtensions());
127: }
128:
129: /**
130: * return the object identifier representing the signature algorithm
131: */
132: public String getSignatureAlgOID() {
133: if (!this .isSigned()) {
134: return null;
135: }
136:
137: return req.getOptionalSignature().getSignatureAlgorithm()
138: .getObjectId().getId();
139: }
140:
141: public byte[] getSignature() {
142: if (!this .isSigned()) {
143: return null;
144: }
145:
146: return req.getOptionalSignature().getSignature().getBytes();
147: }
148:
149: private List getCertList(String provider) throws OCSPException,
150: NoSuchProviderException {
151: List certs = new ArrayList();
152: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
153: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
154: CertificateFactory cf;
155:
156: try {
157: cf = CertificateFactory.getInstance("X.509", provider);
158: } catch (CertificateException ex) {
159: throw new OCSPException("can't get certificate factory.",
160: ex);
161: }
162:
163: //
164: // load the certificates if we have any
165: //
166: ASN1Sequence s = req.getOptionalSignature().getCerts();
167:
168: if (s != null) {
169: Enumeration e = s.getObjects();
170:
171: while (e.hasMoreElements()) {
172: try {
173: aOut.writeObject(e.nextElement());
174:
175: certs
176: .add(cf
177: .generateCertificate(new ByteArrayInputStream(
178: bOut.toByteArray())));
179: } catch (IOException ex) {
180: throw new OCSPException(
181: "can't re-encode certificate!", ex);
182: } catch (CertificateException ex) {
183: throw new OCSPException(
184: "can't re-encode certificate!", ex);
185: }
186:
187: bOut.reset();
188: }
189: }
190:
191: return certs;
192: }
193:
194: public X509Certificate[] getCerts(String provider)
195: throws OCSPException, NoSuchProviderException {
196: if (!this .isSigned()) {
197: return null;
198: }
199:
200: List certs = this .getCertList(provider);
201:
202: return (X509Certificate[]) certs
203: .toArray(new X509Certificate[certs.size()]);
204: }
205:
206: /**
207: * If the request is signed return a possibly empty CertStore containing the certificates in the
208: * request. If the request is not signed the method returns null.
209: *
210: * @param type type of CertStore to return
211: * @param provider provider to use
212: * @return null if not signed, a CertStore otherwise
213: * @throws NoSuchAlgorithmException
214: * @throws NoSuchProviderException
215: * @throws OCSPException
216: */
217: public CertStore getCertificates(String type, String provider)
218: throws NoSuchAlgorithmException, NoSuchProviderException,
219: OCSPException {
220: if (!this .isSigned()) {
221: return null;
222: }
223:
224: try {
225: return CertStore.getInstance(type,
226: new CollectionCertStoreParameters(this
227: .getCertList(provider)), provider);
228: } catch (InvalidAlgorithmParameterException e) {
229: throw new OCSPException("can't setup the CertStore", e);
230: }
231: }
232:
233: /**
234: * Return whether or not this request is signed.
235: *
236: * @return true if signed false otherwise.
237: */
238: public boolean isSigned() {
239: return req.getOptionalSignature() != null;
240: }
241:
242: /**
243: * verify the signature against the TBSRequest object we contain.
244: */
245: public boolean verify(PublicKey key, String sigProvider)
246: throws OCSPException, NoSuchProviderException {
247: if (!this .isSigned()) {
248: throw new OCSPException(
249: "attempt to verify signature on unsigned object");
250: }
251:
252: try {
253: java.security.Signature signature = java.security.Signature
254: .getInstance(this .getSignatureAlgOID(), sigProvider);
255:
256: signature.initVerify(key);
257:
258: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
259: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
260:
261: aOut.writeObject(req.getTbsRequest());
262:
263: signature.update(bOut.toByteArray());
264:
265: return signature.verify(this .getSignature());
266: } catch (NoSuchProviderException e) {
267: throw e;
268: } catch (Exception e) {
269: throw new OCSPException("exception processing sig: " + e, e);
270: }
271: }
272:
273: /**
274: * return the ASN.1 encoded representation of this object.
275: */
276: public byte[] getEncoded() throws IOException {
277: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
278: ASN1OutputStream aOut = new ASN1OutputStream(bOut);
279:
280: aOut.writeObject(req);
281:
282: return bOut.toByteArray();
283: }
284:
285: /**
286: * RFC 2650 doesn't specify any critical extensions so we return true
287: * if any are encountered.
288: *
289: * @return true if any critical extensions are present.
290: */
291: public boolean hasUnsupportedCriticalExtension() {
292: Set extns = getCriticalExtensionOIDs();
293: if (extns != null && !extns.isEmpty()) {
294: return true;
295: }
296:
297: return false;
298: }
299:
300: private Set getExtensionOIDs(boolean critical) {
301: Set set = new HashSet();
302: X509Extensions extensions = this .getRequestExtensions();
303:
304: if (extensions != null) {
305: Enumeration e = extensions.oids();
306:
307: while (e.hasMoreElements()) {
308: DERObjectIdentifier oid = (DERObjectIdentifier) e
309: .nextElement();
310: X509Extension ext = extensions.getExtension(oid);
311:
312: if (critical == ext.isCritical()) {
313: set.add(oid.getId());
314: }
315: }
316: }
317:
318: return set;
319: }
320:
321: public Set getCriticalExtensionOIDs() {
322: return getExtensionOIDs(true);
323: }
324:
325: public Set getNonCriticalExtensionOIDs() {
326: return getExtensionOIDs(false);
327: }
328:
329: public byte[] getExtensionValue(String oid) {
330: X509Extensions exts = this .getRequestExtensions();
331:
332: if (exts != null) {
333: X509Extension ext = exts
334: .getExtension(new DERObjectIdentifier(oid));
335:
336: if (ext != null) {
337: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
338: DEROutputStream dOut = new DEROutputStream(bOut);
339:
340: try {
341: dOut.writeObject(ext.getValue());
342:
343: return bOut.toByteArray();
344: } catch (Exception e) {
345: throw new RuntimeException("error encoding "
346: + e.toString());
347: }
348: }
349: }
350:
351: return null;
352: }
353: }
|