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

Last change on this file was 8379cd8, checked in by Antonio Araujo Brett <aaraujo@…>, 11 years ago

Agregado encabezado de licencia a archivos fuentes.

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