View Javadoc

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 }