KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ejbca > core > protocol > ocsp > OCSPUnidClient


1 /*************************************************************************
2  * *
3  * EJBCA: The OpenSource Certificate Authority *
4  * *
5  * This software is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU Lesser General Public *
7  * License as published by the Free Software Foundation; either *
8  * version 2.1 of the License, or any later version. *
9  * *
10  * See terms of license at gnu.org. *
11  * *
12  *************************************************************************/

13
14 package org.ejbca.core.protocol.ocsp;
15
16 import java.io.ByteArrayInputStream JavaDoc;
17 import java.io.ByteArrayOutputStream JavaDoc;
18 import java.io.FileInputStream JavaDoc;
19 import java.io.FileNotFoundException JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.OutputStream JavaDoc;
23 import java.net.HttpURLConnection JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.net.URLConnection JavaDoc;
26 import java.security.GeneralSecurityException JavaDoc;
27 import java.security.KeyStore JavaDoc;
28 import java.security.KeyStoreException JavaDoc;
29 import java.security.NoSuchAlgorithmException JavaDoc;
30 import java.security.NoSuchProviderException JavaDoc;
31 import java.security.PublicKey JavaDoc;
32 import java.security.SignatureException JavaDoc;
33 import java.security.cert.Certificate JavaDoc;
34 import java.security.cert.CertificateException JavaDoc;
35 import java.security.cert.X509Certificate JavaDoc;
36 import java.util.Enumeration JavaDoc;
37 import java.util.Hashtable JavaDoc;
38
39 import javax.net.ssl.HostnameVerifier;
40 import javax.net.ssl.HttpsURLConnection;
41 import javax.net.ssl.KeyManagerFactory;
42 import javax.net.ssl.SSLContext;
43 import javax.net.ssl.SSLSession;
44 import javax.net.ssl.SSLSocketFactory;
45 import javax.net.ssl.TrustManagerFactory;
46
47 import org.bouncycastle.asn1.ASN1InputStream;
48 import org.bouncycastle.asn1.ASN1OctetString;
49 import org.bouncycastle.asn1.DEROctetString;
50 import org.bouncycastle.asn1.x509.X509Extension;
51 import org.bouncycastle.asn1.x509.X509Extensions;
52 import org.bouncycastle.ocsp.BasicOCSPResp;
53 import org.bouncycastle.ocsp.CertificateID;
54 import org.bouncycastle.ocsp.OCSPException;
55 import org.bouncycastle.ocsp.OCSPReq;
56 import org.bouncycastle.ocsp.OCSPReqGenerator;
57 import org.bouncycastle.ocsp.OCSPResp;
58 import org.bouncycastle.ocsp.RespID;
59 import org.ejbca.util.CertTools;
60 import org.ejbca.util.KeyTools;
61
62 /** A simple OCSP lookup client used to query the OCSPUnidExtension. Attributes needed to call the client is a keystore
63  * issued from the same CA as has issued the TLS server certificate of the OCSP/Lookup server.
64  * The keystore must be a PKCS#12 file.
65  * If a keystore is not used, regular OCSP requests can still be made, using normal http.
66  *
67  * If requesting an Fnr and the fnr rturned is null, even though the OCSP code is good there can be several reasons:
68  * 1.The client was not authorized to request an Fnr
69  * 2.There was no Unid Fnr mapping available
70  * 3.There was no Unid in the certificate (serialNumber DN component)
71  *
72  * @author Tomas Gustavsson, PrimeKey Solutions AB
73  * @version $Id: OCSPUnidClient.java,v 1.9 2006/10/19 21:02:08 primelars Exp $
74  *
75  */

