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.signature;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.File;
26  import java.io.FileNotFoundException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.OutputStream;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.text.SimpleDateFormat;
33  import java.util.Arrays;
34  import java.util.Date;
35  import java.util.List;
36  import java.util.Properties;
37  import java.util.TimeZone;
38  
39  import javax.activation.DataSource;
40  import javax.mail.MessagingException;
41  import javax.mail.Multipart;
42  import javax.mail.Session;
43  import javax.mail.internet.MimeBodyPart;
44  import javax.mail.internet.MimeMessage;
45  import javax.mail.internet.MimeMultipart;
46  import javax.mail.util.SharedByteArrayInputStream;
47  
48  import org.apache.log4j.Logger;
49  import org.bouncycastle.cms.CMSException;
50  import org.bouncycastle.util.encoders.Hex;
51  
52  import es.accv.arangi.base.certificate.Certificate;
53  import es.accv.arangi.base.certificate.validation.CAList;
54  import es.accv.arangi.base.certificate.validation.CertificateOCSPResponse;
55  import es.accv.arangi.base.certificate.validation.CertificateValidationService;
56  import es.accv.arangi.base.certificate.validation.OCSPResponse;
57  import es.accv.arangi.base.document.ByteArrayDocument;
58  import es.accv.arangi.base.document.IDocument;
59  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
60  import es.accv.arangi.base.exception.certificate.validation.MalformedOCSPResponseException;
61  import es.accv.arangi.base.exception.certificate.validation.ServiceException;
62  import es.accv.arangi.base.exception.certificate.validation.ServiceNotFoundException;
63  import es.accv.arangi.base.exception.document.HashingException;
64  import es.accv.arangi.base.exception.signature.NoDocumentToSignException;
65  import es.accv.arangi.base.exception.signature.SignatureException;
66  import es.accv.arangi.base.exception.timestamp.MalformedTimeStampException;
67  import es.accv.arangi.base.signature.ISignature;
68  import es.accv.arangi.base.signature.Signature;
69  import es.accv.arangi.base.util.Util;
70  import es.accv.arangi.base.util.validation.ValidationResult;
71  import es.accv.arangi.exception.signature.ACCVWebServicesConnectionException;
72  import es.accv.arangi.exception.signature.MalformedTokenException;
73  import es.accv.arangi.timestamp.TimeStamp;
74  import es.accv.arangi.util.smime.SMIMESigned;
75  
76  /**
77   * Clase para el tratamiento de los tokens de validación de la ACCV. Estos tokens se
78   * generan y validan mediante llamadas a los servicios web de la ACCV, por lo que
79   * estas acciones requieren que la URL de los servicios web sea accesible desde donde
80   * se esté ejecutando esta clase. Además, si va a utilizar los servicios web de test, 
81   * necesitará que desde la ACCV le abran los puertos para poder acceder.<br><br>
82   * 
83   * El token de validación de la ACCV es un formato de firma longeva, ya que contiene,
84   * además de la firma del documento, un sello de tiempos y la respuesta OCSP que 
85   * garantizan que el certificado era válido en el momento de producirse la generación
86   * del token. Además toda esta información viene firmada por la propia ACCV.<br><br>
87   * 
88   * El token de validación de la ACCV es un S/MIME firmado que los servicios web de la 
89   * ACCV devuelven en formato base64.  
90   * 
91   * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
92   */
93  public class ACCVValidationToken extends Signature {
94  	
95  	/*
96  	 * Logger de la clase
97  	 */
98  	Logger logger = Logger.getLogger(ACCVValidationToken.class);
99  	
100 	/**
101 	 * URL de los servicios web de la ACCV: http://webserv.pki.gva.es:8080/axis/services/serviciospki
102 	 */
103 	public static final String URL_ACCV_WEBSERVICES 		= "http://webserv.pki.gva.es:8080/axis/services/serviciospki";
104 	
105 	/**
106 	 * URL de los servicios web de la ACCV: http://sleipnir2.pki.gva.es:8084/axis/services/serviciospki
107 	 */
108 	public static final String URL_ACCV_WEBSERVICES_TEST 	= "http://sleipnir2.pki.gva.es:8084/axis/services/serviciospki";
109 	
110 	/*
111 	 * Formateador de la fecha para el XML de validacion
112 	 */
113 	public static SimpleDateFormat VALIDATION_XML_DATE_FORMAT = new SimpleDateFormat ("yyyyMMddHHmmssz");
114 	
115 	/*
116 	 * Mensaje SMIME
117 	 */
118 	private MimeMessage token;
119 	
120 	/*
121 	 * Contenido en base64
122 	 */
123 	private byte[] tokenB64;
124 	
125 	/*
126 	 * Firma PKCS#7
127 	 */
128 	private PKCS7Signature pkcs7Signature;
129 	
130 	/*
131 	 * Firma PKCS#7 del token
132 	 */
133 	private PKCS7Signature tokenSignature;
134 	
135 	/*
136 	 * Sello de tiempos
137 	 */
138 	private TimeStamp timeStamp;
139 	
140 	/*
141 	 * Respuesta OCSP
142 	 */
143 	private OCSPResponse ocspResponse;
144 	
145 	/*
146 	 * Sello de tiempos de archivo
147 	 */
148 	private TimeStamp archiveTimeStamp;
149 	
150 	/*
151 	 * Respuesta OCSP del certificado de sello de tiempos
152 	 */
153 	private OCSPResponse tsCertificateOcspResponse;
154 	
155 	static {
156 		//-- La fecha del xml de validación se mostrará como si estuviésemos en la zona
157 		//-- horaria GMT0
158 		VALIDATION_XML_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("Etc/GMT+0"));
159 		
160 		//-- Añadir esta clase a la lista de clases reconocedoras de firma
161 		addClassToSignatureValidation();
162 	}
163 	
164 	//-- Constructores
165 	
166 	/**
167 	* Crea un Token de la Autoridad de Validación de la ACCV cargándolo desde un array de bytes
168 	* (contendrá el token en formato base64). 
169 	* 
170 	* @param bytesToken Array de bytes con el contenido del token
171 	* @throws MalformedTokenException El token no está correctamente formado
172 	*/
173 	public ACCVValidationToken(byte[] bytesToken) throws MalformedTokenException {
174 		initialize(bytesToken);
175 	}
176 
177 	/**
178 	* Crea un Token de la Autoridad de Validación de la ACCV a partir de un fichero 
179 	* (contendrá el token en formato base64). 
180 	* 
181 	* @param fileToken Fichero con el contenido del token
182 	* @throws MalformedTokenException El token no está correctamente formado
183 	* @throws FileNotFoundException  El fichero no existe o no se puede leer
184 	*/
185 	public ACCVValidationToken(File fileToken) throws MalformedTokenException, FileNotFoundException {
186 		try {
187 			initialize (Util.loadFile(fileToken));
188 		} catch (IOException e) {
189 			logger.info ("[ACCVValidationToken]::El fichero no existe o no se puede leer", e);
190 			throw new FileNotFoundException ("El fichero no existe o no se puede leer");
191 		}
192 	}
193 
194 	/**
195 	* Crea un Token de la Autoridad de Validación de la ACCV a partir de un stream
196 	* de lectura (contendrá el token en formato base64). 
197 	* 
198 	* @param isToken Stream de lectura que apunta al contenido del token
199 	* @throws MalformedTokenException El token no está correctamente formado
200 	*/
201 	public ACCVValidationToken(InputStream isToken) throws MalformedTokenException {
202 		try {
203 			initialize(Util.readStream(isToken));
204 		} catch (IOException e) {
205 			logger.info ("[ACCVValidationToken]::Ha ocurrido un error leyendo el stream de lectura", e);
206 			throw new MalformedTokenException ("Ha ocurrido un error leyendo el stream de lectura", e);
207 		}
208 	}
209 	
210 	/**
211 	 * Obtiene un token de validación conectándose a los servicios web de explotación de 
212 	 * la ACCV (({@link #URL_ACCV_WEBSERVICES URL_ACCV_WEBSERVICES})). Para obtener el 
213 	 * token se requiere una firma PKCS#7 y el documento que la originó.
214 	 * 
215 	 * @param document Documento que originó la firma PKCS#7
216 	 * @param pkcs7Signature Firma PKCS#7
217 	 * @throws HashingException No se puede obtener el hash del documento
218 	 * @throws MalformedTokenException El token obtenido no parece ser correcto
219 	 * @throws SignatureException El servicio web de la ACCV indica que la firma es
220 	 * 	incorrecta
221 	 * @throws ACCVWebServicesConnectionException No es posible conectarse y obtener
222 	 * 	una respuesta de la URL indicada
223 	 */
224 	public ACCVValidationToken (IDocument document, es.accv.arangi.base.signature.PKCS7Signature pkcs7Signature) throws HashingException, SignatureException, MalformedTokenException, ACCVWebServicesConnectionException {
225 		try {
226 			initialize(document, pkcs7Signature, new URL(ACCVValidationToken.URL_ACCV_WEBSERVICES));
227 		} catch (MalformedURLException e) {
228 			// No se va a dar
229 		}
230 	}
231 	
232 	/**
233 	 * Obtiene un token de validación conectándose a los servicios web de la ACCV (a la
234 	 * URL que se pasa como parámetro). Para obtener el token se requiere una firma PKCS#7
235 	 * y el documento que la originó.
236 	 * 
237 	 * @param document Documento que originó la firma PKCS#7
238 	 * @param pkcs7Signature Firma PKCS#7
239 	 * @param urlWebServices URL de los servicios web de la ACCV. Si la firma se
240 	 * 	realizó con un certificado de test habrá que utilizar los servicios web de
241 	 * 	test: {@link #URL_ACCV_WEBSERVICES_TEST URL_ACCV_WEBSERVICES_TEST}
242 	 * @throws HashingException No se puede obtener el hash del documento
243 	 * @throws MalformedTokenException El token obtenido no parece ser correcto
244 	 * @throws SignatureException El servicio web de la ACCV indica que la firma es
245 	 * 	incorrecta
246 	 * @throws ACCVWebServicesConnectionException No es posible conectarse y obtener
247 	 * 	una respuesta de la URL indicada
248 	 */
249 	public ACCVValidationToken (IDocument document, es.accv.arangi.base.signature.PKCS7Signature pkcs7Signature, URL urlWebServices) throws HashingException, SignatureException, MalformedTokenException, ACCVWebServicesConnectionException {
250 		initialize(document, pkcs7Signature, urlWebServices);
251 	}
252 
253 
254 	/* (non-Javadoc)
255 	 * @see es.accv.arangi.base.signature.ISignature#getCertificates()
256 	 */
257 	public Certificate[] getCertificates() {
258 		return pkcs7Signature.getCertificates();
259 	}
260 
261 	/* (non-Javadoc)
262 	 * @see es.accv.arangi.base.signature.ISignature#getDocument()
263 	 */
264 	public IDocument getDocument() {
265 		return null;
266 	}
267 
268 	/**
269 	 * El token de validación de la ACCV nunca contiene el documento firmado, por 
270 	 * lo que este método siempre lanzará una excepción {@link es.accv.arangi.base.exception.signature.NoDocumentToSignException NoDocumentToSignException}.
271 	 */
272 	public ValidationResult[] isValid(CAList arg0) throws HashingException, SignatureException, NormalizeCertificateException,
273 			NoDocumentToSignException {
274 		throw new NoDocumentToSignException ("El token de validación de la ACCV nunca contiene el documento firmado, " +
275 			"por lo que es imposible realizar esta validación");
276 	}
277 
278 	/**
279 	 * Valida el token con la información que contiene.<br><br>
280 	 * 
281 	 * No utiliza para nada los servicios de validación pasados como parámetro, 
282 	 * pero el método debía estar presente para implementar la interfaz 
283 	 * {@link es.accv.arangi.base.signature.ISignature ISignature}.
284 	 * 
285 	 * @param document Documento que originó la firma
286 	 * @param caList Lista de certificados de CA
287 	 * @return Resultado de la validación (siempre contendrá un único elemento)
288 	 */
289 	public ValidationResult[] isValid(IDocument document, CAList caList) throws HashingException, SignatureException, NormalizeCertificateException {
290 		return isValid(document);
291 	}
292 
293 	/**
294 	 * El token de validación de la ACCV nunca contiene el documento firmado, por 
295 	 * lo que este método siempre lanzará una excepción {@link es.accv.arangi.base.exception.signature.NoDocumentToSignException NoDocumentToSignException}.
296 	 */
297 	public ValidationResult[] isValid(List<CertificateValidationService> validationServices)
298 			throws HashingException, SignatureException, NormalizeCertificateException, NoDocumentToSignException {
299 		throw new NoDocumentToSignException ("El token de validación de la ACCV nunca contiene el documento firmado, " +
300 				"por lo que es imposible realizar esta validación");
301 	}
302 
303 	/**
304 	 * Valida el token con la información que contiene.<br><br>
305 	 * 
306 	 * No utiliza para nada los servicios de validación pasados como parámetro, 
307 	 * pero el método debía estar presente para implementar la interfaz 
308 	 * {@link es.accv.arangi.base.signature.ISignature ISignature}.
309 	 * 
310 	 * @param document Documento que originó la firma
311 	 * @param validationServices Lista de servicios de validación de certificados
312 	 * @return Resultado de la validación (siempre contendrá un único elemento)
313 	 */
314 	public ValidationResult[] isValid(IDocument document, List<CertificateValidationService> validationServices)
315 			throws HashingException, SignatureException, NormalizeCertificateException {
316 		return isValid(document);
317 	}
318 
319 	/**
320 	 * Valida el token realizando las siguientes comprobaciones:
321 	 * <ul>
322 	 * 	<li>Valida la firma del token.</li>
323 	 * 	<li>Valida que la firma PKCS#7 se corresponda con el documento.</li>
324 	 *  <li>Valida el sello de tiempos.</li>
325 	 *  <li>Comprueba que el sello de tiempos se realizase sobre la firma PKCS#7.</li>
326 	 *  <li>Comprueba que la fecha del sello de tiempos se encuentre dentro del
327 	 *  	periodo de validez de la respuesta OCSP.</li>
328 	 *  <li>Comprueba que la respuesta OCSP se corresponde con el certificado
329 	 *  	de la firma PKCS#7.</li>
330 	 *  <li>Comprueba que la respuesta OCSP es una respuesta de validez del
331 	 *  	certificado.</li>
332 	 * </ul>
333 	 * 
334 	 * @param document Documento que originó la firma
335 	 * @return Resultado de la validación (siempre contendrá un único elemento)
336 	 * @throws HashingException Error al hacer el hash del documento o del PKCS#7 
337 	 * 	contenido en el token
338 	 * @throws SignatureException Error comprobando la firma PKCS#7
339 	 */
340 	public ValidationResult[] isValid (IDocument document) throws HashingException, SignatureException, NormalizeCertificateException {
341 		return isValidWithHash(document.getHash());
342 	}
343 	
344 	/**
345 	 * Valida el token realizando las siguientes comprobaciones:
346 	 * <ul>
347 	 * 	<li>Valida la firma del token.</li>
348 	 * 	<li>Valida que la firma PKCS#7 se corresponda con el hash del documento.</li>
349 	 *  <li>Comprueba que la fecha del sello de tiempos se encuentre dentro del
350 	 *  	periodo de validez de la respuesta OCSP.</li>
351 	 *  <li>Comprueba que la respuesta OCSP se corresponde con el certificado
352 	 *  	de la firma PKCS#7.</li>
353 	 *  <li>Comprueba que la respuesta OCSP es una respuesta de validez del
354 	 *  	certificado.</li>
355 	 * </ul>
356 	 * 
357 	 * @param hash Hash del documento que originó la firma
358 	 * @return Resultado de la validación (siempre contendrá un único elemento)
359 	 * @throws HashingException Error al hacer el hash del PKCS#7 contenido en el token
360 	 * @throws SignatureException Error comprobando la firma PKCS#7
361 	 */
362 	public ValidationResult[] isValidWithHash (byte[] hash) throws SignatureException {
363 		logger.debug("[ACCVValidationToken.isValidWithHash]::Entrada::" + Arrays.asList(new Object[] { hash }));
364 		
365 		//-- Obtener el certificado del PKCS#7 (hará falta para devolver el resultado)
366 		Certificate certificate = null;
367 		if (getCertificates() != null && getCertificates().length > 0) {
368 			certificate = getCertificates()[0];
369 		}
370 				
371 		//-- Validar la firma del S-MIME
372 		try {
373 			SMIMESigned smimeSigned = new SMIMESigned((MimeMultipart) token.getContent());
374 	        ByteArrayOutputStream baos = new ByteArrayOutputStream();
375 	        smimeSigned.getContent().writeTo(baos);
376 	        ByteArrayDocument tokenDocument = new ByteArrayDocument(baos.toByteArray());
377 			ValidationResult[] validationResults = this.tokenSignature.isValidSignatureOnly(tokenDocument);
378 			for (int i = 0; i < validationResults.length; i++) {
379 				if (!validationResults[i].isValid()) {
380 					logger.debug("[ACCVValidationToken.isValidWithHash]::La firma del token no se corresponde con el contenido del mismo: " + validationResults[i].getResultText());
381 					return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_INVALID, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
382 				}
383 			}
384 		} catch (Exception e1) {
385 			logger.debug("[ACCVValidationToken.isValidWithHash]::No es posible validar la firma del token");
386 			return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_INVALID, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
387 		}
388 		
389 		//-- Verificar que la firma se corresponde con el documento
390 		ValidationResult[] validationResults = this.pkcs7Signature.isValidSignatureOnlyWithHash(hash);
391 		for (int i = 0; i < validationResults.length; i++) {
392 			if (!validationResults[i].isValid()) {
393 				logger.debug("[ACCVValidationToken.isValidWithHash]::El PKCS#7 no se corresponde con el documento: " + validationResults[i].getResultText());
394 				return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_SIGNATURE_NOT_MATCH_DATA, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
395 			}
396 		}
397 		
398 		//-- Validar el sello de tiempos
399 		boolean timeStampValid;
400 		try {
401 			timeStampValid = this.timeStamp.isValid();
402 		} catch (MalformedTimeStampException e) {
403 			logger.debug("[ACCVValidationToken.isValidWithHash]::El sello de tiempos se halla mal formado", e);
404 			return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_INVALID_TIMESTAMP, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
405 		}
406 		if (!timeStampValid) {
407 			//-- Los tokens antiguos siempre tienen mal el sello, no se validará
408 			logger.debug("[ACCVValidationToken.isValidWithHash]::El sello de tiempos no es válido");
409 		}
410 		
411 		//-- Fecha del sello de tiempos
412 		Date tsDate = this.timeStamp.getTime();
413 		
414 		//-- Comprobar que la fecha del sello de tiempos está dentro del periodo de
415 		//-- Validez de la respuesta OCSP
416 		CertificateOCSPResponse[] ocspResponses = this.ocspResponse.getSingleResponses();
417 		for(int i=0;i<ocspResponses.length;i++) {
418 			if (ocspResponses[i].getValidityPeriodBeginning().after(tsDate) ||
419 					ocspResponses[i].getValidityPeriodEnd().before(tsDate)) {
420 				logger.debug("[ACCVValidationToken.isValidWithHash]::La fecha del sello de tiempos no se encuentra en el periodo de validez de la respuesta OCSP");
421 				return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_TIMESTAMP_AFTER_VALIDITY_ITEM, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
422 			}
423 		}
424 		
425 		//-- Comprobar que la respuesta OCSP se corresponde con el certificado del PKCS#7
426 		for(int i=0;i<ocspResponses.length;i++) {
427 			if (!ocspResponses[i].match(certificate)) {
428 				logger.debug("[ACCVValidationToken.isValidWithHash]::La respuesta OCSP del token no se corresponde con el certificado");
429 				return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_INVALID_VALIDITY_ITEM, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
430 			}
431 		}
432 		
433 		//-- Comprobar que la respuesta OCSP es de validez
434 		for(int i=0;i<ocspResponses.length;i++) {
435 			if (ocspResponses[i].getStatus() != ValidationResult.RESULT_VALID) {
436 				logger.debug("[ACCVValidationToken.isValidWithHash]::La respuesta OCSP del token no es una respuesta válida: " + ocspResponses[i].getStatus());
437 				return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_INVALID_VALIDITY_ITEM, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
438 			}
439 		}
440 		
441 		//-- Todo OK
442 		logger.debug("[ACCVValidationToken.isValidWithHash]::TOKEN VÁLIDO");
443 		return new ValidationResult[] {new ValidationResult(ValidationResult.RESULT_VALID, certificate.toX509Certificate(), null, getTimeStamp(), new OCSPResponse[] { getOcspResponse() }) };
444 	}
445 	
446 	/**
447 	 * Valida el token realizando las siguientes comprobaciones:
448 	 * <ul>
449 	 * 	<li>Valida la firma del token.</li>
450 	 * 	<li>Valida que la firma PKCS#7 se corresponda con el hash del documento.</li>
451 	 *  <li>Comprueba que la fecha del sello de tiempos se encuentre dentro del
452 	 *  	periodo de validez de la respuesta OCSP.</li>
453 	 *  <li>Comprueba que la respuesta OCSP se corresponde con el certificado
454 	 *  	de la firma PKCS#7.</li>
455 	 *  <li>Comprueba que la respuesta OCSP es una respuesta de validez del
456 	 *  	certificado.</li>
457 	 * </ul><br><br>
458 	 * 
459 	 * Obtiene un XML que contendrá el resultado:
460 	 * <ul>
461 	 * 	<li>APLICACION: si aparece este tag es que la firma se ha podido validar</li>
462 	 *  <li>ERROR: se ha producido un error durante la validación o alguno de los
463 	 *  	elementos del token no es válido</li>
464 	 *  <li>FIRMA: si indica "Fallo al verificar la firma" es porque el documento
465 	 *  	no se corresponde con la firma. En caso contrario es que el token es
466 	 *  	valido y se muestra: FIRMANTE y CERTIFICADO</li>
467 	 * </ul><br><br>
468 	 * 
469 	 * El resultad de este método es el mismo de llamar al método <code>getVerifyToken_hash</code>
470 	 * de los servicios web de validación de la ACCV.
471 	 * 
472 	 * @param hash Hash del documento que originó la firma
473 	 * @throws HashingException Error al hacer el hash del PKCS#7 contenido en el token
474 	 * @throws SignatureException Error comprobando la firma PKCS#7
475 	 */
476 	public String getValidationXML (byte[] hash) {
477 		logger.debug("[ACCVValidationToken.getValidationXML]::Entrada::" + Arrays.asList(new Object[] { hash }));
478 		
479 		//-- Obtener el certificado del PKCS#7 (hará falta para devolver el resultado)
480 		Certificate certificate = null;
481 		if (getCertificates() != null && getCertificates().length > 0) {
482 			certificate = getCertificates()[0];
483 		}
484 				
485 		//-- Validar la firma del S-MIME
486 		try {
487 			SMIMESigned smimeSigned = new SMIMESigned((MimeMultipart) token.getContent());
488 	        ByteArrayOutputStream baos = new ByteArrayOutputStream();
489 	        smimeSigned.getContent().writeTo(baos);
490 	        ByteArrayDocument tokenDocument = new ByteArrayDocument(baos.toByteArray());
491 			ValidationResult[] validationResults = this.tokenSignature.isValidSignatureOnly(tokenDocument);
492 			for (int i = 0; i < validationResults.length; i++) {
493 				if (!validationResults[i].isValid()) {
494 					logger.debug("[ACCVValidationToken.getValidationXML]::La firma del token no se corresponde con el contenido del mismo: " + validationResults[i].getResultText());
495 					return getXMLValidacionError();
496 				}
497 			}
498 		} catch (Exception e) {
499 			logger.debug("[ACCVValidationToken.getValidationXML]::No es posible validar la firma del token", e);
500 			return getXMLValidacionError();
501 		}
502 		
503 		//-- Verificar que la firma se corresponde con el documento
504 		ValidationResult[] validationResults;
505 		try {
506 			validationResults = this.pkcs7Signature.isValidSignatureOnlyWithHash(hash);
507 		} catch (SignatureException e1) {
508 			logger.debug("[ACCVValidationToken.getValidationXML]::Error validando el PKCS#7");
509 			return getXMLFirmaDocumentoNoMatch();
510 		}
511 		for (int i = 0; i < validationResults.length; i++) {
512 			if (!validationResults[i].isValid()) {
513 				logger.debug("[ACCVValidationToken.getValidationXML]::El PKCS#7 no se corresponde con el documento: " + validationResults[i].getResultText());
514 				return getXMLFirmaDocumentoNoMatch();
515 			}
516 		}
517 		
518 		//-- Validar el sello de tiempos
519 		String textoTS = null;
520 		try {
521 			if (!this.timeStamp.isValid()) {
522 				//-- Los tokens antiguos siempre tienen mal el sello, no se validará
523 				logger.debug("[ACCVValidationToken.getValidationXML]::El sello de tiempos no es válido");
524 			}
525 		} catch (MalformedTimeStampException e) {
526 			logger.debug("[ACCVValidationToken.getValidationXML]::El sello de tiempos se halla mal formado", e);
527 			textoTS = "Fallo al verificar el sello de tiempos";
528 		}
529 		
530 		//-- Fecha del sello de tiempos
531 		Date tsDate = this.timeStamp.getTime();
532 		
533 		//-- Comprobar que la fecha del sello de tiempos está dentro del periodo de
534 		//-- Validez de la respuesta OCSP
535 		String textoOCSP = null;
536 		CertificateOCSPResponse[] ocspResponses = this.ocspResponse.getSingleResponses();
537 		for(int i=0;i<ocspResponses.length;i++) {
538 			if (ocspResponses[i].getValidityPeriodBeginning().after(tsDate) ||
539 					ocspResponses[i].getValidityPeriodEnd().before(tsDate)) {
540 				logger.debug("[ACCVValidationToken.getValidationXML]::La fecha del sello de tiempos no se encuentra en el periodo de validez de la respuesta OCSP");
541 				textoOCSP = "No es valida en la fecha del sello";
542 			}
543 		}
544 		
545 		//-- Comprobar que la respuesta OCSP se corresponde con el certificado del PKCS#7
546 		for(int i=0;i<ocspResponses.length;i++) {
547 			if (!ocspResponses[i].match(certificate)) {
548 				logger.debug("[ACCVValidationToken.getValidationXML]::La respuesta OCSP del token no se corresponde con el certificado");
549 				textoOCSP = "No valida";
550 			}
551 		}
552 		
553 		//-- Comprobar que la respuesta OCSP es de validez
554 		for(int i=0;i<ocspResponses.length;i++) {
555 			if (ocspResponses[i].getStatus() != ValidationResult.RESULT_VALID) {
556 				logger.debug("[ACCVValidationToken.getValidationXML]::La respuesta OCSP del token no es una respuesta válida: " + ocspResponses[i].getStatus());
557 				textoOCSP = "No valida";
558 			}
559 		}
560 		
561 		//-- Resultado
562 		logger.debug("[ACCVValidationToken.getValidationXML]::TOKEN VÁLIDO");
563 		return getXMLFirmaOk (textoTS, textoOCSP, certificate, tsDate);
564 	}
565 	
566 	/**
567 	 * Método que se conecta a la URL de los servicios web de la ACCV (pasada como
568 	 * parámetro) para validar el token de validación.
569 	 * 
570 	 * @param document Documento que originó la firma del token
571 	 * @param urlWebServices URL de los servicios web de la ACCV. Si la firma se
572 	 * 	realizó con un certificado de test habrá que utilizar los servicios web de
573 	 * 	test: {@link #URL_ACCV_WEBSERVICES_TEST URL_ACCV_WEBSERVICES_TEST}
574 	 * @return Cierto si el token es válido
575 	 * @throws HashingException No se puede obtener el hash del documento
576 	 * @throws ACCVWebServicesConnectionException No es posible conectarse y obtener
577 	 * 	una respuesta de la URL indicada
578 	 */
579 	public boolean isValid (IDocument document, URL urlWebServices) throws HashingException, ACCVWebServicesConnectionException {
580 		logger.debug("[ACCVValidationToken.isValid]::Entrada::" + document);
581 		
582 		//-- Construir el mensaje SOAP
583 		String message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
584 			"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
585 			"<soapenv:Body><ns1:getEstadoToken_hash soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:serviciospki\">" +
586 			"<in0 xsi:type=\"xsd:string\">" + new String (Hex.encode(document.getHash())) + "</in0>" +
587 			"<in1 xsi:type=\"xsd:string\">" + new String (this.tokenB64) + "</in1>" +
588 			"</ns1:getEstadoToken_hash></soapenv:Body></soapenv:Envelope>";
589 		
590 		try {
591 			
592 			StringBuffer sb = Util.sendPost(message, urlWebServices);
593 			
594 			//-- Evaluar la respuesta
595 			if (sb.indexOf("-1") > -1) {
596 				return false;
597 			} else {
598 				return true;
599 			}
600 			
601 		} catch (ServiceNotFoundException e) {
602 			logger.info("[ACCVValidationToken.isValid]::Error de conexión en la URL '" + urlWebServices + "'", e);
603 			throw new ACCVWebServicesConnectionException ("Error de conexión en la URL '" + urlWebServices + "'", e);
604 		} catch (ServiceException e) {
605 			logger.info("[ACCVValidationToken.isValid]::No se puede obtener una respuesta correcta de la URL '" + urlWebServices + "'", e);
606 			throw new ACCVWebServicesConnectionException ("No se puede obtener una respuesta correcta de la URL '" + urlWebServices + "'", e);
607 		}
608 
609 	}
610 	
611 	/**
612 	 * El token de validación de la ACCV nunca contiene el documento firmado, por 
613 	 * lo que este método siempre lanzará una excepción {@link es.accv.arangi.base.exception.signature.NoDocumentToSignException NoDocumentToSignException}.
614 	 */
615 	public ValidationResult[] isValidSignatureOnly() throws HashingException, SignatureException, NoDocumentToSignException {
616 		throw new NoDocumentToSignException ("El token de validación de la ACCV nunca contiene el documento firmado, " +
617 				"por lo que es imposible realizar esta validación");
618 	}
619 
620 	/**
621 	 * Sólo comprueba que la firma PKCS#7 contenida es correcta
622 	 */
623 	public ValidationResult[] isValidSignatureOnly(IDocument document) throws HashingException, SignatureException {
624 		return pkcs7Signature.isValidSignatureOnly(document);
625 	}
626 
627 //	
628 // Haría falta hacer el resellado en servidor ya que al final hay que volver a firmar el SMIME para que siga siendo válido
629 //	
630 //	public void addArchiveTimeStamp () throws OCSPException, OCSPValidateException, MalformedTokenException, 
631 //		CertificateCANotFoundException, NormalizeCertificateException, MalformedTimeStampException, 
632 //		ResponseTimeStampException, HashingException, TimeStampServerConnectionException {
633 //		
634 //		logger.debug("[ACCVValidationToken.addArchiveTimeStamp]::Entrada");
635 //		
636 //		//-- Obtener las partes del smime
637 //		Multipart multipart;
638 //		try {
639 //			SMIMESigned smimeSigned = new SMIMESigned((MimeMultipart) token.getContent());
640 //			multipart = (Multipart) smimeSigned.getContent().getContent();
641 //		} catch (Exception e) {
642 //			logger.info("[ACCVValidationToken.addArchiveTimeStamp]::No se ha podido leer el SMIME", e);
643 //			throw new MalformedTokenException ("No se ha podido leer el SMIME", e);
644 //		} 	
645 //		
646 //		//-- Obtener el certificado del sello de tiempos
647 //		ValidateCertificate tsCertificate = new ValidateCertificate (timeStamp.getSignatureCertificate().toX509Certificate(), 
648 //				ArangiUtil.getACCVCaList());
649 //		if (tsCertificate.validate() != ValidationResult.RESULT_VALID) {
650 //			logger.error ("El certificado del sello ya no es válido: \n" + tsCertificate);
651 //			throw new OCSPValidateException("El certificado del sello ya no es válido: \n" + tsCertificate);
652 //		}
653 //		
654 //		//-- Obtener la respuesta ocsp para el certificado
655 //		logger.debug("[ACCVValidationToken.addArchiveTimeStamp]::Obtener la respuesta ocsp para el certificado");
656 //		OCSPResponse ocspResponse = null;
657 //		for (OCSPClient ocspClient : tsCertificate.getOCSPClients()) {
658 //			try {
659 //				ocspResponse = ocspClient.getOCSPResponse(tsCertificate);
660 //				break;
661 //			} catch (Exception e) {
662 //				logger.debug("No se puede validar contra el OCSP: " + ocspClient.getURL(), e);
663 //			} 
664 //		}
665 //		
666 //		if (ocspResponse == null || ocspResponse.getSingleResponses().length == 0) {
667 //			//-- No se ha podido obtener la respuesta
668 //			logger.error ("No es posible obtener la respuesta OCSP para el certificado: \n" + tsCertificate);
669 //			throw new OCSPException("No es posible obtener la respuesta OCSP para el certificado");
670 //		}
671 //		if (ocspResponse.getSingleResponses()[0].getStatus() != ValidationResult.RESULT_VALID) {
672 //			//-- El certificado del sello ya no es válido
673 //			logger.error ("El certificado del sello ya no es válido: \n" + tsCertificate);
674 //			throw new OCSPValidateException("El certificado del sello ya no es válido: \n" + tsCertificate);
675 //		}
676 //
677 //		try {
678 //			//-- Crear el mimepart
679 //			logger.debug("[ACCVValidationToken.addArchiveTimeStamp]::Crear el mimepart para la respuesta ocsp del certificado del sello");
680 //			BodyPart mimeBodyPart = new MimeBodyPart();
681 //		    mimeBodyPart.setFileName("OCSPTS");
682 //		    mimeBodyPart.setDataHandler(new DataHandler(new TokenDataSource(ocspResponse.toDER(), "application/octet-stream", "OCSPTS")));
683 //		    mimeBodyPart.addHeader("Content-Transfer-Encoding", "base64");
684 //		    multipart.addBodyPart(mimeBodyPart);
685 //		} catch (Exception e) {
686 //			logger.info("[ACCVValidationToken.addArchiveTimeStamp]::No se han podido añadir las nuevas partes del SMIME", e);
687 //			throw new MalformedTokenException ("No se han podido añadir las nuevas partes del SMIME", e);
688 //		} 	
689 //		    
690 //	    //-- Recoger todo y sellar
691 //		logger.debug("[ACCVValidationToken.addArchiveTimeStamp]::Obtener el sello de tiempos de archivo");
692 //	    InputStream is;
693 //		try {
694 //			is = token.getInputStream();
695 //		} catch (Exception e) {
696 //			logger.info("[ACCVValidationToken.addArchiveTimeStamp]::No se han podido obtener el stream de lectura del SMIME", e);
697 //			throw new MalformedTokenException ("No se han podido obtener el stream de lectura del SMIME", e);
698 //		} 
699 //	    TimeStamp archiveTs = TimeStamp.stampDocument(is);
700 //			
701 //		try {
702 //		    logger.debug("[ACCVValidationToken.addArchiveTimeStamp]::Crear el mimepart con el sello de tiempos de archivo");
703 //		    BodyPart mimeBodyPart = new MimeBodyPart();
704 //		    mimeBodyPart.setFileName("TSSARCHIVO");
705 //		    mimeBodyPart.setDataHandler(new DataHandler(new TokenDataSource(archiveTs.toDER(), "application/octet-stream", "TSSARCHIVO")));
706 //		    mimeBodyPart.addHeader("Content-Transfer-Encoding", "base64");
707 //		    multipart.addBodyPart(mimeBodyPart);
708 //		} catch (Exception e) {
709 //			logger.info("[ACCVValidationToken.addArchiveTimeStamp]::No se han podido añadir las nuevas partes del SMIME", e);
710 //			throw new MalformedTokenException ("No se han podido añadir las nuevas partes del SMIME", e);
711 //		} 	
712 //	    
713 //	    //-- Cargar objetos
714 //	    this.archiveTimeStamp = archiveTs;
715 //	    this.tsCertificateOcspResponse = ocspResponse;
716 //	    try {
717 //			this.tokenB64 = Util.encodeBase64(this.token.getInputStream()).getBytes();
718 //		} catch (Exception e) {
719 //			logger.info("[ACCVValidationToken.addArchiveTimeStamp]::No se han podido obtener el stream de lectura del SMIME", e);
720 //			throw new MalformedTokenException ("No se han podido obtener el stream de lectura del SMIME", e);
721 //		} 
722 //	}
723 	
724 	/**
725 	 * Obtiene el sello de tiempos del token de validación
726 	 * 
727 	 * @return Sello de tiempos del token de validación
728 	 */
729 	public TimeStamp getTimeStamp() {
730 		return timeStamp;
731 	}
732 
733 	/**
734 	 * Obtiene la respuesta OCSP del token de validación
735 	 * 
736 	 * @return Respuesta OCSP del token de validación
737 	 */
738 	public OCSPResponse getOcspResponse() {
739 		return ocspResponse;
740 	}
741 
742 	/**
743 	 * Obtiene la firma PKCS#7 contenida en el token de validación
744 	 * 
745 	 * @return Firma PKCS#7 contenida en el token de validación
746 	 */
747 	public PKCS7Signature getPkcs7Signature() {
748 		return pkcs7Signature;
749 	}
750 
751 	/**
752 	 * Guarda la firma en disco
753 	 * 
754 	 * @param file Fichero donde se guardará la firma
755 	 * @throws IOException Errores de entrada / salida
756 	 */
757 	public void save (File file) throws IOException {
758 		logger.debug ("[CMSPKCS7Signature.save]::Entrada::" + file);
759 		
760 		Util.saveFile(file, this.tokenB64);
761 	}
762 	
763 	/**
764 	 * Guarda la firma en un stream de escritura.
765 	 * 
766 	 * @param out Stream de escritura
767 	 * @throws IOException Errores de entrada / salida
768 	 */
769 	public void save (OutputStream out) throws IOException {
770 		logger.debug ("[CMSPKCS7Signature.save]::Entrada::" + out);
771 		
772 		Util.save(out, this.tokenB64);
773 	}
774 	
775 	/**
776 	 * Obtiene una respuesta de error para el método getValidationXML
777 	 */
778 	public static String getXMLValidacionError() {
779 		return "<?xml version=\"1.0\"?><ERROR><FIRMA>No se ha podido verificar la firma de la aplicacion.</FIRMA></ERROR>";
780 	}
781 
782 	/**
783 	 * Método para poder validar con el método Signature.validateSignature.<br><br>
784 	 * 
785 	 * Analiza el parámetro y, si se trata de un token de validación
786 	 * ACCV, devuelve un objeto de este tipo.
787 	 * 
788 	 * @param bSignature Firma como array de bytes
789 	 * @return Token de validación ACCV
790 	 * @throws Exception El parámetro no es un token de validación ACCV
791 	 */
792 	public static ISignature getSignatureInstance (byte[] bSignature) throws Exception {
793 		return new ACCVValidationToken(bSignature);
794 	}
795 	
796 	/**
797 	 * Añade esta clase a la lista de clases reconocedoras de firmas. A partir
798 	 * de este momento, si se usan las instrucciones Signature.validateSignature
799 	 * se tendrá en cuenta que la firma puede ser un token de validación de la ACCV.
800 	 */
801 	public static void addClassToSignatureValidation () {
802 		Signature.addRecognizerClass(ACCVValidationToken.class);
803 	}
804 
805 	/**
806 	 * Obtiene el token de validación de la ACCV en base64
807 	 * 
808 	 * @return Token de validación
809 	 */
810 	public byte[] toByteArray() {
811 		return this.tokenB64;
812 	}
813 
814 	public String getSignatureType() {
815 		return "Token Validación ACCV";
816 	}
817 
818 	//-- Métodos privados
819 	
820 	/*
821 	 * Inicializa este objeto
822 	 */
823 	private void initialize(byte[] bytesToken) throws MalformedTokenException {
824 		
825 		logger.debug("[ACCVValidationToken.initialize]::Entrada::" + bytesToken);
826 		
827 		//-- Pasar de base64
828 		ByteArrayInputStream bais = new ByteArrayInputStream (Util.decodeBase64(new String (bytesToken)));
829 		
830 		try {
831 			
832 			//-- Nos creamos el objeto sesión.
833 			Properties properties = new Properties();
834 			Session session = Session.getDefaultInstance(properties, null);
835 		    
836 			//-- Obtenemos el mensaje	
837 			token = new MimeMessage(session, bais);
838 			SMIMESigned smimeSigned = new SMIMESigned((MimeMultipart) token.getContent());
839 			Multipart multiPart = (Multipart) smimeSigned.getContent().getContent();
840 			if (multiPart.getCount() != 3) {
841 				throw new SignatureException("El token contiene " + multiPart.getCount() + " multiparts en lugar de 3");
842 			}
843 			
844 			//-- Procesamos la firma del usuario
845 			MimeBodyPart partFirma = (MimeBodyPart) multiPart.getBodyPart(0);
846 			SharedByteArrayInputStream sbais = (SharedByteArrayInputStream)partFirma.getContent();
847 			String pkcs7SignatureB64 = new String(Util.readStream(sbais));			
848 			pkcs7Signature = new PKCS7Signature (Util.decodeBase64(pkcs7SignatureB64));
849 	
850 			//-- Procesamos el OCSP
851 			MimeBodyPart partOCSP = (MimeBodyPart) multiPart.getBodyPart(1);
852 			com.sun.mail.util.BASE64DecoderStream decoderStream = (com.sun.mail.util.BASE64DecoderStream)partOCSP.getContent();
853 		    byte[] abRet = new byte[decoderStream.available()];
854 		    decoderStream.read(abRet);
855 			ocspResponse = new OCSPResponse(abRet);
856       
857 			//-- Procesamos el Token TSS	
858 			MimeBodyPart partTSS = (MimeBodyPart) multiPart.getBodyPart(2);
859 			decoderStream = (com.sun.mail.util.BASE64DecoderStream)partTSS.getContent();
860 		    abRet = new byte[decoderStream.available()];
861 		    decoderStream.read(abRet);
862 			timeStamp = new TimeStamp (abRet);		
863 			
864 			//-- Procesamos la firma del token
865 			tokenSignature = new PKCS7Signature (smimeSigned.getEncoded());
866 			
867 			//-- Guardar el mensaje en su campo
868 			tokenB64 = bytesToken;
869 			
870 		} catch (MessagingException e) {
871 			logger.info("[ACCVValidationToken.initialize]::El token no es un SMIME correcto", e);
872 			throw new MalformedTokenException ("El token no es un SMIME correcto", e);
873 		} catch (IOException e) {
874 			logger.info("[ACCVValidationToken.initialize]::No se puede leer alguna de las partes del token", e);
875 			throw new MalformedTokenException ("No se puede leer alguna de las partes del token", e);
876 		} catch (CMSException e) {
877 			logger.info("[ACCVValidationToken.initialize]::No se ha encontrado la firma del SMIME o ésta no es correcta", e);
878 			throw new MalformedTokenException ("No se ha encontrado la firma del SMIME o ésta no es correcta", e);
879 		} catch (NormalizeCertificateException e) {
880 			logger.info("[ACCVValidationToken.initialize]::El certificado de la firma PKCS#7 no puede ser tratado por el proveedor criptográfico de Arangi", e);
881 			throw new MalformedTokenException ("El certificado de la firma PKCS#7 no puede ser tratado por el proveedor criptográfico de Arangi", e);
882 		} catch (MalformedOCSPResponseException e) {
883 			logger.info("[ACCVValidationToken.initialize]::La respuesta OCSP del token no está bien construída", e);
884 			throw new MalformedTokenException ("La respuesta OCSP del token no está bien construída", e);
885 		} catch (MalformedTimeStampException e) {
886 			logger.info("[ACCVValidationToken.initialize]::El sello de tiempos del token no está bien construído", e);
887 			throw new MalformedTokenException ("El sello de tiempos del token no está bien construído", e);
888 		} catch (SignatureException e) {
889 			logger.info("[ACCVValidationToken.initialize]::Error obteniendo los certificados del PKCS#7", e);
890 			throw new MalformedTokenException ("Error obteniendo los certificados del PKCS#7", e);
891 		} catch (Exception e) {
892 			logger.info("[ACCVValidationToken.initialize]::La firma no parece un token de validación de la ACCV en base64", e);
893 			throw new MalformedTokenException ("La firma no parece un token de validación de la ACCV en base64", e);
894 		}
895 		
896 	}
897 	
898 	/*
899 	 * Obtiene un token de validación conectándose a los servicios web de la ACCV (a la
900 	 * URL que se pasa como parámetro). Para obtener el token se requiere una firma PKCS#7
901 	 * y el documento que la originó.
902 	 * 
903 	 * @param document Documento que originó la firma PKCS#7
904 	 * @param pkcs7Signature Firma PKCS#7
905 	 * @param urlWebServices URL de los servicios web de la ACCV. Si la firma se
906 	 * 	realizó con un certificado de test habrá que utilizar los servicios web de
907 	 * 	test: {@link URL_ACCV_WEBSERVICES_TEST URL_ACCV_WEBSERVICES_TEST}
908 	 * @throws HashingException No se puede obtener el hash del documento
909 	 * @throws MalformedTokenException El token obtenido no parece ser correcto
910 	 * @throws SignatureException El servicio web de la ACCV indica que la firma es
911 	 * 	incorrecta
912 	 * @throws ACCVWebServicesConnectionException No es posible conectarse y obtener
913 	 * 	una respuesta de la URL indicada
914 	 */
915 	private void initialize (IDocument document, es.accv.arangi.base.signature.PKCS7Signature pkcs7Signature, URL urlWebServices) throws HashingException, SignatureException, MalformedTokenException, ACCVWebServicesConnectionException {
916 		logger.debug("[ACCVValidationToken.initialize]::Entrada::" + document);
917 		
918 		//-- Construir el mensaje SOAP
919 		String message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
920 			"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
921 			"<soapenv:Body><ns1:getToken_hash soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:serviciospki\">" +
922 			"<in0 xsi:type=\"xsd:string\">" + new String (Hex.encode(document.getHash())) + "</in0>" +
923 			"<in1 xsi:type=\"xsd:string\">" + Util.encodeBase64(pkcs7Signature.toByteArray()) + "</in1>" +
924 			"</ns1:getToken_hash></soapenv:Body></soapenv:Envelope>";
925 		
926 		StringBuffer sb;
927 		try {
928 			sb = Util.sendPost(message, urlWebServices);
929 			logger.debug("[ACCVValidationToken.initialize]::Response::" + sb.toString());
930 			
931 		} catch (ServiceNotFoundException e) {
932 			logger.info("[ACCVValidationToken.isValid]::Error de conexión en la URL '" + urlWebServices + "'", e);
933 			throw new ACCVWebServicesConnectionException ("Error de conexión en la URL '" + urlWebServices + "'", e);
934 		} catch (ServiceException e) {
935 			logger.info("[ACCVValidationToken.isValid]::No se puede obtener una respuesta correcta de la URL '" + urlWebServices + "'", e);
936 			throw new ACCVWebServicesConnectionException ("No se puede obtener una respuesta correcta de la URL '" + urlWebServices + "'", e);
937 		}
938 
939 		//-- Evaluar la respuesta
940 		if (sb.indexOf("getToken_hashReturn") > -1) {
941 			
942 			String finalMessage = sb.substring(sb.indexOf("getToken_hashReturn"));
943 			finalMessage = finalMessage.substring(finalMessage.indexOf(">") + 1);
944 			finalMessage = finalMessage.substring(0, finalMessage.indexOf("<"));
945 			if (finalMessage.equals("Fallo al verificar la firma.")) {
946 				logger.info("[ACCVValidationToken.initialize]::Fallo al verificar la firma");
947 				throw new SignatureException ("El servicio web de la ACCV devuelve un fallo al verificar la firma");
948 			}
949 			if (finalMessage.equals("Imposible comprobar el Certificado")) {
950 				logger.info("[ACCVValidationToken.initialize]::Imposible comprobar el Certificado");
951 				throw new SignatureException ("El servicio web de la ACCV devuelve que es imposible comprobar el certificado");
952 			}
953 			
954 			initialize(finalMessage.getBytes());
955 		}
956 	}
957 
958 	/*
959 	 * Obtiene una respuesta de que el documento no hace match con la firma
960 	 * para el método getValidationXML
961 	 */
962 	private static String getXMLFirmaDocumentoNoMatch() {
963 		return "<?xml version=\"1.0\"?><APLICACION>Se ha verificado la firma de la aplicacion</APLICACION><FIRMA>Fallo al verificar la firma</FIRMA>";
964 	}
965 
966 	/*
967 	 * Obtiene una respuesta válida o no válida
968 	 */
969 	private static String getXMLFirmaOk(String textoTS,
970 			String textoOCSP, Certificate certificate, Date tsDate) {
971 		return "<?xml version=\"1.0\"?><APLICACION>Se ha verificado la firma de la aplicacion</APLICACION>" +
972 				"<FIRMA>Resultado de la firma original<FIRMANTE>" + certificate.getSubjectDN().replaceAll("SERIALNUMBER=", "SN=") + "</FIRMANTE>" +
973 				"<CERTIFICADO>" + (textoOCSP != null ? "Certificado no Valido" : "Certificado Valido") + 
974 				"</CERTIFICADO></FIRMA><OCSP>" + (textoOCSP != null ? textoOCSP : "Valido") + 
975 				"</OCSP><TSS>" + (textoTS != null ? textoTS : VALIDATION_XML_DATE_FORMAT.format(tsDate)) + "</TSS>";
976 	}
977 
978 	//-- Clases
979 	
980 	/**
981 	 * Clase para añadir a cada MimeBodyPart su contenido 
982 	 */
983 	public class TokenDataSource implements DataSource {
984 		
985 		byte[] contenido;
986 		String contentType;
987 		String name;
988 		
989 		public TokenDataSource (byte[] contenido, String contentType, String name) {
990 			this.contenido = contenido;
991 			this.contentType = contentType;
992 			this.name = name;
993 		}
994 
995 		public String getContentType() {
996 			return this.contentType;
997 		}
998 
999 		public InputStream getInputStream() throws IOException {
1000 			return new ByteArrayInputStream(this.contenido);
1001 		}
1002 
1003 		public String getName() {
1004 			return this.name;
1005 		}
1006 
1007 		public OutputStream getOutputStream() throws IOException {
1008 			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1009 			return baos;
1010 		}
1011 		
1012 	}
1013 
1014 }