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.signature;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.security.InvalidKeyException;
31  import java.security.NoSuchAlgorithmException;
32  import java.security.NoSuchProviderException;
33  import java.security.PrivateKey;
34  import java.security.cert.X509Certificate;
35  import java.text.SimpleDateFormat;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  import java.util.Calendar;
39  import java.util.Date;
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.List;
43  
44  import org.apache.log4j.Logger;
45  import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
46  import org.bouncycastle.cert.ocsp.BasicOCSPResp;
47  
48  import com.itextpdf.text.DocumentException;
49  import com.itextpdf.text.Image;
50  import com.itextpdf.text.Rectangle;
51  import com.itextpdf.text.pdf.AcroFields;
52  import com.itextpdf.text.pdf.PdfDate;
53  import com.itextpdf.text.pdf.PdfDictionary;
54  import com.itextpdf.text.pdf.PdfName;
55  import com.itextpdf.text.pdf.PdfPKCS7;
56  import com.itextpdf.text.pdf.PdfReader;
57  import com.itextpdf.text.pdf.PdfSignature;
58  import com.itextpdf.text.pdf.PdfSignatureAppearance;
59  import com.itextpdf.text.pdf.PdfStamper;
60  import com.itextpdf.text.pdf.PdfString;
61  import com.itextpdf.text.pdf.TSAClient;
62  import com.itextpdf.text.pdf.TSAClientBouncyCastle;
63  
64  import es.accv.arangi.base.algorithm.DigitalSignatureAlgorithm;
65  import es.accv.arangi.base.certificate.Certificate;
66  import es.accv.arangi.base.certificate.validation.CAList;
67  import es.accv.arangi.base.certificate.validation.CertificateValidationService;
68  import es.accv.arangi.base.certificate.validation.ValidateCertificate;
69  import es.accv.arangi.base.device.DeviceManager;
70  import es.accv.arangi.base.document.IDocument;
71  import es.accv.arangi.base.document.InputStreamDocument;
72  import es.accv.arangi.base.exception.certificate.CertificateCANotFoundException;
73  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
74  import es.accv.arangi.base.exception.device.AliasNotFoundException;
75  import es.accv.arangi.base.exception.device.LoadingObjectException;
76  import es.accv.arangi.base.exception.device.SearchingException;
77  import es.accv.arangi.base.exception.document.HashingException;
78  import es.accv.arangi.base.exception.signature.AlgorithmNotSuitableException;
79  import es.accv.arangi.base.exception.signature.InvalidCertificateException;
80  import es.accv.arangi.base.exception.signature.PDFDocumentException;
81  import es.accv.arangi.base.exception.signature.SignatureException;
82  import es.accv.arangi.base.exception.signature.SignatureNotFoundException;
83  import es.accv.arangi.base.exception.timestamp.MalformedTimeStampException;
84  import es.accv.arangi.base.timestamp.TimeStamp;
85  import es.accv.arangi.base.util.Util;
86  import es.accv.arangi.base.util.validation.ValidationResult;
87  
88  /**
89   * Clase para manejar firmas en PDF según la norma 
90   * <a href="http://www.iso.org/iso/catalogue_detail.htm?csnumber=51502" target="estandar">ISO 32000-1</a><br><br>
91   * 
92   * Para evitar problemas de saturación de memoria con ficheros PDF muy grandes,
93   * esta clase siempre trabajará sobre un objeto java.io.File. Si el objeto no se 
94   * inicializa con un fichero se creará un archivo temporal en la carpeta temporal 
95   * de Arangi: {@link #getArangiTemporalFolder() getArangiTemporalFolder}.
96   * 
97   * Existen dos métodos para obtener una firma PDF, dependiendo de si se desea 
98   * una firma visible o invisible. En el caso de las firmas visibles hay que proporcionar 
99   * al método las coordenadas de las esquinas inferior izquierda y superior derecha, así 
100  * como el número de página donde se desea ubicar la firma. También es posible asociar
101  * una imagen a la firma.<br><br>
102  * 
103  * <code>
104  * KeyStoreManager manager = new KeyStoreManager (...,...);<br>
105  * String alias = ...;<br>
106  * ByteArrayDocument documentPDF = new ByteArrayDocument (...);<br>
107  * URL urlTSA = new URL (...);<br>
108  * CAList caList = new CAList (...);<br><br>
109  * 
110  * //-- Firma invisible<br>
111  * PDFSignature signatureInv = PDFSignature.sign (new KeyStoreManager[] {manager}, new String[] {alias}, documentPDF, urlTSA, caList, "Porque quiero firmarlo");<br><br>
112  * 
113  * //-- Firma visible<br>
114  * PDFSignature signatureVis = PDFSignature.sign (new ACCVDeviceManager[] {manager}, new String[] {alias},documentPDF, urlTSA, caList, "Porque quiero firmarlo",
115  *      true, Util.readStream(ClassLoader.getSystemResourceAsStream("signature/chip.gif")), 100, 100, 300, 200, 1);<br><br>
116  * </code>
117  * 
118  * En la página 1 de la segunda firma, en la ubicación indicada por las coordenadas, se 
119  * verá la imagen chip.gif como una firma realizada sobre el PDF. El primer parámetro del
120  * método de firma es un array de managers, ya que es posible realizar varias firmas a 
121  * la vez.<br><br>
122  * 
123  * Cabe la posibilidad de crear firmas sin sello de tiempo. Para ello se debe utilizar alguno 
124  * de los métodos a los que no hay que pasar la URL de un servidor de sello de tiempos.<br>
125  * 
126  * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
127  */
128 public class PDFSignature extends BasePDFSignature {
129 
130 	/*
131 	 * Constante para incluir como motivo de la firma cuando éste no es pasado 
132 	 * como parámetro
133 	 */
134 	private static final String DEFAULT_SIGNATURE_REASON = "Firma realizada con Arangi (ACCV)";
135 
136 	/*
137 	 * Logger de la clase 
138 	 */
139 	static Logger logger = Logger.getLogger(PDFSignature.class);
140 	
141 	//-- Constructores
142 	
143 	/**
144 	 * Inicializa el objeto con un fichero PDF firmado.
145 	 * 
146 	 * @param pdfFile Fichero PDF firmado
147 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
148 	 * 	ser leído
149 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
150 	 */
151 	public PDFSignature (File pdfFile) throws PDFDocumentException, SignatureNotFoundException {
152 		initialize(pdfFile);
153 	}
154 	
155 	/**
156 	 * Inicializa el objeto con el contenido de un fichero PDF firmado.
157 	 * 
158 	 * @param pdfContentBytes Array de bytes con el contenido del fichero PDF firmado
159 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
160 	 * 	ser leído
161 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
162 	 * @throws IOException No se puede crear el fichero temporal
163 	 */
164 	public PDFSignature (byte[] pdfContentBytes) throws PDFDocumentException, SignatureNotFoundException, IOException {
165 		//-- Guardar en un fichero temporal
166 		File fileTemp = null;
167 		try {
168 			fileTemp = getFileTemp ();
169 			Util.saveFile(fileTemp, pdfContentBytes);
170 		} catch (IOException e) {
171 			logger.info("[PDFSignature(byte[])::No se puede crear el fichero temporal o no se puede escribir en él", e);
172 			if (fileTemp != null) { fileTemp.delete(); }
173 			throw e;
174 		}
175 		
176 		initialize(fileTemp);
177 	}
178 	
179 	/**
180 	 * Inicializa el objeto con un stream de lectura al contenido de un fichero PDF firmado.
181 	 * 
182 	 * @param streamPDF Stream de lectura al contenido del fichero PDF firmado
183 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
184 	 * 	ser leído
185 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
186 	 * @throws IOException No se puede crear el fichero temporal
187 	 */
188 	public PDFSignature (InputStream streamPDF) throws PDFDocumentException, SignatureNotFoundException, IOException {
189 		//-- Guardar en un fichero temporal
190 		FileOutputStream fos = null;
191 		File fileTemp = null;
192 		try {
193 			fileTemp = getFileTemp ();
194             fos = new FileOutputStream(fileTemp);
195             int c;
196             while ((c = streamPDF.read()) != -1) {
197                fos.write(c);
198             }
199 		} catch (IOException e) {
200 			logger.info("[PDFSignature(byte[])::No se puede crear el fichero temporal o no se puede escribir en él", e);
201 			if (fileTemp != null) { 
202 				fos.close();
203 				fos = null;
204 				fileTemp.delete(); 
205 			}
206 			throw e;
207 		} finally {
208 			if (fos != null) {
209 				fos.close();
210 			}
211 		}
212 		
213 		//-- Inicializar
214 		initialize(fileTemp);
215 	}
216 	
217 	/**
218 	 * Inicializa el objeto con un documento que debe contener un fichero PDF firmado.
219 	 * 
220 	 * @param document Documento con el contenido del fichero PDF firmado
221 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
222 	 * 	ser leído
223 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
224 	 * @throws IOException No se puede crear el fichero temporal
225 	 */
226 	public PDFSignature (IDocument document) throws PDFDocumentException, SignatureNotFoundException, IOException {
227 		//-- Guardar en un fichero temporal
228 		FileOutputStream fos = null;
229 		InputStream fis = null;
230 		File fileTemp = null;
231 		try {
232 			fileTemp = getFileTemp ();
233 			fis = document.getInputStream();
234             fos = new FileOutputStream(fileTemp);
235             int c;
236             while ((c = fis.read()) != -1) {
237                fos.write(c);
238             }
239 		} catch (IOException e) {
240 			logger.info("[PDFSignature(byte[])::No se puede crear el fichero temporal o no se puede escribir en él", e);
241 			if (fileTemp != null) { 
242 				fos.close();
243 				fos = null;
244 				fileTemp.delete(); 
245 			}
246 			throw e;
247 		} finally {
248 			if (fos != null) {
249 				fos.close();
250 			}
251 			if (fis != null) {
252 				fis.close();
253 			}
254 		}
255 		
256 		//-- Inicializar
257 		initialize(fileTemp);
258 	}
259 	
260 	/*
261 	 * Inicializa el objeto con un documento igual.
262 	 */
263 	protected PDFSignature (PDFSignature signature) {
264 		this.pdfFile = signature.pdfFile;
265 	}
266 	
267 	//-- Métodos estáticos
268 	
269 	/**
270 	 * Obtiene un objeto {@link PDFSignature PDFSignature} tras firmar un documento PDF.
271 	 * La firma es invisible.
272 	 * 
273 	 * @param managers Dispositivos criptográfico que realizarán la firma
274 	 * @param alias Alias donde se encuentran las claves privada dentro de los dispositivos
275 	 * @param pdfDocument Documento PDF a firmar
276 	 * @param urlTimestamp URL del servidor de sello de tiempos
277 	 * @param caList Lista de certificados de CA, uno de ellos ha de ser el emisor del 
278 	 * 	certificado con el que se realiza la firma. Si no es null el certificado se validará
279 	 * 	antes de la firma y, en ésta, se añadirá la cadena de confianza.
280 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
281 	 * 	nulo se escribirá un texto por defecto.
282 	 * @return Documento PDF firmado, con sello de tiempos
283 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
284 	 * 	realizar la firma no existe
285 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
286 	 * 	realizar la firma
287 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
288 	 * @throws SignatureException No se puede realizar la firma
289 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
290 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
291 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
292 	 * 	el proveedor criptográfico de Arangi
293 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
294 	 */
295 	public static PDFSignature sign (DeviceManager managers[], String[] alias, IDocument pdfDocument, URL urlTimestamp, 
296 			CAList caList, String reason) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, SignatureException, HashingException, CertificateCANotFoundException, InvalidCertificateException {
297 		try {
298 			return sign (managers, alias, pdfDocument, null, urlTimestamp, null, null, caList, reason, false, null, -1, -1, -1, -1, 0);
299 		} catch (AlgorithmNotSuitableException e) {
300 			logger.info("El algoritmo por defecto no debería provocar este error", e);
301 			throw new SignatureException("El algoritmo por defecto no debería provocar este error", e);
302 		}
303 	}
304 	
305 	/**
306 	 * Obtiene un objeto {@link PDFSignature PDFSignature} tras firmar un documento PDF.
307 	 * El servidor de la TSA requiere autenticación y la firma obtenida es invisible.
308 	 * 
309 	 * @param managers Dispositivos criptográfico que realizarán la firma
310 	 * @param alias Alias donde se encuentran las claves privada dentro de los dispositivos
311 	 * @param pdfDocument Documento PDF a firmar
312 	 * @param urlTimestamp URL del servidor de sello de tiempos
313 	 * @param userTSA Usuario para acceder al servidor de sello de tiempos. Nulo si no
314 	 * 	requiere autenticación.
315 	 * @param passwordTSA Contraseña para acceder al servidor de sello de tiempos. Nula si 
316 	 * no requiere autenticación.
317 	 * @param caList Lista de certificados de CA, uno de ellos ha de ser el emisor del 
318 	 * 	certificado con el que se realiza la firma. Si no es null el certificado se validará
319 	 * 	antes de la firma y, en ésta, se añadirá la cadena de confianza.
320 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
321 	 * 	nulo se escribirá un texto por defecto.
322 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
323 	 * 	realizar la firma no existe
324 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
325 	 * 	realizar la firma
326 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
327 	 * @throws SignatureException No se puede realizar la firma
328 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
329 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
330 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
331 	 * 	el proveedor criptográfico de Arangi
332 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
333 	 */
334 	public static PDFSignature sign (DeviceManager[] managers, String[] alias, IDocument pdfDocument, URL urlTimestamp, 
335 			String userTSA, String passwordTSA, CAList caList, String reason) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, SignatureException, HashingException, CertificateCANotFoundException, InvalidCertificateException {
336 		try {
337 			return sign (managers, alias, pdfDocument, null, urlTimestamp, userTSA, passwordTSA, caList, reason, false, null, -1, -1, -1, -1, 0);
338 		} catch (AlgorithmNotSuitableException e) {
339 			logger.info("El algoritmo por defecto no debería provocar este error", e);
340 			throw new SignatureException("El algoritmo por defecto no debería provocar este error", e);
341 		}
342 	}
343 	
344 	/**
345 	 * Obtiene un objeto {@link PDFSignature PDFSignature} tras firmar un documento PDF.
346 	 * La firma obtenida es invisible. La firma PDF no incluye sello de tiempos.
347 	 * 
348 	 * @param managers Dispositivos criptográfico que realizarán la firma
349 	 * @param alias Alias donde se encuentran las claves privada dentro de los dispositivos
350 	 * @param pdfDocument Documento PDF a firmar
351 	 * @param caList Lista de certificados de CA, uno de ellos ha de ser el emisor del 
352 	 * 	certificado con el que se realiza la firma. Si no es null el certificado se validará
353 	 * 	antes de la firma y, en ésta, se añadirá la cadena de confianza.
354 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
355 	 * 	nulo se escribirá un texto por defecto.
356 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
357 	 * 	realizar la firma no existe
358 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
359 	 * 	realizar la firma
360 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
361 	 * @throws SignatureException No se puede realizar la firma
362 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
363 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
364 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
365 	 * 	el proveedor criptográfico de Arangi
366 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
367 	 */
368 	public static PDFSignature sign (DeviceManager[] managers, String[] alias, IDocument pdfDocument, 
369 			CAList caList, String reason) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, SignatureException, HashingException, CertificateCANotFoundException, InvalidCertificateException {
370 		try{
371 			return sign (managers, alias, pdfDocument, null, null, null, null, caList, reason, false, null, -1, -1, -1, -1, 0);
372 		} catch (AlgorithmNotSuitableException e) {
373 			logger.info("El algoritmo por defecto no debería provocar este error", e);
374 			throw new SignatureException("El algoritmo por defecto no debería provocar este error", e);
375 		}
376 	}
377 	
378 	/**
379 	 * Obtiene un objeto {@link PDFSignature PDFSignature} tras firmar un documento PDF.<br><br>
380 	 * 
381 	 * Si la firma es visible se le puede asociar una imagen. El punto 0,0 de la página 
382 	 * se encuentra en la esquina inferior izquierda de la misma. Un página tiene 
383 	 * aproximadamente unas dimensiones de 580x850. <br><br>
384 	 * 
385 	 * La firma PDF no incluye sello de tiempos.
386 	 * 
387 	 * @param managers Dispositivos criptográfico que realizarán la firma
388 	 * @param alias Alias donde se encuentran las claves privada dentro de los dispositivos
389 	 * @param pdfDocument Documento PDF a firmar
390 	 * @param caList Lista de certificados de CA, uno de ellos ha de ser el emisor del 
391 	 * 	certificado con el que se realiza la firma. Si no es null el certificado se validará
392 	 * 	antes de la firma y, en ésta, se añadirá la cadena de confianza.
393 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
394 	 * 	nulo se escribirá un texto por defecto.
395 	 * @param isVisible Si tiene un valor cierto se creará una firma visible.
396 	 * @param image Imagen de la firma. Puede tener un valor nulo.
397 	 * @param llX Posición X de la esquina inferior izquierda de la firma en la página (caso de ser visible)
398 	 * @param llY Posición Y de la esquina inferior izquierda de la firma en la página (caso de ser visible) 
399 	 * @param urX Posición X de la esquina superior derecha de la firma en la página (caso de ser visible)
400 	 * @param urY Posición Y de la esquina superior derecha de la firma en la página (caso de ser visible)
401 	 * @param page Página en la que se situará la firma si ésta es visible (1 es la primera página)
402 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
403 	 * 	realizar la firma no existe
404 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
405 	 * 	realizar la firma
406 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
407 	 * @throws SignatureException No se puede realizar la firma
408 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
409 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
410 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
411 	 * 	el proveedor criptográfico de Arangi
412 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
413 	 */
414 	public static PDFSignature sign (DeviceManager[] managers, String[] alias, IDocument pdfDocument, 
415 			CAList caList, String reason, boolean isVisible, byte[] image, float llX, float llY, float urX, 
416 			float urY, int page) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, SignatureException, HashingException, CertificateCANotFoundException, InvalidCertificateException {
417 		
418 		try {
419 			return sign(managers, alias, pdfDocument, null, null, null, null, caList, reason, isVisible, image, llX, llY, urX, urY, page);
420 		} catch (AlgorithmNotSuitableException e) {
421 			logger.info("El algoritmo por defecto no debería provocar este error", e);
422 			throw new SignatureException("El algoritmo por defecto no debería provocar este error", e);
423 		}
424 	}
425 	
426 	/**
427 	 * Obtiene un objeto {@link PDFSignature PDFSignature} tras firmar un documento PDF.<br><br>
428 	 * 
429 	 * Si la firma es visible se le puede asociar una imagen. El punto 0,0 de la página 
430 	 * se encuentra en la esquina inferior izquierda de la misma. Un página tiene 
431 	 * aproximadamente unas dimensiones de 580x850. 
432 	 * 
433 	 * @param managers Dispositivos criptográfico que realizarán la firma
434 	 * @param alias Alias donde se encuentran las claves privada dentro de los dispositivos
435 	 * @param pdfDocument Documento PDF a firmar
436 	 * @param digitalSignatureAlgorithm Algoritmo de firma (si nulo algoritmo por defecto)
437      * @param urlTimestamp URL del servidor de sello de tiempos
438 	 * @param userTSA Usuario para acceder al servidor de sello de tiempos. Nulo si no
439 	 * 	requiere autenticación.
440 	 * @param passwordTSA Contraseña para acceder al servidor de sello de tiempos. Nula si 
441 	 * no requiere autenticación.
442 	 * @param caList Lista de certificados de CA, uno de ellos ha de ser el emisor del 
443 	 * 	certificado con el que se realiza la firma. Si no es null el certificado se validará
444 	 * 	antes de la firma y, en ésta, se añadirá la cadena de confianza.
445 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
446 	 * 	nulo se escribirá un texto por defecto.
447 	 * @param isVisible Si tiene un valor cierto se creará una firma visible.
448 	 * @param image Imagen de la firma. Puede tener un valor nulo.
449 	 * @param llX Posición X de la esquina inferior izquierda de la firma en la página (caso de ser visible)
450 	 * @param llY Posición Y de la esquina inferior izquierda de la firma en la página (caso de ser visible) 
451 	 * @param urX Posición X de la esquina superior derecha de la firma en la página (caso de ser visible)
452 	 * @param urY Posición Y de la esquina superior derecha de la firma en la página (caso de ser visible)
453 	 * @param page Página en la que se situará la firma si ésta es visible (1 es la primera página)
454 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
455 	 * 	realizar la firma no existe
456 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
457 	 * 	realizar la firma
458 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
459 	 * @throws SignatureException No se puede realizar la firma
460 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
461 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
462 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
463 	 * 	el proveedor criptográfico de Arangi
464 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
465 	 * @throws AlgorithmNotSuitableException El algoritmo de firma pasado no sirve para realizar la firma
466 	 */
467 	public static PDFSignature sign (DeviceManager[] managers, String[] alias, IDocument pdfDocument, 
468 			String digitalSignatureAlgorithm, URL urlTimestamp, String userTSA, String passwordTSA, 
469 			CAList caList, String reason, boolean isVisible, byte[] image, 
470 			float llX, float llY, float urX, float urY, int page) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, SignatureException, HashingException, CertificateCANotFoundException, InvalidCertificateException, AlgorithmNotSuitableException {
471 		
472 		logger.debug("[PDFSignature.sign]::Entrada::" + Arrays.asList (new Object [] { managers, alias, pdfDocument, digitalSignatureAlgorithm, urlTimestamp, userTSA, passwordTSA, caList, reason, new Boolean (isVisible), image, new Float (llX), new Float (llY), new Float (urX), new Float (urY), new Integer (page) }));
473 		
474 		FileOutputStream fos = null;
475 		try {
476 		
477 			//-- Comprobar parámetros
478 			if (reason == null) {
479 				reason = DEFAULT_SIGNATURE_REASON;
480 			}
481 			
482 			//-- Cliente TSA (de iText)
483 			TSAClient tsc = null;
484 			if (urlTimestamp != null) {
485 				tsc = new TSAClientBouncyCastle(urlTimestamp.toString(), userTSA, passwordTSA);
486 			}
487 			
488 			//-- Obtener el fichero temporal donde se dejará el resultado
489 			File fileResult1 = getFileTemp();
490 			Util.saveFile(fileResult1, pdfDocument.getInputStream());
491 			
492 			for (int i = 0; i < managers.length; i++) {
493 				
494 				//-- Obtener la clave privada
495 				PrivateKey pk = managers[i].getPrivateKey(alias[i]);
496 				if (pk == null) {
497 					logger.info("[PDFSignature.sign]::No se ha podido encontrar el alias o bien éste no contiene una clave privada");
498 					throw new AliasNotFoundException ("No se ha podido encontrar el alias o bien éste no contiene una clave privada");
499 				}
500 				
501 				//--Obtener el certificado y validarlo si el caList no es nulo
502 				X509Certificate x509Certificate = managers[i].getCertificate(alias[i]);
503 				ValidateCertificate validateCertificate = null;
504 				java.security.cert.Certificate[] chain;
505 				if (caList != null) {
506 					validateCertificate = new ValidateCertificate (x509Certificate, caList);
507 					int validationResult;
508 					if ((validationResult = validateCertificate.validate()) != ValidationResult.RESULT_VALID) {
509 						logger.info("[PDFSignature.sign]::El certificado de firma no es válido: " + validationResult);
510 						throw new InvalidCertificateException ("El certificado de firma no es válido: " + validationResult, validationResult);
511 					}
512 					chain = new java.security.cert.Certificate[] { x509Certificate, validateCertificate.getIssuerCertificate().toX509Certificate() };
513 				} else {
514 					chain = new java.security.cert.Certificate[] { x509Certificate };
515 				}
516 				
517 				//-- Leer el PDF y crear la firma. Dependiendo de si el PDF ya
518 				//-- está firmado o no se crea el PdfStamper de una manera u otra
519 				FileInputStream fis = new FileInputStream(fileResult1);
520 				PdfReader reader = new PdfReader(fis);
521 				File fileResult2 = getFileTemp();
522 				fos = new FileOutputStream (fileResult2);
523 				AcroFields af = reader.getAcroFields();
524 				ArrayList names = af.getSignatureNames();
525 				PdfStamper stp;
526 				if (names == null || names.isEmpty()) {
527 					stp = PdfStamper.createSignature(reader, fos, '\0');
528 				} else {
529 					stp = PdfStamper.createSignature(reader, fos, '\0', null, true);
530 				}
531 				
532 				//-- Apariencia de la firma
533 				PdfSignatureAppearance sap = stp.getSignatureAppearance();
534 				sap.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
535 				sap.setReason(reason);
536 				if (isVisible) {
537 					sap.setVisibleSignature(new Rectangle(llX, llY, urX, urY), page, null);
538 					StringBuffer detail = new StringBuffer();
539 					if (validateCertificate != null) {
540 						detail.append(DIGITALLY_SIGNED_TEXT + ": " + validateCertificate.getCommonName() + "\n");
541 					}
542 					SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm");
543 					detail.append("Fecha: " + sdf.format(new Date()) + "\n");
544 					if (reason != null && !reason.equals("")) {
545 						detail.append("Motivo: " + reason + "\n");
546 					}
547 					sap.setLayer2Text(detail.toString());
548 					if (image != null) {
549 						sap.setImage(Image.getInstance(image));
550 					}
551 				}
552 		
553 				//-- Cargar valores en el diccionario
554 				PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached"));
555 				dic.setReason(sap.getReason());
556 				dic.setLocation(sap.getLocation());
557 				dic.setContact(sap.getContact());
558 				dic.setDate(new PdfDate(sap.getSignDate()));
559 				sap.setCryptoDictionary(dic);
560 				sap.setExternalDigest(new byte[128], null, "RSA"); 
561 		
562 				int contentEstimated = 15000;
563 				HashMap exc = new HashMap();
564 				exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2));
565 				sap.preClose(exc);
566 				
567 				//-- Algoritmos
568 				if (digitalSignatureAlgorithm == null) {
569 					digitalSignatureAlgorithm = DigitalSignatureAlgorithm.getDefault();
570 				}
571 				String hashingAlgorithm = DigitalSignatureAlgorithm.getHashingAlgorithm(digitalSignatureAlgorithm);
572 				if (hashingAlgorithm == null) {
573 					logger.info("No se puede tratar el algoritmo de firma " + digitalSignatureAlgorithm);
574 					throw new AlgorithmNotSuitableException("No se puede tratar el algoritmo de firma " + digitalSignatureAlgorithm);
575 				}
576 		
577 				//-- Construir el PKCS#7 (firmando con el manager de Arangi)
578 				Calendar cal = Calendar.getInstance();
579 				PdfPKCS7 pkcs7 = new PdfPKCS7(null, chain, null, hashingAlgorithm, CRYPTOGRAPHIC_PROVIDER_NAME, false);
580 				byte hash[] = new InputStreamDocument (sap.getRangeStream()).getHash(hashingAlgorithm);
581 				byte sh[] = pkcs7.getAuthenticatedAttributeBytes(hash, cal, null);
582 				byte[] signatureBytes = managers[i].signDocument(new ByteArrayInputStream (sh), alias[i], digitalSignatureAlgorithm);
583 				pkcs7.setExternalDigest(signatureBytes, sh, "RSA"); 
584 				byte[] encodedSig = pkcs7.getEncodedPKCS7(hash, cal, tsc, hashingAlgorithm, null);
585 				
586 				//-- Añadir la firma al diccionario
587 				if (contentEstimated + 2 < encodedSig.length) {
588 					logger.info("[PDFSignature.sign]::No se puede añadir la firma al PDF por falta de espacio en el mismo");
589 					throw new SignatureException ("No se puede añadir la firma al PDF por falta de espacio en el mismo");
590 				}
591 		
592 				byte[] paddedSig = new byte[contentEstimated];
593 				System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
594 		
595 				PdfDictionary dic2 = new PdfDictionary();
596 				dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
597 				sap.close(dic2);
598 				
599 				fis.close();
600 	            reader.close();
601 	            fos.close();
602 	            fos = null;
603 	            
604 	            fileResult1.delete();
605 	            fileResult1 = fileResult2;
606 			}
607 			
608 			try {
609 				return new PDFSignature (fileResult1);
610 			} catch (SignatureNotFoundException e) {
611 				//-- No se va a dar, el fichero se acaba de firmar
612 				return null;
613 			} catch (PDFDocumentException e) {
614 				//-- No se va a dar, el fichero se acaba de firmar
615 				return null;
616 			}
617 			
618 		} catch (LoadingObjectException e) {
619 			logger.info("[PDFSignature.sign]::No se ha podido obtener la clave privada para firma", e);
620 			throw new LoadingObjectException ("No se ha podido obtener la clave privada para firma", e);
621 		} catch (SearchingException e) {
622 			logger.info("[PDFSignature.sign]::No se ha podido encontrar la clave privada para firma", e);
623 			throw new LoadingObjectException ("No se ha podido encontrar la clave privada para firma", e);
624 		} catch (IOException e) {
625 			logger.info("[PDFSignature.sign]::No se ha podido leer el fichero PDF o no se ha podido crear el fichero temporal con la firma", e);
626 			throw new PDFDocumentException ("No se ha podido leer el fichero PDF o no se ha podido crear el fichero temporal con la firma", e);
627 		} catch (DocumentException e) {
628 			logger.info("[PDFSignature.sign]::No se ha podido manejar alguna de las partes del fichero PDF", e);
629 			throw new PDFDocumentException ("No se ha podido manejar alguna de las partes del fichero PDF", e);
630 		} catch (InvalidKeyException e) {
631 			logger.info("[PDFSignature.sign]::La clave privada no es válida para realizar la firma", e);
632 			throw new SignatureException ("La clave privada no es válida para realizar la firma", e);
633 		} catch (NoSuchProviderException e) {
634 			logger.info("[PDFSignature.sign]::No se ha podido obtener el proveedor criptográfico", e);
635 			return null;
636 		} catch (NoSuchAlgorithmException e) {
637 			logger.info("[PDFSignature.sign]::No existen en el proveedor criptográfico los algoritmos de firma (SHA1WithRSA)", e);
638 			throw new SignatureException ("No existen en el proveedor criptográfico los algoritmos de firma (SHA1WithRSA)", e);
639 		} catch (CertificateCANotFoundException e) {
640 			logger.info("[PDFSignature.sign]::La lista de certificado de CA no contiene el emisor del certificado de firma", e);
641 			throw new CertificateCANotFoundException ("La lista de certificado de CA no contiene el emisor del certificado de firma", e);
642 		} catch (NormalizeCertificateException e) {
643 			logger.info("[PDFSignature.sign]::El certificado de la CA no es normalizable según el proveedor criptográfico de Arangi", e);
644 			throw new CertificateCANotFoundException ("El certificado de la CA no es normalizable según el proveedor criptográfico de Arangi", e);
645 		} 
646 
647 	}
648 	
649 	//-- Métodos públicos
650 	
651 	/* (non-Javadoc)
652 	 * @see es.accv.arangi.base.signature.ISignature#isValid(es.accv.arangi.base.certificate.validation.CAList)
653 	 */
654 	public ValidationResult[] isValid(CAList caList) throws HashingException, SignatureException, NormalizeCertificateException {
655 		
656 		logger.debug("[PDFSignature.isValid]::Entrada::" + caList);
657 		
658 		return isValidCommon(caList, null);
659 	}
660 
661 	/**
662 	 * En el caso de los PDF no tiene sentido realizar la validación sobre un 
663 	 * documento que no sea el mismo PDF. Por ello, el resultado de este método 
664 	 * será igual a llamar a {@link #isValid(CAList) isValid} con sólo un parámetro. 
665 	 */
666 	public ValidationResult[] isValid(IDocument document, CAList caList)
667 			throws HashingException, SignatureException,
668 			NormalizeCertificateException {
669 		return isValid (caList);
670 	}
671 
672 	/* (non-Javadoc)
673 	 * @see es.accv.arangi.base.signature.ISignature#isValid(java.util.List(es.accv.arangi.base.certificate.validation.service.CertificateValidationService))
674 	 */
675 	public ValidationResult[] isValid(List<CertificateValidationService> validationServices)
676 			throws HashingException, SignatureException, NormalizeCertificateException {
677 		
678 		logger.debug("[PDFSignature.isValid]::Entrada::" + validationServices);
679 		
680 		return isValidCommon(null, validationServices);
681 		
682 	}
683 
684 	/**
685 	 * En el caso de los PDF no tiene sentido realizar la validación sobre un 
686 	 * documento que no sea el mismo PDF. Por ello, el resultado de este método 
687 	 * será igual a llamar a {@link #isValid(List) isValid} con sólo un parámetro. 
688 	 */
689 	public ValidationResult[] isValid(IDocument document, List<CertificateValidationService> validationServices)
690 			throws HashingException, SignatureException, NormalizeCertificateException {
691 		return isValid(validationServices);
692 	}
693 
694 	/**
695 	 * Devuelve una cadena de texto con el tipo de la firma
696 	 * 
697 	 * @return Cadena de texto con el tipo de la firma
698 	 */
699 	public String getSignatureType () {
700 		return "PDF";
701 	}
702 	
703 	/**
704 	 * Obtiene las fechas de las firmas. Las obtiene de los sellos de 
705 	 * tiempo o, si éstos no existen, de la fecha indicada en la misma
706 	 * firma.
707 	 * 
708 	 * @return Fechas de firmado
709 	 */
710 	public Date[] getSigningTimes () {
711 		logger.debug("[PDFSignature.getSigningTimes]::Entrada");
712 		
713 		PdfReader reader;
714 		try {
715 			reader = new PdfReader(this.pdfFile.getAbsolutePath());
716 		} catch (IOException e) {
717 			// El fichero ya pasó la validación
718 			logger.info("[PDFSignature.getSigningTimes]::No se puede leer el contenido de este objeto", e);
719 			return null;
720 		}
721 		
722 		AcroFields af = reader.getAcroFields();
723 		ArrayList names = getRealSignatureNames(af);
724 		Date[] dates = new Date[names.size()];
725 		for (int i = 0; i < names.size(); i++) {
726 			String name = (String)names.get(i);
727 			
728 			//-- Validar que el PKCS#7 se corresponde con el documento
729 			PdfPKCS7 pkcs7 = af.verifySignature(name, CRYPTOGRAPHIC_PROVIDER_NAME);
730 
731 			//-- Comprobar que existe el sello de tiempos (no es obligatorio)
732 			if (pkcs7.getTimeStampDate() != null) {
733 				//-- hay sello de tiempos
734 				logger.debug("[PDFSignature.getSigningTimes]::" + i + ": Obteniendo fecha de sello de tiempos");
735 				dates[i] = pkcs7.getTimeStampDate().getTime();
736 			} else {
737 				//-- no hay sello de tiempos
738 				logger.debug("[PDFSignature.getSigningTimes]::" + i + ": Obteniendo fecha de la firma");
739 				dates[i] = pkcs7.getSignDate().getTime();
740 			}
741 		}
742 
743 		logger.debug("[PDFSignature.getSigningTimes]:: Devolviendo " + dates.length + " fechas de la firma");
744 		return dates;		
745 	}
746 	
747 	//-- Métodos privados
748 	
749 	private void initialize (File pdfFile) throws PDFDocumentException, SignatureNotFoundException {
750 		logger.debug("[PDFSignature.initialize]::Entrada::" + pdfFile);
751 		
752 		//-- Comprobar que el fichero es un PDF firmado
753 		try {
754 			PdfReader reader = new PdfReader(pdfFile.getAbsolutePath());
755 			AcroFields af = reader.getAcroFields();
756 			ArrayList names = getRealSignatureNames(af);
757 			if (names == null || names.isEmpty()) {
758 				logger.info("[PDFSignature.initialize]::El documento PDF no está firmado");
759 				pdfFile.delete(); 
760 				throw new SignatureNotFoundException ("El documento PDF no está firmado");
761 			}
762 			for (Iterator iterator = names.iterator(); iterator.hasNext();) {
763 				String name = (String) iterator.next();
764 				PdfPKCS7 pkcs7 = af.verifySignature(name, CRYPTOGRAPHIC_PROVIDER_NAME);
765 			}
766 		} catch (IOException e) {
767 			logger.info("[PDFSignature.initialize]::No se ha podido leer el fichero PDF", e);
768 			pdfFile.delete(); 
769 			throw new PDFDocumentException ("No se ha podido leer el fichero PDF", e);
770 		} 
771 		
772 		//-- Todo correcto, se inicializa
773 		this.pdfFile = pdfFile;
774 
775 	}
776 	
777 	/*
778 	 * Método que realiza la validación, tanto si se trata de validar con caList como
779 	 * con servicios de validación
780 	 */
781 	private ValidationResult[] isValidCommon(CAList caList, List<CertificateValidationService> validationServices) throws HashingException, SignatureException, NormalizeCertificateException {
782 		
783 		logger.debug("[PDFSignature.isValidCommon]::Entrada::" + caList);
784 		
785 		PdfReader reader;
786 		try {
787 			reader = new PdfReader(this.pdfFile.getAbsolutePath());
788 		} catch (IOException e) {
789 			// El fichero ya pasó la validación
790 			logger.info("[PDFSignature.isValidCommon]::No se puede leer el contenido de este objeto", e);
791 			return null;
792 		}
793 		AcroFields af = reader.getAcroFields();
794 		List<String> names = getRealSignatureNames(af);
795 		List<ValidationResult> results = new ArrayList<ValidationResult>(); 
796 		for (int i = 0; i < names.size(); i++) {
797 			String name = (String)names.get(i);
798 			
799 			//-- Validar que el PKCS#7 se corresponde con el documento
800 			PdfPKCS7 pkcs7 = af.verifySignature(name, CRYPTOGRAPHIC_PROVIDER_NAME);
801 			Date fechaFirma = pkcs7.getSignDate().getTime();
802 
803 			//-- Comprobar que existe el sello de tiempos (no es obligatorio)
804 			logger.debug("[PDFSignature.isValidCommon]::" + i + ": Obteniendo fecha de sello de tiempos");
805 			TimeStamp ts = null;
806 			if (pkcs7.getTimeStampDate() != null) {
807 				logger.debug("[PDFSignature.isValidCommon]::" + i + ": La firma no contiene sello de tiempos");
808 			
809 				try {
810 					//-- Obtener el sello de tiempos
811 					ts = new TimeStamp (pkcs7.getTimeStampToken().getEncoded());
812 					fechaFirma = ts.getTime();
813 				} catch (MalformedTimeStampException e) {
814 					logger.info("[PAdESLTVSignature.isValidCommon]::El sello de tiempos del documento no es correcto", e);
815 					throw new SignatureException ("El sello de tiempos del documento no es correcto", e);
816 				} catch (IOException e) {
817 					logger.info("[PAdESLTVSignature.isValidCommon]::Error de entrada/salida obteniendo el sello de tiempos", e);
818 					throw new SignatureException ("Error de entrada/salida obteniendo el sello de tiempos", e);
819 				}
820 			}
821 			
822 			//-- Obtener la respuesta OCSP si está en el PKCS#7 (CAdES)
823 			List<BasicOCSPResp> basicOcspResponses = null;
824 			if (pkcs7.getOcspResponses() != null && !pkcs7.getOcspResponses().isEmpty()) {
825 				try {
826 					basicOcspResponses = new ArrayList<BasicOCSPResp>();
827 					for(BasicOCSPResp bResp : pkcs7.getOcspResponses()) {
828 						basicOcspResponses.add(new BasicOCSPResp(BasicOCSPResponse.getInstance(bResp.getEncoded())));
829 					}
830 				} catch (IOException e) {
831 					logger.info("[PAdESLTVSignature.isValid]::No se ha podido parsear la respuesta OCSP en el CAdES de la firma " + i, e);
832 				}
833 			}
834 
835 			//-- Validar la firma a día de hoy
836 			ValidationResult validationResult;
837 			try {
838 				if (pkcs7.verify()) {
839 					validationResult = new ValidationResult (pkcs7.getSigningCertificate(), fechaFirma, ts, null);
840 				} else {
841 					validationResult = new ValidationResult (ValidationResult.RESULT_SIGNATURE_NOT_MATCH_DATA, pkcs7.getSigningCertificate(), fechaFirma, ts, null);
842 				}
843 			} catch (java.security.SignatureException e) {
844 				logger.info("[PDFSignature.isValidCommon]::Error en una de las firmas del PDF", e);
845 				throw new SignatureException ("Error en una de las firmas del PDF", e);
846 			}
847 			
848 			if (validationResult.isValid()) {
849 				//-- Validar el sello de tiempos (si existe)
850 				if (ts != null) {
851 					try {
852 						if (!ts.isValid()) {
853 							validationResult.setResult(ValidationResult.RESULT_INVALID_TIMESTAMP);
854 							logger.debug("[PDFSignature.isValidCommon]::" + i + ": Resultado validación: El sello de tiempos no es válido");
855 						} else {
856 							//-- Si se ha definido un hueco para firmar pero no se ha rellenado llegamos hasta aquí pero
857 							//-- no hay certificado de firma
858 							Certificate signingCertificate = getSigningCertificate(pkcs7.getCertificates());
859 							if (signingCertificate == null) {
860 								logger.info("Hay un hueco sin firma con el nombre '" + name + "'");
861 								continue;
862 							}
863 
864 							//-- Si el PKCS#7 es un CAdES se podría validar con esa información
865 							int resultadoValidacion = validateFromCAdES(basicOcspResponses, signingCertificate, signingCertificate, ts);
866 							if (resultadoValidacion == ValidationResult.RESULT_VALID) {
867 								validationResult.setResult(resultadoValidacion);
868 							} else {
869 								
870 								//-- Obtener el certificado y validarlo cuando indica el sello de tiempos (siempre que no haya caducado)
871 								if (caList != null) {
872 									logger.debug("[PDFSignature.isValidCommon]::Se valida mediante CAList");
873 									ValidateCertificate certificate = new ValidateCertificate (signingCertificate.toDER(), caList);
874 									if (certificate.isActive()) {
875 										validationResult.setResult(certificate.validate(pkcs7.getTimeStampDate().getTime()));
876 									} else {
877 										validationResult.setResult(certificate.validate());
878 									}
879 								} else {
880 									logger.debug("[PDFSignature.isValidCommon]::Se valida mediante servicios de validación");
881 									if (signingCertificate.isActive()) {
882 										validationResult.setResult(signingCertificate.validate(validationServices, pkcs7.getTimeStampDate().getTime()).getResult());
883 									} else {
884 										validationResult.setResult(signingCertificate.validate(validationServices).getResult());
885 									}
886 								}
887 								
888 							}
889 							results.add(validationResult);
890 							logger.debug("[PDFSignature.isValidCommon]::" + i + ": Resultado validación: " + validationResult.getResult());
891 						}
892 						
893 					} catch (CertificateCANotFoundException e) {
894 						logger.info("[PDFSignature.isValidCommon]::Certificado" + i + " no pertenece a ninguna de las CAs ");
895 						results.add(new ValidationResult(ValidationResult.RESULT_CERTIFICATE_CHAIN_VALIDATION_INVALID, pkcs7.getSigningCertificate(), null, ts, null));
896 						continue;
897 					} catch (MalformedTimeStampException e) {
898 						logger.info("[PAdESLTVSignature.isValidCommon]::El sello de tiempos del documento no es correcto", e);
899 						throw new SignatureException ("El sello de tiempos del documento no es correcto", e);
900 					} 
901 				} else {
902 					results.add(validationResult);
903 					logger.debug("[PDFSignature.isValidCommon]::" + i + ": Resultado validación si sello de tiempos: " + validationResult.getResult());
904 				}
905 			}
906 
907 		}
908 		
909 		reader.close();
910 		
911 		return results.toArray(new ValidationResult[0]);
912 	}
913 
914 }