source: dispositivos_moviles/TibisayMovil/src/ve/gob/cenditel/tibisaymovil/CertificateUtils.java @ 42e7061

Last change on this file since 42e7061 was 288126d, checked in by Jose Ruiz <joseruiz@…>, 11 years ago

Manejo de repositorio de certificados y firma con pkcs7

  • Property mode set to 100644
File size: 11.8 KB
Line 
1package ve.gob.cenditel.tibisaymovil;
2
3import java.io.BufferedOutputStream;
4import java.io.DataOutputStream;
5import java.io.EOFException;
6import java.io.IOException;
7import java.io.InputStream;
8import java.math.BigInteger;
9import java.net.HttpURLConnection;
10import java.net.URL;
11import java.security.Security;
12import java.security.cert.CertificateEncodingException;
13import java.security.cert.CertificateException;
14import java.security.cert.X509Certificate;
15
16import org.spongycastle.asn1.DEROctetString;
17import org.spongycastle.asn1.ocsp.OCSPObjectIdentifiers;
18import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
19import org.spongycastle.asn1.x509.AccessDescription;
20import org.spongycastle.asn1.x509.AlgorithmIdentifier;
21import org.spongycastle.asn1.x509.AuthorityInformationAccess;
22import org.spongycastle.asn1.x509.Extension;
23import org.spongycastle.asn1.x509.Extensions;
24import org.spongycastle.asn1.x509.X509Extension;
25import org.spongycastle.cert.CertException;
26import org.spongycastle.cert.X509CertificateHolder;
27import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
28import org.spongycastle.cert.jcajce.JcaX509CertificateHolder;
29import org.spongycastle.cert.ocsp.BasicOCSPResp;
30import org.spongycastle.cert.ocsp.CertificateID;
31import org.spongycastle.cert.ocsp.CertificateStatus;
32import org.spongycastle.cert.ocsp.OCSPException;
33import org.spongycastle.cert.ocsp.OCSPReq;
34import org.spongycastle.cert.ocsp.OCSPReqBuilder;
35import org.spongycastle.cert.ocsp.OCSPResp;
36import org.spongycastle.cert.ocsp.OCSPRespBuilder;
37import org.spongycastle.jce.provider.BouncyCastleProvider;
38import org.spongycastle.operator.ContentVerifierProvider;
39import org.spongycastle.operator.DigestCalculator;
40import org.spongycastle.operator.OperatorCreationException;
41import org.spongycastle.operator.bc.BcDigestCalculatorProvider;
42import org.spongycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
43
44/**
45 * Utilities to deal with X509 certificates.
46 *
47 * @author José M. Prieto (jmprieto@emergya.com)
48 */
49public class CertificateUtils {
50
51    static {
52      Security.addProvider(new BouncyCastleProvider());
53    }
54
55    /**
56     * Default OCSP responder.
57     *
58     * @see #checkWithOCSP(X509Certificate, X509Certificate)
59     */
60    private static final String DEFAULT_OCSP_RESPONDER = "http://publicador-psc.fii.gob.ve:2560/ocsp/";
61
62    /**
63     * Validates the status of a certificate with an OCSP responder, according to RFC 2560. The Online Certificate
64     * Status Protocol (OCSP) enables applications to determine the (revocation) state of an identified certificate.
65     * Uses the responder specified in the corresponding certificate extension if any or a default one.
66     *
67     * @param certificate the certificate to be validated
68     * @param issuerCertificate the certificate of the issuer authority
69     * @param responderUrl the URL of the OCSP responder service to be used
70     * @return true if the certificate is known by the responder and good, false if it is unknown or revoked
71     * @throws CertificateException on errors when dealing with certificates
72     * @throws IOException on I/O errors
73     * @throws OCSPException on OCSP protocol errors
74     */
75    public static ValidationResult checkWithOCSP(X509Certificate certificate, X509Certificate issuerCertificate)
76    throws CertificateException, IOException, OCSPException {
77
78        String responderUrl = DEFAULT_OCSP_RESPONDER;
79        X509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
80        Extension extension = holder.getExtension(X509Extension.authorityInfoAccess);
81        if (extension != null) {
82            AuthorityInformationAccess authority = AuthorityInformationAccess.getInstance(extension.getParsedValue());
83            AccessDescription[] descriptions = authority.getAccessDescriptions();
84            for (AccessDescription description : descriptions) {
85                if (AccessDescription.id_ad_ocsp.equals(description.getAccessMethod())) {
86                    responderUrl = description.getAccessLocation().getName().toString();
87                }
88            }
89        }
90        return checkWithOCSP(certificate, issuerCertificate, responderUrl);
91    }
92
93    public static String getSubject(X509Certificate cert) {
94
95        return cert.getSubjectDN().getName();
96    }
97
98    public static String getSerial(X509Certificate cert) {
99
100        return cert.getSerialNumber().toString();
101    }
102
103    public static String getIssuer(X509Certificate cert) {
104
105        return cert.getIssuerDN().getName();
106    }
107
108    /**
109     * Validates the status of a certificate with the specified OCSP responder, according to RFC 2560.
110     *
111     * @param certificate the certificate to be validated
112     * @param issuerCertificate the certificate of the issuer authority
113     * @param responderUrl the URL of the OCSP responder service to be used
114     * @return true if the certificate is known by the responder and good, false if it is unknown or revoked
115     * @throws CertificateException on errors when dealing with certificates
116     * @throws IOException on I/O errors
117     * @throws OCSPException on OCSP protocol errors
118     */
119    static ValidationResult checkWithOCSP(X509Certificate certificate, X509Certificate issuerCertificate, String responderUrl)
120    throws CertificateException, IOException, OCSPException {
121
122        try {
123            OCSPReq request = buildOCSPRequest(certificate, issuerCertificate);
124
125            URL url = new URL(responderUrl);
126            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
127            connection.setRequestProperty("Content-Type", "application/ocsp-request");
128            connection.setRequestProperty("Accept", "application/ocsp-response");
129            connection.setDoOutput(true);
130            DataOutputStream output = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream()));
131            output.write(request.getEncoded());
132            output.flush();
133            output.close();
134
135            int statusCode = connection.getResponseCode();
136            if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
137                return checkWithOCSP(certificate, issuerCertificate, connection.getHeaderField("Location"));
138            } else if (statusCode < 200 || statusCode >= 300) {
139                throw new IOException();
140            }
141
142            return isOCSPResponseValid(new OCSPResp(
143                    getBytesFromStream((InputStream) connection.getContent(), connection.getContentLength())),
144                    issuerCertificate);
145        } catch (OperatorCreationException e) {
146                //throw new RuntimeException(e);
147             throw new CertificateException(e);
148        } catch (CertException e) {
149                //throw new RuntimeException(e);
150            throw new CertificateException(e);
151        }
152    }
153
154    /**
155     * Checks a response received from the OCSP service.
156     *
157     * @param response the OCSP service response
158     * @param issuerCertificate X509Certificate of the certificate being checked
159     * @return true if the certificate is known by the responder and good, false if it is unknown or revoked
160     * @throws CertificateException on errors when dealing with certificates
161     * @throws IOException on I/O errors
162     * @throws OCSPException on OCSP protocol errors
163     * @throws OperatorCreationException on errors from the crypto library (hopefully never)
164     * @throws CertException on certificate handling errors
165     */
166    private static ValidationResult isOCSPResponseValid(OCSPResp response, X509Certificate issuerCertificate)
167    throws IOException, OCSPException, OperatorCreationException, CertificateException, CertException {
168
169        if (response.getStatus() != OCSPRespBuilder.SUCCESSFUL) {
170            throw new IOException();
171        }
172
173        BasicOCSPResp validations = (BasicOCSPResp) response.getResponseObject();
174        assert validations.getResponses().length == 1;
175
176        CertificateStatus status = validations.getResponses()[0].getCertStatus();
177        X509CertificateHolder signer = validations.getCerts()[0];
178        ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().build(signer);
179        //return (status == null) && validations.isSignatureValid(verifier) && isValidSigner(signer, issuerCertificate);
180   
181        return new ValidationResult(status, validations.isSignatureValid(verifier), isValidSigner(signer, issuerCertificate));
182   
183    }
184   
185   
186   
187
188    /**
189     * Check if the OCSP response has been signed by a legitimate authority. From the RFC:
190     *
191     * All definitive response messages SHALL be digitally signed. The key
192     * used to sign the response MUST belong to one of the following:
193     *
194     * -- the CA who issued the certificate in question
195     * -- a Trusted Responder whose public key is trusted by the requester
196     * -- a CA Designated Responder (Authorized Responder) who holds a
197     *    specially marked certificate issued directly by the CA, indicating
198     *    that the responder may issue OCSP responses for that CA
199     *
200     * @param signer
201     * @param issuerCertificate
202     * @return true if the signer is valid. false otherwise
203     * @throws CertificateException on certificate handling errors
204     * @throws CertException if the signature cannot be processed or is inappropriate
205     * @throws OperatorCreationException on errors from the crypto library (hopefully never)
206     */
207    private static boolean isValidSigner(X509CertificateHolder signer, X509Certificate issuerCertificate)
208    throws CertificateException, CertException, OperatorCreationException {
209
210        X509Certificate signerCertificate = new JcaX509CertificateConverter().getCertificate(signer);
211        ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().build(issuerCertificate);
212        return signerCertificate.equals(issuerCertificate) || signer.isSignatureValid(verifier);
213    }
214
215    /**
216     * Build a request to validate a certificate with an OCSP responder.
217     *
218     * @param certificate the certificate to be validated
219     * @param issuerCertificate the certificate of the issuer authority
220     * @return the request object
221     * @throws CertificateException on errors when dealing with certificates
222     * @throws OCSPException on OCSP protocol errors
223     * @throws OperatorCreationException on errors from the crypto library (hopefully never)
224     */
225    private static OCSPReq buildOCSPRequest(X509Certificate certificate, X509Certificate issuerCertificate)
226    throws OperatorCreationException, CertificateEncodingException, OCSPException {
227
228        DigestCalculator sha1;
229        sha1 = new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
230        X509CertificateHolder issuer = new JcaX509CertificateHolder(issuerCertificate);
231        CertificateID id = new CertificateID(sha1, issuer, certificate.getSerialNumber());
232        OCSPReqBuilder gen = new OCSPReqBuilder();
233        BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
234        Extension extension = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(
235                nonce.toByteArray()));
236
237        gen.addRequest(id);
238        gen.setRequestExtensions(new Extensions(new Extension[] { extension }));
239        OCSPReq request = gen.build();
240        return request;
241    }
242
243    /**
244     * Read data from a stream.
245     *
246     * @param stream the stream to be read
247     * @param length number of bytes to read
248     * @return the data read
249     * @throws EOFException if the file has less than length bytes available to read
250     * @throws IOException on I/O errors
251     */
252    private static byte[] getBytesFromStream(InputStream stream, int length) throws IOException {
253
254        byte[] bytes = new byte[length];
255        int numRead = 0;
256
257        for (int offset = 0; offset < length; offset += (numRead = stream.read(bytes, offset, length - offset))) {
258            if (numRead < 0) {
259                throw new EOFException();
260            }
261        }
262
263        stream.close();
264        return bytes;
265    }
266}
Note: See TracBrowser for help on using the repository browser.