1 /**
2 * LICENCIA LGPL:
3 *
4 * Esta librería es Software Libre; Usted puede redistribuirla y/o modificarla
5 * bajo los términos de la GNU Lesser General Public License (LGPL) tal y como
6 * ha sido publicada por la Free Software Foundation; o bien la versión 2.1 de
7 * la Licencia, o (a su elección) cualquier versión posterior.
8 *
9 * Esta librería se distribuye con la esperanza de que sea útil, pero SIN
10 * NINGUNA GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o
11 * ADECUACIÓN A UN PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public
12 * License (LGPL) para más detalles
13 *
14 * Usted debe recibir una copia de la GNU Lesser General Public License (LGPL)
15 * junto con esta librería; si no es así, escriba a la Free Software Foundation
16 * Inc. 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
17 * <http://www.gnu.org/licenses/>.
18 *
19 * Copyright 2011 Agencia de Tecnología y Certificación Electrónica
20 */
21 package es.accv.arangi.base.certificate.validation;
22
23 import java.util.Date;
24
25 import org.apache.log4j.Logger;
26 import org.bouncycastle.cert.ocsp.CertificateID;
27 import org.bouncycastle.cert.ocsp.RevokedStatus;
28 import org.bouncycastle.cert.ocsp.SingleResp;
29 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
30
31 import es.accv.arangi.base.ArangiObject;
32 import es.accv.arangi.base.certificate.Certificate;
33 import es.accv.arangi.base.util.validation.ValidationResult;
34
35 /**
36 * Clase que contiene cada una de las respuestas contenidas en la respuesta OCSP.
37 * Normalmente sólo habrá una, pero se puede realizar una petición para varios
38 * certificados, en cuyo caso habrá una de estas respuestas para cada uno de
39 * ellos.
40 *
41 * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
42 */
43 public class CertificateOCSPResponse extends ArangiObject {
44
45 /*
46 * Certificado de firma de la respuesta OCSP
47 */
48 Certificate signatureCertificate;
49
50 /*
51 * Respuesta para un certificado
52 */
53 SingleResp singleResponse;
54
55 /*
56 * Logger de la clase
57 */
58 Logger logger = Logger.getLogger(CertificateOCSPResponse.class);
59
60 /**
61 * Constructor
62 *
63 * @param signatureCertificate Certificado de firma de la respuesta OCSP
64 * @param singleResponse Respuesta para un certificado
65 */
66 public CertificateOCSPResponse (Certificate signatureCertificate, SingleResp singleResponse) {
67 this.signatureCertificate = signatureCertificate;
68 this.singleResponse = singleResponse;
69 }
70
71 /**
72 * Comprueba si este objeto es una respuesta para el certificado pasado como
73 * parámetro. Para ello:
74 * <ul>
75 * <li>Verifica que la respuesta se corresponde con el certificado</li>
76 * <li>Verifica que el emisor del certificado de firma de la respuesta esté
77 * en la cadena de confianza del certificado[aunque se mantiene el log
78 * que avisa de este caso sólo se tiene en cuenta la comprobación anterior
79 * para obtener el resultado del método]</li>
80 * </ul>
81 *
82 * @param certificate Certificado
83 * @return Cierto si esta respuesta coincide con el certificado
84 */
85 public boolean match (ValidateCertificate certificate) {
86 logger.debug("[CertificateOCSPResponse.match]::Entrada");
87
88 //-- Obtener el certID de la respuesta
89 CertificateID certIdResp = getCertificateID ();
90
91 //-- Obtener el certID del certificado
92 CertificateID certIdCert;
93 try {
94 certIdCert = new CertificateID(new JcaDigestCalculatorProviderBuilder().
95 setProvider(CRYPTOGRAPHIC_PROVIDER_NAME).build().get(CertificateID.HASH_SHA1),
96 certificate.getIssuerCertificate().toX509CertificateHolder(), certificate.getSerialNumberBigInteger());
97 } catch (Exception e) {
98 logger.info("[CertificateOCSPResponse.match]::No se puede obtener el certId del certificado", e);
99 return false;
100 }
101
102 //-- Comparar
103 if (certIdResp.equals(certIdCert)) {
104 ValidateCertificate[] chainCertificate = certificate.getCertificationChain();
105 for (int i = 0; i < chainCertificate.length; i++) {
106 if (this.signatureCertificate.getIssuerKeyIdentifier() != null) {
107 if (chainCertificate[i].getSubjectKeyIdentifier().equals(
108 this.signatureCertificate.getIssuerKeyIdentifier())) {
109 return true;
110 }
111 } else {
112 if (chainCertificate[i].getSubjectDN().equals(
113 this.signatureCertificate.getIssuerDN())) {
114 return true;
115 }
116 }
117 }
118 logger.warn("[CertificateOCSPResponse.match]::La respuesta OCSP se corresponde con el certificado pero el emisor del certificado " +
119 "de firma de la respuesta no se encuentra en la cadena de confianza del certificado");
120 //-- Hacemos una validación menos estricta permitiendo que respuestas firmadas con certificados fuera de la cadena de certificación
121 //return false;
122 return true;
123 } else {
124 logger.debug("[CertificateOCSPResponse.match]::La respuesta OCSP no se corresponde con el certificado");
125 return false;
126 }
127 }
128
129 /**
130 * Comprueba si este objeto es una respuesta para el certificado pasado como
131 * parámetro. Para ello verifica que la respuesta se corresponde con el certificado.
132 *
133 * @param certificate Certificado
134 * @return Cierto si esta respuesta coincide con el certificado
135 */
136 public boolean match (Certificate certificate) {
137 logger.debug("[CertificateOCSPResponse.match]::Entrada");
138
139 //-- Obtener el certID de la respuesta
140 CertificateID certIdResp = getCertificateID ();
141
142 //-- Obtener el certID del certificado
143 CertificateID certIdCert;
144 try {
145 certIdCert = OCSPClient.getCertificateID(certificate);
146 } catch (Exception e) {
147 logger.info("[CertificateOCSPResponse.match]::No se puede obtener el certId del certificado", e);
148 return false;
149 }
150
151 //-- Comparar
152 if (certIdResp.equals(certIdCert)) {
153 logger.debug("[CertificateOCSPResponse.match]::La respuesta OCSP se corresponde con el certificado");
154 return true;
155 } else {
156 logger.debug("[CertificateOCSPResponse.match]::La respuesta OCSP no se corresponde con el certificado");
157 return false;
158 }
159 }
160
161 /**
162 * Obtiene el certificateID de la respuesta. Este objeto identifica
163 * al certificado sobre el que se ha verificado el certificado y
164 * contiene su Issuer and Serial Number
165 *
166 * @return CertificateID de la respuesta
167 */
168 public CertificateID getCertificateID () {
169 return singleResponse.getCertID();
170 }
171
172 /**
173 * Obtiene la fecha a partir de la cual es válida la respuesta.
174 *
175 * @return Fecha de inicio del periodo de validez de la respuesta
176 */
177 public Date getValidityPeriodBeginning () {
178 return singleResponse.getThisUpdate();
179 }
180
181 /**
182 * Obtiene la fecha a partir de la cual ya no es válida la respuesta.
183 *
184 * @return Fecha de fin del periodo de validez de la respuesta
185 */
186 public Date getValidityPeriodEnd () {
187 return singleResponse.getNextUpdate();
188 }
189
190 /**
191 * Devuelve el estado de la respuesta, que será alguna de las siguientes constantes de
192 * la clase {@link es.accv.arangi.base.util.validation.ValidationResult ValidationResult}:<br>
193 *
194 * <ul>
195 * <li><i>RESULT_VALID</i>: El certificado es válido.</li>
196 * <li><i>RESULT_CERTIFICATE_REVOKED</i>: El certificado está revocado</li>
197 * <li><i>RESULT_CERTIFICATE_UNKNOWN</i>: El certificado es desconocido para el OCSP</li>
198 * </ul>
199 *
200 * @return Estado de la respuesta
201 */
202 public int getStatus () {
203 logger.debug("[CertificateOCSPResponse.getStatus]::Entrada");
204
205 if(singleResponse.getCertStatus()== null){
206 //-- Certificate valid
207 return ValidationResult.RESULT_VALID;
208
209 }else{
210 //-- Certificate is not valid
211 if(singleResponse.getCertStatus() instanceof RevokedStatus) {
212 //-- Certificate revoked
213 return ValidationResult.RESULT_CERTIFICATE_REVOKED;
214 }
215 //-- Certificate unknown
216 return ValidationResult.RESULT_CERTIFICATE_UNKNOWN;
217
218 }
219 }
220
221 /**
222 * Obtiene la fecha de revocación. Si se llama a este método y el estado de
223 * la respuesta no es revocado, se devolverá un valor nulo.
224 *
225 * @return Fecha de revocación o nulo si la respuesta no es de revocación
226 */
227 public Date getRevocationTime () {
228
229 if(!(singleResponse.getCertStatus() instanceof RevokedStatus)) {
230 return null;
231 }
232
233 RevokedStatus revokedStatus = (RevokedStatus) singleResponse.getCertStatus();
234
235 logger.debug("[CertificateOCSPResponse.getRevocationTime]::revocationtime=" + revokedStatus.getRevocationTime());
236
237 return revokedStatus.getRevocationTime();
238 }
239
240 /**
241 * Obtiene la razón de revocación. Si se llama a este método y el estado de
242 * la respuesta no es revocado, se devolverá un valor -1. La razón de
243 * revocación es un campo opcional, por lo que aunque el estado sea
244 * revocado este método puede devolver un valor -1.
245 *
246 * @return Razón de revocación o -1 si no existe razón de revocación o la
247 * respuesta no es de revocación
248 */
249 public int getRevocationReason () {
250
251 if(!(singleResponse.getCertStatus() instanceof RevokedStatus)) {
252 return -1;
253 }
254
255 RevokedStatus revokedStatus = (RevokedStatus) singleResponse.getCertStatus();
256 if (!revokedStatus.hasRevocationReason()) {
257 return -1;
258 }
259 return revokedStatus.getRevocationReason();
260 }
261
262 }