76 public class OCSPUnidClient {
77
78     private String JavaDoc httpReqPath = null;
79     private KeyStore JavaDoc ks = null;
80     private String JavaDoc passphrase = null;
81     
82     /**
83      *
84      * @param ks KeyStore client keystore used to authenticate TLS client authentication, or null if TLS is not used
85      * @param pwd String password for the key store, or null if no keystore is used
86      * @param ocspurl String url to the OCSP server, or null if we should try to use the AIA extension from the cert; e.g. http://127.0.0.1:8080/ejbca/publicweb/status/ocsp (or https for TLS)
87      */

88     public OCSPUnidClient(KeyStore JavaDoc keystore, String JavaDoc pwd, String JavaDoc ocspurl) {
89         this.httpReqPath = ocspurl;
90         this.passphrase = pwd;
91         this.ks = keystore;
92         CertTools.installBCProvider();
93     }
94     
95     /**
96      *
97      * @param ksfilename String Filename of PKCS#12 keystore used to authenticate TLS client authentication, or null if TLS is not used
98      * @param pwd String password for the key store,or null if no keystore is used
99      * @param ocspurl String url to the OCSP server, or null if we should try to use the AIA extension from the cert; e.g. http://127.0.0.1:8080/ejbca/publicweb/status/ocsp (or https for TLS)
100      * @throws NoSuchProviderException
101      * @throws KeyStoreException
102      * @throws IOException
103      * @throws FileNotFoundException
104      * @throws CertificateException
105      * @throws NoSuchAlgorithmException
106      */

107     public OCSPUnidClient(String JavaDoc ksfilename, String JavaDoc pwd, String JavaDoc ocspurl) throws KeyStoreException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, CertificateException JavaDoc, FileNotFoundException JavaDoc, IOException JavaDoc {
108         this.httpReqPath = ocspurl;
109         this.passphrase = pwd;
110         if (ksfilename != null) {
111             ks = KeyStore.getInstance("PKCS12", "BC");
112             ks.load(new FileInputStream JavaDoc(ksfilename), pwd.toCharArray());
113         }
114         CertTools.installBCProvider();
115     }
116
117     /**
118      *
119      * @param cert X509Certificate to query, the DN should contain serialNumber which is Unid to be looked up
120      * @param cacert CA certificate that issued the certificate to be queried
121      * @param getfnr if we should ask for a Unid-Fnr mapping or only query the OCSP server
122      * @return OCSPUnidResponse conatining the response and the fnr, can contain and an error code and the fnr can be null, never returns null.
123      */

124     public OCSPUnidResponse lookup(X509Certificate JavaDoc cert, X509Certificate JavaDoc cacert, boolean getfnr) throws OCSPException, IOException JavaDoc, GeneralSecurityException JavaDoc {
125         // See if we must try to get the ocsprul from the cert
126
if (httpReqPath == null) {
127             httpReqPath = CertTools.getAuthorityInformationAccessOcspUrl(cert);
128             // If we didn't pass a url to the constructor and the cert does not have the URL, we will fail...
129
if (httpReqPath == null) {
130                 OCSPUnidResponse ret = new OCSPUnidResponse();
131                 ret.setErrorCode(OCSPUnidResponse.ERROR_NO_OCSP_URI);
132                 return ret;
133             }
134         }
135         // And an OCSP request
136
OCSPReqGenerator gen = new OCSPReqGenerator();
137         CertificateID certId = new CertificateID(CertificateID.HASH_SHA1, cacert, cert.getSerialNumber());
138 // System.out.println("Generating CertificateId:\n"
139
// + " Hash algorithm : '" + certId.getHashAlgOID() + "'\n"
140
// + " CA certificate\n"
141
// + " CA SubjectDN: '" + cacert.getSubjectDN().getName() + "'\n"
142
// + " SerialNumber: '" + cacert.getSerialNumber().toString(16) + "'\n"
143
// + " CA certificate hashes\n"
144
// + " Name hash : '" + new String(Hex.encode(certId.getIssuerNameHash())) + "'\n"
145
// + " Key hash : '" + new String(Hex.encode(certId.getIssuerKeyHash())) + "'\n");
146
gen.addRequest(certId);
147         // Don't bother adding Unid extension if we are not using client authentication
148
if (getfnr && (ks != null)) {
149             Hashtable JavaDoc exts = new Hashtable JavaDoc();
150             X509Extension ext = new X509Extension(false, new DEROctetString(new FnrFromUnidExtension("1")));
151             exts.put(FnrFromUnidExtension.FnrFromUnidOid, ext);
152             gen.setRequestExtensions(new X509Extensions(exts));
153         }
154         OCSPReq req = gen.generate();
155
156         // Send the request and receive a BasicResponse
157
OCSPUnidResponse ret = sendOCSPPost(req.getEncoded(), cacert);
158         return ret;
159     }
160
161     //
162
// Private helper methods
163
//
164

165     private OCSPUnidResponse sendOCSPPost(byte[] ocspPackage, X509Certificate JavaDoc cacert) throws IOException JavaDoc, OCSPException, GeneralSecurityException JavaDoc {
166         // POST the OCSP request
167
URL JavaDoc url = new URL JavaDoc(httpReqPath);
168         HttpURLConnection JavaDoc con = (HttpURLConnection JavaDoc)getUrlConnection(url);
169         // we are going to do a POST
170
con.setDoOutput(true);
171         con.setRequestMethod("POST");
172
173         // POST it
174
con.setRequestProperty("Content-Type", "application/ocsp-request");
175         OutputStream JavaDoc os = null;
176         try {
177             os = con.getOutputStream();
178             os.write(ocspPackage);
179         } finally {
180             if (os != null) os.close();
181         }
182         OCSPUnidResponse ret = new OCSPUnidResponse();
183         ret.setHttpReturnCode(con.getResponseCode());
184         if (ret.getHttpReturnCode() != 200) {
185             if (ret.getHttpReturnCode() == 401) {
186                 ret.setErrorCode(OCSPUnidResponse.ERROR_UNAUTHORIZED);
187             } else {
188                 ret.setErrorCode(OCSPUnidResponse.ERROR_UNKNOWN);
189             }
190             return ret;
191         }
192         ByteArrayOutputStream JavaDoc baos = null;
193         InputStream JavaDoc in = null;
194         byte[] respBytes = null;
195         try {
196             baos = new ByteArrayOutputStream JavaDoc();
197             // This works for small requests, and OCSP requests are small
198
in = con.getInputStream();
199             int b = in.read();
200             while (b != -1) {
201                 baos.write(b);
202                 b = in.read();
203             }
204             baos.flush();
205             in.close();
206             respBytes = baos.toByteArray();
207         } finally {
208             if (baos != null) baos.close();
209             if (in != null) in.close();
210         }
211         if (respBytes == null) {
212             ret.setErrorCode(OCSPUnidResponse.ERROR_NO_RESPONSE);
213             return ret;
214         }
215         OCSPResp response = new OCSPResp(new ByteArrayInputStream JavaDoc(respBytes));
216         BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
217         X509Certificate JavaDoc[] chain = brep.getCerts("BC");
218         PublicKey JavaDoc signerPub = chain[0].getPublicKey();
219         RespID respId = new RespID(signerPub);
220         if (!brep.getResponderId().equals(respId)) {
221             // Response responderId does not match signer certificate responderId!
222
ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNERID);
223         }
224         boolean verify = brep.verify(signerPub, "BC");
225         if (!verify) {
226             ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNATURE);
227             return ret;
228         }
229         // Also verify the signers certificate
230
try {
231             chain[0].verify(cacert.getPublicKey());
232         } catch (SignatureException JavaDoc e) {
233             ret.setErrorCode(OCSPUnidResponse.ERROR_INVALID_SIGNERCERT);
234             return ret;
235         }
236         ret.setResp(response);
237         String JavaDoc fnr = getFnr(brep);
238         if (fnr != null) {
239             ret.setFnr(fnr);
240         }
241         return ret;
242     }
243
244     private String JavaDoc getFnr(BasicOCSPResp brep) throws IOException JavaDoc {
245         byte[] fnrrep = brep.getExtensionValue(FnrFromUnidExtension.FnrFromUnidOid.getId());
246         if (fnrrep == null) {
247             return null;
248         }
249         ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream JavaDoc(fnrrep));
250         ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
251         aIn = new ASN1InputStream(new ByteArrayInputStream JavaDoc(octs.getOctets()));
252         FnrFromUnidExtension fnrobj = FnrFromUnidExtension.getInstance(aIn.readObject());
253         return fnrobj.getFnr();
254     }
255
256     private SSLSocketFactory getSSLFactory() throws GeneralSecurityException JavaDoc, IOException JavaDoc {
257
258         SSLContext ctx = SSLContext.getInstance("TLS");
259         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
260
261         // Put the key and certs in the user keystore
262
kmf.init(ks, passphrase.toCharArray());
263
264         // Now make a truststore to verify the server
265
KeyStore JavaDoc trustks = KeyStore.getInstance("jks");
266         trustks.load(null, "foo123".toCharArray());
267         // add trusted CA cert
268
Enumeration JavaDoc en = ks.aliases();
269         String JavaDoc alias = null;
270         // If this alias is a trusted certificate entry, we don't want to fetch that, we want the key entry
271
while ( (alias==null || ks.isCertificateEntry(alias)) && en.hasMoreElements() )
272             alias = (String JavaDoc)en.nextElement();
273         Certificate JavaDoc[] certs = KeyTools.getCertChain(ks, alias);
274         if (certs == null) {
275             throw new IOException JavaDoc("Can not find a certificate entry in PKCS12 keystore for alias "+alias);
276         }
277         trustks.setCertificateEntry("trusted", certs[certs.length-1]);
278         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
279         tmf.init(trustks);
280
281         ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
282
283         return ctx.getSocketFactory();
284     }
285
286     /**
287      *
288      * @param url
289      * @return URLConnection
290      * @throws IOException
291      * @throws GeneralSecurityException
292      */

293     private URLConnection JavaDoc getUrlConnection(URL JavaDoc url) throws IOException JavaDoc, GeneralSecurityException JavaDoc {
294         URLConnection JavaDoc orgcon = url.openConnection();
295         if (orgcon instanceof HttpsURLConnection) {
296             HttpsURLConnection con = (HttpsURLConnection) orgcon;
297             con.setHostnameVerifier(new SimpleVerifier());
298             con.setSSLSocketFactory(getSSLFactory());
299         }
300         return orgcon;
301     }
302
303     class SimpleVerifier implements HostnameVerifier {
304         public boolean verify(String JavaDoc hostname, SSLSession session) {
305             return true;
306         }
307     }
308     
309 }
310
Popular Tags