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.timestamp;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.DataInputStream;
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.FileNotFoundException;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.OutputStream;
32  import java.math.BigInteger;
33  import java.net.HttpURLConnection;
34  import java.net.URL;
35  import java.security.NoSuchAlgorithmException;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  import java.util.Date;
39  import java.util.Hashtable;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Map;
43  
44  import org.apache.log4j.Logger;
45  import org.bouncycastle.asn1.ASN1Encoding;
46  import org.bouncycastle.asn1.ASN1ObjectIdentifier;
47  import org.bouncycastle.asn1.ASN1Sequence;
48  import org.bouncycastle.asn1.cmp.PKIFailureInfo;
49  import org.bouncycastle.asn1.cmp.PKIFreeText;
50  import org.bouncycastle.asn1.cmp.PKIStatus;
51  import org.bouncycastle.asn1.cmp.PKIStatusInfo;
52  import org.bouncycastle.asn1.tsp.TimeStampResp;
53  import org.bouncycastle.cert.X509CertificateHolder;
54  import org.bouncycastle.cms.CMSException;
55  import org.bouncycastle.cms.CMSSignatureAlgorithmNameGenerator;
56  import org.bouncycastle.cms.CMSSignedData;
57  import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
58  import org.bouncycastle.cms.SignerInformationVerifier;
59  import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
60  import org.bouncycastle.operator.DigestCalculatorProvider;
61  import org.bouncycastle.operator.OperatorCreationException;
62  import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
63  import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
64  import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
65  import org.bouncycastle.tsp.TSPException;
66  import org.bouncycastle.tsp.TSPValidationException;
67  import org.bouncycastle.tsp.TimeStampRequest;
68  import org.bouncycastle.tsp.TimeStampRequestGenerator;
69  import org.bouncycastle.tsp.TimeStampResponse;
70  import org.bouncycastle.tsp.TimeStampToken;
71  import org.bouncycastle.util.Store;
72  
73  import es.accv.arangi.base.ArangiObject;
74  import es.accv.arangi.base.algorithm.HashingAlgorithm;
75  import es.accv.arangi.base.certificate.Certificate;
76  import es.accv.arangi.base.document.ByteArrayDocument;
77  import es.accv.arangi.base.document.Document;
78  import es.accv.arangi.base.document.FileDocument;
79  import es.accv.arangi.base.document.InputStreamDocument;
80  import es.accv.arangi.base.document.URLDocument;
81  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
82  import es.accv.arangi.base.exception.document.HashingException;
83  import es.accv.arangi.base.exception.document.InitDocumentException;
84  import es.accv.arangi.base.exception.timestamp.MalformedTimeStampException;
85  import es.accv.arangi.base.exception.timestamp.ResponseTimeStampException;
86  import es.accv.arangi.base.exception.timestamp.TimeStampServerConnectionException;
87  import es.accv.arangi.base.util.Util;
88  
89  /**
90   * Clase para trabajar con sellos de tiempo según la 
91   * <a href="http://tools.ietf.org/rfc/rfc3161.txt" target="rfc">RFC-3161</a>.<br><br>
92   * 
93   * Si lo único que se quiere es obtener la hora actual es mejor utilizar la clase
94   * {@link es.accv.arangi.base.util.time.Time Time}.<br><br>
95   * 
96   * NOTA: En la clase se utilizan indistintamente los términos <i>servidor de sello de 
97   * tiempos</i> y <i>TSA - Time Stamp Authority</i><br><br>
98   * 
99   * Un ejemplo de uso sería: <br><br>
100  * 
101  * <code>
102  * 	byte[] dataToStamp = "data to stamp".getBytes();<br>
103  * 	TimeStamp timeStamp = TimeStamp.stampDocument (dataToStamp, new URL ("http://server/tsa"));<br><br>
104  * 
105  * 	//guardar sello<br>
106  * 	Util.saveFile(new File ("/sellos/sello.ts"), timeStamp.toDER());<br><br>
107  * 
108  * 	//cargar el sello<br>
109  *  TimeStamp timeStamp2 = new TimeStamp (new File ("/sellos/sello.ts"));
110  * </code><br><br>
111  * 
112  * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
113  */
114 public class TimeStamp extends ArangiObject implements Comparable<TimeStamp>{
115 
116 	/*
117 	 * Logger de la clase
118 	 */
119 	static Logger logger = Logger.getLogger(TimeStamp.class);
120 	
121 	/*
122 	 * Sello de tiempo 
123 	 */
124 	private TimeStampToken timeStamp;
125 	
126 	//-- Constructores
127 	
128 	/**
129 	 * Constructor en base a un objeto sello de tiempos de Bouncy Castle
130 	 * 
131 	 * @param timeStamp Sello de tiempos de Bouncy Castle
132 	 */
133 	public TimeStamp (TimeStampToken timeStamp) {
134 		logger.debug("[TimeStamp(File)]::Entrada::" + timeStamp);
135 		
136 		this.timeStamp = timeStamp;
137 	}
138 	
139 	/**
140 	 * Constructor en base a un fichero que contiene un sello de tiempo.
141 	 * 
142 	 * @param fileTimeStamp Fichero que contiene un objeto sello de tiempo
143 	 * @throws MalformedTimeStampException El objeto contenido en el stream de lectura no parece
144 	 * 	ser un sello de tiempo
145 	 * @throws FileNotFoundException El fichero no existe
146 	 */
147 	public TimeStamp (File fileTimeStamp) throws MalformedTimeStampException, FileNotFoundException {
148 		logger.debug("[TimeStamp(File)]::Entrada::" + fileTimeStamp);
149 		
150 		FileInputStream fis = null;
151 		try {
152 			//-- Obtener stream de lectura al fichero
153 			fis = new FileInputStream (fileTimeStamp);
154 			
155 			//-- Inicializar objeto
156 			initialize (fis);
157 		} catch (FileNotFoundException e) {
158 			logger.info("[TimeStamp(File)]::No se ha podido encontrar el fichero " + fileTimeStamp.getAbsolutePath(), e);
159 			throw e;
160 		} finally {
161 			if (fis != null) {
162 				try {
163 					fis.close();
164 				} catch (IOException e) {
165 					logger.info("[TimeStamp(File)]::No se ha podido cerrar el stream de lectura al fichero " + fileTimeStamp.getAbsolutePath());
166 				}
167 			}
168 		}
169 	}
170 	
171 	/**
172 	 * Constructor en base a un stream de lectura que contiene el sello de tiempo.
173 	 * 
174 	 * @param isTimeStamp Stream de lectura a un objeto sello de tiempo
175 	 * @throws MalformedTimeStampException El objeto contenido en el stream de lectura no parece
176 	 * 	ser un sello de tiempo
177 	 */
178 	public TimeStamp (InputStream isTimeStamp) throws MalformedTimeStampException {
179 		logger.debug("[TimeStamp(InputStream)]::Entrada::" + isTimeStamp);
180 		
181 		//-- Inicializar objeto
182 		initialize (isTimeStamp);
183 	}
184 	
185 	/**
186 	 * Constructor en base a un array de bytes que contiene un sello de tiempo.
187 	 * 
188 	 * @param bytesTimeStamp Contenido de un objeto sello de tiempo
189 	 * @throws MalformedTimeStampException El objeto contenido en el stream de lectura no parece
190 	 * 	ser un sello de tiempo
191 	 */
192 	public TimeStamp (byte[] bytesTimeStamp) throws MalformedTimeStampException {
193 		logger.debug("[TimeStamp(byte[])]::Entrada::" + bytesTimeStamp);
194 		
195 		ByteArrayInputStream bais = null;
196 		try {
197 			//-- Obtener stream de lectura 
198 			bais = new ByteArrayInputStream (bytesTimeStamp);
199 			
200 			//-- Inicializar objeto
201 			initialize (bais);
202 		} finally {
203 			if (bais != null) {
204 				try {
205 					bais.close();
206 				} catch (IOException e) {
207 					logger.info("[TimeStamp(byte[])]::No se ha podido cerrar el stream de lectura");
208 				}
209 			}
210 		}
211 	}
212 	
213 	//-- Métodos públicos
214 	
215 	/**
216 	 * Devuelve la fecha y la hora en la que se estampó el sello de tiempo.
217 	 * 
218 	 */
219 	public Date getTime () {
220 		logger.debug("[TimeStamp.getTime]::Entrada");
221 		
222 		return timeStamp.getTimeStampInfo().getGenTime();
223 	}
224 	
225 	/**
226 	 * Devuelve el nonce o null si no existe 
227 	 */
228 	public BigInteger getNonce () {
229 		logger.debug("[TimeStamp.getNonce]::Entrada");
230 		
231 		return timeStamp.getTimeStampInfo().getNonce();
232 	}
233 	
234 	/**
235 	 * Obtiene el sello en formato DER
236 	 * 
237 	 * @return Array de bytes con el sello de tiempos
238 	 */
239 	public byte[] toDER () {
240 		logger.debug("[TimeStamp.toDER]::Entrada");
241 		
242 		try {
243 			return timeStamp.getEncoded();
244 		} catch (IOException e) {
245 			// No se dará
246 			return null;
247 		}
248 	}
249 	
250 	/**
251 	 * Obtiene el hash que fue sellado por la TSA
252 	 * 
253 	 * @return Hash sellado por la TSA
254 	 */
255 	public byte[] getHash () {
256 		logger.debug("[TimeStamp.getDataStamped]::Entrada");
257 		
258 		return timeStamp.getTimeStampInfo().getMessageImprintDigest();
259 	}
260 	
261 	/**
262 	 * Obtiene el OID del algoritmo de hashing con el que fue obtenido el hash que 
263 	 * selló la TSA
264 	 * 
265 	 * @return OID del algoritmo de hashing con el que se creó el hash sellado por la TSA
266 	 */
267 	public String getHashAlgorithm () {
268 		logger.debug("[TimeStamp.getHashAlgorithm]::Entrada");
269 		
270 		return timeStamp.getTimeStampInfo().getMessageImprintAlgOID().getId();
271 	}
272 	
273 	/**
274 	 * Obtiene el algoritmo de hashing con el que fue obtenido el hash que 
275 	 * selló la TSA
276 	 * 
277 	 * @return Nombre del algoritmo de hashing con el que se creó el hash sellado por la TSA
278 	 * @throws NoSuchAlgorithmException El algoritmo no existe en Arangí
279 	 */
280 	public String getHashAlgorithmName () throws NoSuchAlgorithmException {
281 		logger.debug("[TimeStamp.getHashAlgorithmName]::Entrada");
282 		
283 		return HashingAlgorithm.getAlgorithmName(timeStamp.getTimeStampInfo().getMessageImprintAlgOID().getId());
284 	}
285 	
286 	/**
287 	 * Obtiene el OID de la política con la que se realizó el firmado
288 	 * 
289 	 * @return OID de la política
290 	 */
291 	public String getPolicyOID () {
292 		logger.debug("[TimeStamp.getPolicyOID]::Entrada");
293 		
294 		return timeStamp.getTimeStampInfo().getPolicy().getId();
295 	}
296 	
297 	/**
298 	 * Obtiene el campo TSA
299 	 * 
300 	 * @return TSA
301 	 */
302 	public String getTSA () {
303 		logger.debug("[TimeStamp.getTSA]::Entrada");
304 		if (timeStamp.getTimeStampInfo() == null || timeStamp.getTimeStampInfo().getTsa() == null) {
305 			return null;
306 		}
307 		
308 		return timeStamp.getTimeStampInfo().getTsa().toString();
309 	}
310 	
311 	/**
312 	 * Método que obtiene el certificado de la TSA: el certificado con el que está
313 	 * firmado el sello de tiempos.
314 	 * 
315 	 * @return Certificado de la TSA
316 	 * @throws MalformedTimeStampException El sello de tiempos no está bien formado y no es posible
317 	 * 	obtener el certificado de la TSA
318 	 */
319 	public Certificate getSignatureCertificate () throws MalformedTimeStampException {
320 		logger.debug("[TimeStamp.getSignatureCertificate]::Entrada");
321 		
322 		Store certs = timeStamp.getCertificates();
323 
324 		Iterator<X509CertificateHolder> iterator1 = certs.getMatches(timeStamp.getSID()).iterator();
325 		Iterator<X509CertificateHolder> iterator2 = certs.getMatches(timeStamp.getSID()).iterator();
326 		
327 		List<String> lIssuers = new ArrayList<String>();
328 		
329 		while (iterator1.hasNext()) {
330 			try {
331 				Certificate certificate = new Certificate (iterator1.next());
332 				lIssuers.add(certificate.getIssuerDN());
333 			} catch (NormalizeCertificateException e) {
334 	    		logger.info("[TimeStamp.getSignatureCertificate]::El certificado contenido en el sello de tiempos no puede " +
335 	    				"ser normalizado de acuerdo al proveedor criptográfico de Arangi", e);
336 			}
337 		}
338 		
339 		while (iterator2.hasNext()) {
340 			try {
341 				Certificate certificate = new Certificate (iterator2.next());
342 				if (!lIssuers.contains(certificate.getSubjectDN())) {
343 					logger.debug("[TimeStamp.getSignatureCertificate]::Encontrado certificado de sello: " + certificate.getCommonName());
344 					return certificate;
345 				}
346 			} catch (NormalizeCertificateException e) {
347 	    		logger.info("[TimeStamp.getSignatureCertificate]::El certificado contenido en el sello de tiempos no puede " +
348 	    				"ser normalizado de acuerdo al proveedor criptográfico de Arangi", e);
349 			}
350 		}
351 		
352 		throw new MalformedTimeStampException ("No se ha encontrado un certificado normalizable de sello de" +
353 				" tiempos en el sello de tiempos");
354 
355 	}
356 	
357 	/**
358 	 * Método que comprueba que el sello de tiempos sea correcto, lo que implica
359 	 * las siguientes validaciones:
360 	 * <ul>
361 	 * 	<li>El certificado de la firma está correctamente definido dentro de la 
362 	 * 	firma del sello.</li>
363 	 * 	<li>El certificado de la firma se encontraba dentro de su periodo de validez 
364 	 * 	en el momento en que se realizó el sello.</li>
365 	 * 	<li>El certificado de la firma posee el uso extendido de clave necesario 
366 	 * 	para ser un certificado de TSA: KeyPurposeId.id_kp_timeStamping.</li>
367 	 * 	<li>La firma fue creada efectivamente con el certificado indicado</li>
368 	 * 	<li>La firma es un CMS bien formado</li>
369 	 * 	<li>La firma es correcta</li>
370 	 * </ul>
371 	 * 
372 	 * @return Cierto si la firma del sello de tiempos es correcta
373 	 * @throws MalformedTimeStampException Error realizando las comprobaciones
374 	 */
375 	public boolean isValid () throws MalformedTimeStampException {
376 		logger.debug("[TimeStamp.isValid]::Entrada::" + timeStamp);
377 		
378 		//-- Obtener el certificado de firma
379 		Certificate certificate = getSignatureCertificate();
380 		logger.debug("Certificado timestamp: " + certificate);
381 		
382 		//-- Validar firma
383 		try {
384 			JcaContentVerifierProviderBuilder qaz = new JcaContentVerifierProviderBuilder(); 
385 			CMSSignatureAlgorithmNameGenerator sang = new DefaultCMSSignatureAlgorithmNameGenerator(); 
386 			SignatureAlgorithmIdentifierFinder saif = new DefaultSignatureAlgorithmIdentifierFinder();
387 			DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().build();
388 			SignerInformationVerifier siv = new SignerInformationVerifier(sang, saif, qaz.build(certificate.toX509Certificate()), dcp);
389 			timeStamp.validate(siv);
390 		} catch (TSPValidationException e) {
391     		logger.debug("[TimeStamp.isValid]::La firma no es válida", e);
392 			return false;
393 		} catch (TSPException e) {
394     		logger.debug("[TimeStamp.isValid]::El CMS de la firma no es correcto", e);
395 			return false;
396 		} catch (OperatorCreationException e) {
397     		logger.debug("[TimeStamp.isValid]::No es posible obtener el validador del certificado de firma de la TSA", e);
398 			return false;
399 		}
400 		
401 		//-- Si no hay excepciones es que todo va bien
402 		return true;
403 	}
404 	
405 	/**
406 	 * Método estático que comprueba que el sello de tiempos pasado como parámetro
407 	 * sea correcto, lo que implica las siguientes validaciones:
408 	 * <ul>
409 	 * 	<li>El certificado de la firma está correctamente definido dentro de la 
410 	 * 	firma del sello.</li>
411 	 * 	<li>El certificado de la firma se encontraba dentro de su periodo de validez 
412 	 * 	en el momento en que se realizó el sello.</li>
413 	 * 	<li>El certificado de la firma posee el uso extendido de clave necesario 
414 	 * 	para ser un certificado de TSA: KeyPurposeId.id_kp_timeStamping.</li>
415 	 * 	<li>La firma fue creada efectivamente con el certificado indicado</li>
416 	 * 	<li>La firma es un CMS bien formado</li>
417 	 * 	<li>La firma es correcta</li>
418 	 * </ul>
419 	 * 
420 	 * @return Cierto si la firma del sello de tiempos es correcta
421 	 * @throws MalformedTimeStampException Error realizando las comprobaciones
422 	 */
423 	public static boolean validate (TimeStamp timeStamp) throws MalformedTimeStampException {
424 		return timeStamp.isValid ();
425 	}
426 	
427 	//-- Implementación de Comparable
428 	
429 	public int compareTo(TimeStamp ts) {
430 		return getTime().compareTo(ts.getTime());
431 	}
432 	
433 	//-- Métodos estáticos
434 	
435 	/**
436 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
437 	 * 
438 	 * @param file Fichero con el documento cuyo contenido se desea sellar
439 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
440 	 * @param parameters Parámetros de la llamada al sello de tiempos
441 	 * @throws FileNotFoundException El fichero no existe
442 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
443 	 * 	de tiempos
444 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
445 	 * 	sello de tiempos
446 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
447 	 * @throws HashingException Error obteniendo el hash
448 	 * @throws InitDocumentException El fichero es nulo o no existe
449 	 */
450 	public static TimeStamp stampDocument (File file, URL serverTimeStampURL, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
451 		FileDocument document = new FileDocument(file);
452 		try {
453 			return stampHash (document.getHash(), serverTimeStampURL, HashingAlgorithm.getDefault(), parameters);
454 		} catch (NoSuchAlgorithmException e) {
455 			// No se va a dar porque el algoritmo de hashing por defecto si existe
456 			return null;
457 		}
458 	}
459 
460 	/**
461 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
462 	 * 
463 	 * @param file Fichero con el documento cuyo contenido se desea sellar
464 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
465 	 * @throws FileNotFoundException El fichero no existe
466 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
467 	 * 	de tiempos
468 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
469 	 * 	sello de tiempos
470 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
471 	 * @throws HashingException Error obteniendo el hash
472 	 * @throws InitDocumentException El fichero es nulo o no existe
473 	 */
474 	public static TimeStamp stampDocument (File file, URL serverTimeStampURL) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
475 		FileDocument document = new FileDocument(file);
476 		return TimeStamp.stampHash(document.getHash(), serverTimeStampURL);
477 	}
478 
479 	/**
480 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
481 	 * 
482 	 * @param bDocument Contenido del documento que se desea sellar
483 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
484 	 * @param parameters Parámetros de la llamada al sello de tiempos
485 	 * @throws FileNotFoundException El fichero no existe
486 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
487 	 * 	de tiempos
488 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
489 	 * 	sello de tiempos
490 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
491 	 * @throws HashingException Error obteniendo el hash
492 	 */
493 	public static TimeStamp stampDocument (byte[] bDocument, URL serverTimeStampURL, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
494 		ByteArrayDocument document = new ByteArrayDocument(bDocument);
495 		try {
496 			return stampHash (document.getHash(), serverTimeStampURL, HashingAlgorithm.getDefault(), parameters);
497 		} catch (NoSuchAlgorithmException e) {
498 			// No se va a dar porque el algoritmo de hashing por defecto si existe
499 			return null;
500 		}
501 	}
502 
503 	/**
504 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
505 	 * 
506 	 * @param bDocument Contenido del documento que se desea sellar
507 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
508 	 * @throws FileNotFoundException El fichero no existe
509 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
510 	 * 	de tiempos
511 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
512 	 * 	sello de tiempos
513 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
514 	 * @throws HashingException Error obteniendo el hash
515 	 */
516 	public static TimeStamp stampDocument (byte[] bDocument, URL serverTimeStampURL) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
517 		ByteArrayDocument document = new ByteArrayDocument(bDocument);
518 		return TimeStamp.stampHash(document.getHash(), serverTimeStampURL);
519 	}
520 
521 	/**
522 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
523 	 * 
524 	 * @param stream Stream de lectura al contenido del documento que se desea sellar
525 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
526 	 * @param parameters Parámetros de la llamada al sello de tiempos
527 	 * @throws FileNotFoundException El fichero no existe
528 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
529 	 * 	de tiempos
530 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
531 	 * 	sello de tiempos
532 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
533 	 * @throws HashingException Error obteniendo el hash
534 	 */
535 	public static TimeStamp stampDocument (InputStream stream, URL serverTimeStampURL, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
536 		InputStreamDocument document = new InputStreamDocument(stream);
537 		try {
538 			return stampHash (document.getHash(), serverTimeStampURL, HashingAlgorithm.getDefault(), parameters);
539 		} catch (NoSuchAlgorithmException e) {
540 			// No se va a dar porque el algoritmo de hashing por defecto si existe
541 			return null;
542 		}
543 	}
544 
545 	/**
546 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
547 	 * 
548 	 * @param stream Stream de lectura al contenido del documento que se desea sellar
549 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
550 	 * @throws FileNotFoundException El fichero no existe
551 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
552 	 * 	de tiempos
553 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
554 	 * 	sello de tiempos
555 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
556 	 * @throws HashingException Error obteniendo el hash
557 	 */
558 	public static TimeStamp stampDocument (InputStream stream, URL serverTimeStampURL) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
559 		InputStreamDocument document = new InputStreamDocument(stream);
560 		return TimeStamp.stampHash(document.getHash(), serverTimeStampURL);
561 	}
562 
563 	/**
564 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
565 	 * 
566 	 * @param urlDocument URL del documento cuyo contenido se desea sellar
567 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
568 	 * @param parameters Parámetros de la llamada al sello de tiempos
569 	 * @throws FileNotFoundException El fichero no existe
570 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
571 	 * 	de tiempos
572 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
573 	 * 	sello de tiempos
574 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
575 	 * @throws HashingException Error obteniendo el hash
576 	 * @throws InitDocumentException No se puede obtener el documento en la URL
577 	 */
578 	public static TimeStamp stampDocument (URL urlDocument, URL serverTimeStampURL, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
579 		URLDocument document = new URLDocument(urlDocument);
580 		try {
581 			return stampHash (document.getHash(), serverTimeStampURL, HashingAlgorithm.getDefault(), parameters);
582 		} catch (NoSuchAlgorithmException e) {
583 			// No se va a dar porque el algoritmo de hashing por defecto si existe
584 			return null;
585 		}
586 	}
587 
588 	/**
589 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
590 	 * 
591 	 * @param urlDocument URL del documento cuyo contenido se desea sellar
592 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
593 	 * @throws FileNotFoundException El fichero no existe
594 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
595 	 * 	de tiempos
596 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
597 	 * 	sello de tiempos
598 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
599 	 * @throws HashingException Error obteniendo el hash
600 	 * @throws InitDocumentException No se puede obtener el documento en la URL
601 	 */
602 	public static TimeStamp stampDocument (URL urlDocument, URL serverTimeStampURL) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
603 		URLDocument document = new URLDocument(urlDocument);
604 		return TimeStamp.stampHash(document.getHash(), serverTimeStampURL);
605 	}
606 
607 	/**
608 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
609 	 * 
610 	 * @param document Documento cuyo contenido se desea sellar
611 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
612 	 * @param parameters Parámetros de la llamada al sello de tiempos
613 	 * @throws FileNotFoundException El fichero no existe
614 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
615 	 * 	de tiempos
616 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
617 	 * 	sello de tiempos
618 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
619 	 * @throws HashingException Error obteniendo el hash
620 	 */
621 	public static TimeStamp stampDocument (Document document, URL serverTimeStampURL, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
622 		try {
623 			return stampHash (document.getHash(), serverTimeStampURL, HashingAlgorithm.getDefault(), parameters);
624 		} catch (NoSuchAlgorithmException e) {
625 			// No se va a dar porque el algoritmo de hashing por defecto si existe
626 			return null;
627 		}
628 	}
629 
630 	/**
631 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
632 	 * 
633 	 * @param document Documento cuyo contenido se desea sellar
634 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
635 	 * @throws FileNotFoundException El fichero no existe
636 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
637 	 * 	de tiempos
638 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
639 	 * 	sello de tiempos
640 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
641 	 * @throws HashingException Error obteniendo el hash
642 	 */
643 	public static TimeStamp stampDocument (Document document, URL serverTimeStampURL) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
644 		return TimeStamp.stampHash(document.getHash(), serverTimeStampURL);
645 	}
646 
647 	/**
648 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
649 	 * 
650 	 * @param file Fichero con el documento cuyo contenido se desea sellar
651 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
652 	 * @param hashingAlgorithm Algoritmo de hashing
653 	 * @param parameters Parámetros de la llamada al sello de tiempos
654 	 * @throws FileNotFoundException El fichero no existe
655 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
656 	 * 	de tiempos
657 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
658 	 * 	sello de tiempos
659 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
660 	 * @throws HashingException Error obteniendo el hash
661 	 * @throws InitDocumentException El fichero es nulo o no existe
662 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
663 	 */
664 	public static TimeStamp stampDocument (File file, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException, NoSuchAlgorithmException {
665 		FileDocument document = new FileDocument(file);
666 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL, hashingAlgorithm, parameters);
667 	}
668 
669 	/**
670 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
671 	 * 
672 	 * @param file Fichero con el documento cuyo contenido se desea sellar
673 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
674 	 * @param hashingAlgorithm Algoritmo de hashing
675 	 * @throws FileNotFoundException El fichero no existe
676 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
677 	 * 	de tiempos
678 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
679 	 * 	sello de tiempos
680 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
681 	 * @throws HashingException Error obteniendo el hash
682 	 * @throws InitDocumentException El fichero es nulo o no existe
683 	 */
684 	public static TimeStamp stampDocument (File file, URL serverTimeStampURL, String hashingAlgorithm) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
685 		FileDocument document = new FileDocument(file);
686 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL);
687 	}
688 
689 	/**
690 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
691 	 * 
692 	 * @param bDocument Contenido del documento que se desea sellar
693 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
694 	 * @param hashingAlgorithm Algoritmo de hashing
695 	 * @param parameters Parámetros de la llamada al sello de tiempos
696 	 * @throws FileNotFoundException El fichero no existe
697 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
698 	 * 	de tiempos
699 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
700 	 * 	sello de tiempos
701 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
702 	 * @throws HashingException Error obteniendo el hash
703 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
704 	 */
705 	public static TimeStamp stampDocument (byte[] bDocument, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, NoSuchAlgorithmException {
706 		ByteArrayDocument document = new ByteArrayDocument(bDocument);
707 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL, hashingAlgorithm, parameters);
708 	}
709 
710 	/**
711 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
712 	 * 
713 	 * @param bDocument Contenido del documento que se desea sellar
714 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
715 	 * @param hashingAlgorithm Algoritmo de hashing
716 	 * @throws FileNotFoundException El fichero no existe
717 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
718 	 * 	de tiempos
719 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
720 	 * 	sello de tiempos
721 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
722 	 * @throws HashingException Error obteniendo el hash
723 	 */
724 	public static TimeStamp stampDocument (byte[] bDocument, URL serverTimeStampURL, String hashingAlgorithm) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
725 		ByteArrayDocument document = new ByteArrayDocument(bDocument);
726 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL);
727 	}
728 
729 	/**
730 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
731 	 * 
732 	 * @param stream Stream de lectura al contenido del documento que se desea sellar
733 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
734 	 * @param hashingAlgorithm Algoritmo de hashing
735 	 * @param parameters Parámetros de la llamada al sello de tiempos
736 	 * @throws FileNotFoundException El fichero no existe
737 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
738 	 * 	de tiempos
739 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
740 	 * 	sello de tiempos
741 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
742 	 * @throws HashingException Error obteniendo el hash
743 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
744 	 */
745 	public static TimeStamp stampDocument (InputStream stream, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, NoSuchAlgorithmException {
746 		InputStreamDocument document = new InputStreamDocument(stream);
747 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL, hashingAlgorithm, parameters);
748 	}
749 
750 	/**
751 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
752 	 * 
753 	 * @param stream Stream de lectura al contenido del documento que se desea sellar
754 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
755 	 * @param hashingAlgorithm Algoritmo de hashing
756 	 * @throws FileNotFoundException El fichero no existe
757 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
758 	 * 	de tiempos
759 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
760 	 * 	sello de tiempos
761 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
762 	 * @throws HashingException Error obteniendo el hash
763 	 */
764 	public static TimeStamp stampDocument (InputStream stream, URL serverTimeStampURL, String hashingAlgorithm) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
765 		InputStreamDocument document = new InputStreamDocument(stream);
766 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL);
767 	}
768 
769 	/**
770 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
771 	 * 
772 	 * @param urlDocument URL del documento cuyo contenido se desea sellar
773 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
774 	 * @param hashingAlgorithm Algoritmo de hashing
775 	 * @param parameters Parámetros de la llamada al sello de tiempos
776 	 * @throws FileNotFoundException El fichero no existe
777 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
778 	 * 	de tiempos
779 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
780 	 * 	sello de tiempos
781 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
782 	 * @throws HashingException Error obteniendo el hash
783 	 * @throws InitDocumentException No se puede obtener el documento en la URL
784 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
785 	 */
786 	public static TimeStamp stampDocument (URL urlDocument, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException, NoSuchAlgorithmException {
787 		URLDocument document = new URLDocument(urlDocument);
788 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL, hashingAlgorithm, parameters);
789 	}
790 
791 	/**
792 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
793 	 * 
794 	 * @param urlDocument URL del documento cuyo contenido se desea sellar
795 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
796 	 * @param hashingAlgorithm Algoritmo de hashing
797 	 * @throws FileNotFoundException El fichero no existe
798 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
799 	 * 	de tiempos
800 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
801 	 * 	sello de tiempos
802 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
803 	 * @throws HashingException Error obteniendo el hash
804 	 * @throws InitDocumentException No se puede obtener el documento en la URL
805 	 */
806 	public static TimeStamp stampDocument (URL urlDocument, URL serverTimeStampURL, String hashingAlgorithm) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, InitDocumentException {
807 		URLDocument document = new URLDocument(urlDocument);
808 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL);
809 	}
810 
811 	/**
812 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
813 	 * 
814 	 * @param document Documento cuyo contenido se desea sellar
815 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
816 	 * @param hashingAlgorithm Algoritmo de hashing
817 	 * @param parameters Parámetros de la llamada al sello de tiempos
818 	 * @throws FileNotFoundException El fichero no existe
819 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
820 	 * 	de tiempos
821 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
822 	 * 	sello de tiempos
823 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
824 	 * @throws HashingException Error obteniendo el hash
825 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
826 	 */
827 	public static TimeStamp stampDocument (Document document, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, NoSuchAlgorithmException {
828 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL, hashingAlgorithm, parameters);
829 	}
830 
831 	/**
832 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos. 
833 	 * 
834 	 * @param document Documento cuyo contenido se desea sellar
835 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
836 	 * @param hashingAlgorithm Algoritmo de hashing
837 	 * @throws FileNotFoundException El fichero no existe
838 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
839 	 * 	de tiempos
840 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
841 	 * 	sello de tiempos
842 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
843 	 * @throws HashingException Error obteniendo el hash
844 	 */
845 	public static TimeStamp stampDocument (Document document, URL serverTimeStampURL, String hashingAlgorithm) throws HashingException, TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
846 		return TimeStamp.stampHash(document.getHash(hashingAlgorithm), serverTimeStampURL);
847 	}
848 
849 	/**
850 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos.
851 	 * 
852 	 * @param hash Hash del documento cuyo contenido se desea sellar
853 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
854 	 * @param hashingAlgorithm Algoritmo de hashing
855 	 * @param parameters Parámetros de la llamada al sello de tiempos
856 	 * @throws FileNotFoundException El fichero no existe
857 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
858 	 * 	de tiempos
859 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
860 	 * 	sello de tiempos
861 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
862 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hashing
863 	 */
864 	public static TimeStamp stampHash (byte[] hash, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, NoSuchAlgorithmException {
865 		return new TimeStamp (getTimeStamp(hash, serverTimeStampURL, hashingAlgorithm, parameters));
866 	}
867 
868 	/**
869 	 * Método que obtiene un sello de tiempo de un servidor de sello de tiempos.
870 	 * 
871 	 * @param hash Hash del documento cuyo contenido se desea sellar
872 	 * @param serverTimeStampURL URL del servidor de sello de tiempos
873 	 * @throws FileNotFoundException El fichero no existe
874 	 * @throws TimeStampServerConnectionException Errores en la conexión con el servidor de sello
875 	 * 	de tiempos
876 	 * @throws MalformedTimeStampException El objeto devuelto por el servidor no parece ser un 
877 	 * 	sello de tiempos
878 	 * @throws ResponseTimeStampException La TSA ha devuelto una respuesta con un error
879 	 */
880 	public static TimeStamp stampHash (byte[] hash, URL serverTimeStampURL) throws TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException {
881 		try {
882 			return stampHash (hash, serverTimeStampURL, HashingAlgorithm.getDefault(), null);
883 		} catch (NoSuchAlgorithmException e) {
884 			// No se va a dar porque el algoritmo de hashing por defecto si existe
885 			return null;
886 		}
887 	}
888 	
889 	/**
890 	 * Devuelve una respuesta de sello de tiempo de error: punto 2.4.2 de la
891 	 * <a href="http://tools.ietf.org/rfc/rfc3161.txt" target="rfc">RFC-3161</a>
892 	 * 
893 	 * @param status Estado de la respuesta (obligatorio)
894 	 * @param statusString Descripción del estado de la respuesta (opcional)
895 	 * @param failInfo Código de error (opcional)
896 	 * @return Respuesta de error 
897 	 */
898 	public static TimeStampResponse getErrorTimestamp (PKIStatus status, String statusString, PKIFailureInfo failInfo) {
899 		logger.debug("[TimeStamp.getErrorTimestamp]::Entrada::" + Arrays.asList(new Object[] {status, statusString, failInfo}));
900 		
901 		PKIFreeText textoEstado = null;
902 		if (statusString != null) {
903 			textoEstado = new PKIFreeText(statusString);
904 		}
905 		
906 		PKIStatusInfo statusInfo = new PKIStatusInfo(status, textoEstado, failInfo);
907 		TimeStampResp response = new TimeStampResp(statusInfo, null);
908 		try {
909 			return new TimeStampResponse(response);
910 		} catch (Exception e) {
911 			logger.info("[TimeStamp.getErrorTimestamp]::Error inesperado", e);
912 			return null;
913 		} 
914 	}
915 
916 	/**
917 	 * Obtiene una petición de sello de tiempos
918 	 * 
919 	 * @param hash Hash
920 	 * @param hashingAlgorithm Algoritmo de hash
921 	 * @param parameters Parámetros a incluir en la petición
922 	 * @return Petición de sello de tiempos
923 	 * @throws NoSuchAlgorithmException No existe el algoritmo de hash
924 	 */
925 	public static TimeStampRequest getTimeStampRequest(byte[] hash, String hashingAlgorithm, TimeStampRequestParameters parameters) throws NoSuchAlgorithmException {
926 		logger.debug("[TimeStamp.getTimeStampRequest]::Entrada::" + Arrays.asList(new Object[] { hash, parameters }));
927 		
928 	    // Obtener el generador de peticiones a la TSA
929 	    TimeStampRequestGenerator tsrGenerator = new TimeStampRequestGenerator();
930 	    tsrGenerator.setCertReq(true);
931 	    
932 	    // Añadir parámetros
933 	    if (parameters != null) {
934 		    if (parameters.getOid() != null) {
935 		    	tsrGenerator.setReqPolicy(new ASN1ObjectIdentifier(parameters.getOid()));
936 		    }
937 		    
938 		    // Obtener las extensiones
939 		    if (parameters.getExtensions() != null && !parameters.getExtensions().isEmpty()) {
940 		    	for(TimeStampRequestParameters.TimeStampRequestExtension extension : parameters.getExtensions()) {
941 		    		tsrGenerator.addExtension(new ASN1ObjectIdentifier(extension.getOid()), false, extension.getValue());
942 		    	}
943 		    }
944 	    }
945 	    
946 	    // Obtener el algoritmo por el tamaño del hash
947 	    String hashingAlgorithmOID = HashingAlgorithm.getOID(hashingAlgorithm);
948 	    try {
949 		    switch (hash.length) {
950 				case 20:
951 					hashingAlgorithmOID = HashingAlgorithm.getOID(HashingAlgorithm.SHA1);
952 					break;
953 				case 32:
954 					hashingAlgorithmOID = HashingAlgorithm.getOID(HashingAlgorithm.SHA256);
955 					break;
956 				case 48:
957 					hashingAlgorithmOID = HashingAlgorithm.getOID(HashingAlgorithm.SHA384);
958 					break;
959 				case 64:
960 					hashingAlgorithmOID = HashingAlgorithm.getOID(HashingAlgorithm.SHA512);
961 			}
962 	    } catch (NoSuchAlgorithmException e) {
963 	    	logger.debug("[TimeStamp.getTimeStamp]::No existe el algoritmo para el tamaño: " + hash.length);
964 	    }
965 	    
966 	    //-- Obtener la petición
967 	    BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
968 	    if (parameters != null && parameters.getNonce() != null) {
969 	    	nonce = parameters.getNonce();
970 	    }
971 	    
972 	    return tsrGenerator.generate(new ASN1ObjectIdentifier(hashingAlgorithmOID), hash, nonce);
973 	}
974 	
975 	//-- Métodos privados
976 	
977 	/*
978 	 * Obtiene un sello de tiempos de la URL
979 	 */
980 	protected static TimeStampToken getTimeStamp (byte[] hash, URL serverTimeStampURL, String hashingAlgorithm, TimeStampRequestParameters parameters) throws TimeStampServerConnectionException, MalformedTimeStampException, ResponseTimeStampException, NoSuchAlgorithmException {
981 		logger.debug("[TimeStamp.getTimeStamp]::Entrada::" + Arrays.asList(new Object[] { hash, serverTimeStampURL, hashingAlgorithm, parameters }));
982 		
983 	    TimeStampRequest request = TimeStamp.getTimeStampRequest(hash, hashingAlgorithm, parameters);
984 	    byte[] requestBytes = null;
985 		try {
986 			requestBytes = request.getEncoded();
987 		} catch (IOException e) {
988 			logger.info("[TimeStamp.getTimeStamp]:: Error obteniendo la petición de sello de tiempo", e);
989 		}
990 
991 		//-- Conectar a la TSA y obtener la respuesta
992 	    Hashtable reqProperties = new Hashtable();
993 
994 	    if (parameters != null && parameters.getUser() != null && parameters.getPassword() != null) {
995 		    String userPassword = parameters.getUser() + ":" + parameters.getPassword();
996 		    String reqString = "Basic " + new String(Util.encodeBase64(userPassword.getBytes()));
997 		    reqProperties.put("Authorization", reqString);
998 		    reqProperties.put("Content-Length", String.valueOf(reqString.length()));
999 	    }
1000 
1001 	    reqProperties.put("Content-Type", "application/timestamp-query");
1002 	    reqProperties.put("Content-Transfer-Encoding", "binary");
1003 
1004 	    TimeStampResponse response;
1005 		try {
1006 			response = connect(serverTimeStampURL, reqProperties, requestBytes);
1007 		} catch (IOException e) {
1008 			logger.info("[TimeStamp.getTimeStamp]::Error conectando con la TSA", e);
1009 			throw new TimeStampServerConnectionException ("Error conectando con la TSA", e);
1010 		}
1011 
1012 	    //-- Validar la respuesta
1013 	    if (response.getTimeStampToken()== null) {
1014 	    	
1015 	    	if (response.getFailInfo() == null) {
1016 	    		logger.info("[TimeStamp.getTimeStamp]::Por algún motivo desconocido la TSA no ha podido devolver un sello de tiempos");
1017 	    		throw new ResponseTimeStampException ("Por algún motivo desconocido la TSA no ha podido devolver un sello de tiempos");
1018 	    	} else {
1019 	    	
1020 		    	switch (response.getFailInfo().intValue()) {
1021 		    		case PKIFailureInfo.badAlg:
1022 			    		logger.info("[TimeStamp.getTimeStamp]::El algoritmo del hashing no está reconocido por la TSA");
1023 		    			throw new ResponseTimeStampException ("El algoritmo del hashing no está reconocido por la TSA");
1024 		    		case PKIFailureInfo.badRequest:
1025 			    		logger.info("[TimeStamp.getTimeStamp]::La TSA considera que la petición no es correcta");
1026 		    			throw new ResponseTimeStampException ("La TSA considera que la petición no es correcta");
1027 		    		case PKIFailureInfo.badDataFormat:
1028 			    		logger.info("[TimeStamp.getTimeStamp]::La información a sellar tiene un formato incorrecto");
1029 		    			throw new ResponseTimeStampException ("La información a sellar tiene un formato incorrecto");
1030 		    		case PKIFailureInfo.timeNotAvailable:
1031 			    		logger.info("[TimeStamp.getTimeStamp]::En estos momentos la fuente de tiempos de la TSA no se encuentra disponible");
1032 		    			throw new ResponseTimeStampException ("En estos momentos la fuente de tiempos de la TSA no se encuentra disponible");
1033 		    		case PKIFailureInfo.unacceptedPolicy:
1034 			    		logger.info("[TimeStamp.getTimeStamp)]::La política pedida a la TSA no es aceptada por ésta");
1035 		    			throw new ResponseTimeStampException ("La política pedida a la TSA no es aceptada por ésta");
1036 		    		case PKIFailureInfo.unacceptedExtension:
1037 			    		logger.info("[TimeStamp.getTimeStamp]::La extensión pedida no es aceptada por la TSA");
1038 		    			throw new ResponseTimeStampException ("La extensión pedida no es aceptada por la TSA");
1039 		    		case PKIFailureInfo.addInfoNotAvailable:
1040 			    		logger.info("[TimeStamp.getTimeStamp]::La información adicional pedida no es entendida o no está disponible en la TSA");
1041 		    			throw new ResponseTimeStampException ("La información adicional pedida no es entendida o no está disponible en la TSA");
1042 		    		case PKIFailureInfo.systemFailure:
1043 			    		logger.info("[TimeStamp.getTimeStamp]::La respuesta no puede ser atendida debido a un error interno del servidor");
1044 		    			throw new ResponseTimeStampException ("La respuesta no puede ser atendida debido a un error interno del servidor");
1045 		    		default:
1046 			    		logger.info("[TimeStamp.getTimeStamp]::Por algún motivo desconocido la TSA no ha podido devolver un sello de tiempos");
1047 		    			throw new ResponseTimeStampException ("Por algún motivo desconocido la TSA no ha podido devolver un sello de tiempos");
1048 		    	}
1049 	    	}
1050 	    }
1051 	    
1052 	    return response.getTimeStampToken();
1053 		
1054 	}
1055 
1056 	/*
1057 	 * Metodo que inicializa este objeto en base al stream de lectura que se pasa como
1058 	 * parámetro.
1059 	 * 
1060 	 * @param isTimeStamp Stream de lectura a un objeto sello de tiempos
1061 	 * @throws TimeStampException El objeto contenido en el stream de lectura no parece
1062 	 * 	ser un sello de tiempos
1063 	 */
1064 	private void initialize (InputStream isTimeStamp) throws MalformedTimeStampException {
1065 		logger.debug("[TimeStamp.initialize]::Entrada::" + isTimeStamp);
1066 		
1067 		// Obtenemos el objeto DER
1068 		ASN1Sequence derTS;
1069 		try {
1070 			derTS = (ASN1Sequence)Util.toDER(isTimeStamp);
1071 		} catch (IOException e) {
1072 			logger.info("[TimeStamp.initialize]::El stream no parece contener un objeto en formato DER", e);
1073 			throw new MalformedTimeStampException ("El stream no parece contener un objeto en formato DER", e);
1074 		}
1075 		
1076 		try {
1077 			this.timeStamp = new TimeStampToken (new CMSSignedData(derTS.getEncoded(ASN1Encoding.DER)));
1078 		} catch (TSPException e) {
1079 			logger.info("[TimeStamp.initialize]::El stream no parece contener un sello de tiempos", e);
1080 			throw new MalformedTimeStampException ("El stream no parece contener un sello de tiempos", e);
1081 		} catch (IOException e) {
1082 			logger.info("[TimeStamp.initialize]::El stream no parece contener un objeto en formato DER", e);
1083 			throw new MalformedTimeStampException ("El stream no parece contener un objeto en formato DER", e);
1084 		} catch (CMSException e) {
1085 			logger.info("[TimeStamp.initialize]::El stream no parece contener un sello de tiempos", e);
1086 			throw new MalformedTimeStampException ("El stream no parece contener un sello de tiempos", e);
1087 		}
1088 
1089 	}
1090 	
1091 	/*
1092 	 * Se conecta a una TSA, le pasa la petición con las propiedades reqProperties y
1093 	 * obtiene una respuesta
1094 	 */
1095 	private static TimeStampResponse connect(URL serverTimeStampURL, Hashtable reqProperties, byte[] requestBytes) throws IOException, MalformedTimeStampException {
1096 	    logger.debug("[TimeStamp.connect]:: " + Arrays.asList(new Object[] { serverTimeStampURL, reqProperties, requestBytes }));
1097 
1098 	    HttpURLConnection urlConn;
1099 	    OutputStream printout = null;
1100 	    DataInputStream input = null;
1101 
1102 	    // Abrir la conexión a la URL
1103 	    urlConn = (HttpURLConnection) serverTimeStampURL.openConnection();
1104 	    
1105 	    // La conexión será en las dos direcciones
1106 	    urlConn.setDoInput(true);
1107 	    urlConn.setDoOutput(true);
1108 	    urlConn.setRequestMethod("POST");
1109 	    
1110 	    // No usar caché
1111 	    urlConn.setUseCaches(false);
1112 	    
1113 	    // Propiedades de la petición
1114 	    Iterator iter = reqProperties.entrySet().iterator();
1115 	    while (iter.hasNext()) {
1116 	    	Map.Entry entry = (Map.Entry) iter.next();
1117 	    	urlConn.setRequestProperty((String) entry.getKey(), (String) entry.getValue());
1118 	    }
1119 	    
1120 	    // Escribir la petición
1121 	    try {
1122 		    printout = urlConn.getOutputStream();
1123 		    printout.write(requestBytes);
1124 		    printout.flush();
1125 	    } finally {
1126 	    	if (printout != null) {
1127 	    		try { printout.close(); } catch (IOException e) { }
1128 	    	}
1129 	    }
1130 	    
1131 	    // Obtener la respuesta
1132 	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
1133 	    try {
1134 		    input = new DataInputStream(urlConn.getInputStream());
1135 		    
1136 		    byte[] buffer = new byte[1024];
1137 		    int bytesRead = 0;
1138 		    while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) {
1139 		    	baos.write(buffer, 0, bytesRead);
1140 		    }
1141 	    } finally {
1142 	    	if (input != null) {
1143 	    		try { input.close(); } catch (IOException e) { }
1144 	    	}
1145 	    }
1146 	    
1147 	    try {
1148 			return new TimeStampResponse (baos.toByteArray());
1149 		} catch (TSPException e) {
1150 			logger.info("[TimeStamp.connect]::La respuesta del servidor no parece una respuesta de sello de tiempos", e);
1151 			throw new MalformedTimeStampException ("La respuesta del servidor no parece una respuesta de sello de tiempos", e);
1152 		}
1153 	    
1154 
1155 	}
1156 
1157 
1158 }