View Javadoc

1   /**
2    * LICENCIA LGPL:
3    * 
4    * Esta librería es Software Libre; Usted puede redistribuirla y/o modificarla
5    * bajo los términos de la GNU Lesser General Public License (LGPL) tal y como 
6    * ha sido publicada por la Free Software Foundation; o bien la versión 2.1 de 
7    * la Licencia, o (a su elección) cualquier versión posterior.
8    * 
9    * Esta librería se distribuye con la esperanza de que sea útil, pero SIN 
10   * NINGUNA GARANTÍA; tampoco las implícitas garantías de MERCANTILIDAD o 
11   * ADECUACIÓN A UN PROPÓSITO PARTICULAR. Consulte la GNU Lesser General Public 
12   * License (LGPL) para más detalles
13   * 
14   * Usted debe recibir una copia de la GNU Lesser General Public License (LGPL) 
15   * junto con esta librería; si no es así, escriba a la Free Software Foundation 
16   * Inc. 51 Franklin Street, 5º Piso, Boston, MA 02110-1301, USA o consulte
17   * <http://www.gnu.org/licenses/>.
18   *
19   * Copyright 2011 Agencia de Tecnología y Certificación Electrónica
20   */
21  package es.accv.arangi.signature;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  
27  import es.accv.arangi.base.device.DeviceManager;
28  import es.accv.arangi.base.document.IDocument;
29  import es.accv.arangi.base.exception.certificate.CertificateCANotFoundException;
30  import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
31  import es.accv.arangi.base.exception.device.AliasNotFoundException;
32  import es.accv.arangi.base.exception.device.LoadingObjectException;
33  import es.accv.arangi.base.exception.document.HashingException;
34  import es.accv.arangi.base.exception.signature.AlgorithmNotSuitableException;
35  import es.accv.arangi.base.exception.signature.InvalidCertificateException;
36  import es.accv.arangi.base.exception.signature.PDFDocumentException;
37  import es.accv.arangi.base.exception.signature.RetrieveOCSPException;
38  import es.accv.arangi.base.exception.signature.SignatureException;
39  import es.accv.arangi.base.exception.signature.SignatureNotFoundException;
40  import es.accv.arangi.base.exception.timestamp.ResponseTimeStampException;
41  import es.accv.arangi.base.signature.PDFSignature;
42  import es.accv.arangi.base.util.validation.ValidationResult;
43  import es.accv.arangi.device.ACCVDeviceManager;
44  import es.accv.arangi.timestamp.TimeStamp;
45  import es.accv.arangi.util.ArangiUtil;
46  
47  /**
48   * Clase para manejar firmas en PDF (PAdES-A) según los estándares 
49   * <a href="http://www.etsi.org/deliver/etsi_ts/102700_102799/10277804/01.01.01_60/ts_10277804v010101p.pdf" target="estandar">ETSI TS 102 778-4 V1.2.1</a> 
50   * y <a href="http://www.telecomforum.eu/deliver/etsi_ts/119100_119199/11914402/01.01.01_60/ts_11914402v010101p.pdf" target="estandar">ETSI TS 119 144-2 V1.1.1</a>,
51   * basado en la norma <a href="http://www.iso.org/iso/catalogue_detail.htm?csnumber=51502" target="estandar">ISO 32000-1</a><br><br>
52   * 
53   * Por motivos de compatibilidad la clase sigue llamándose PAdESLTVSignature 
54   * aunque sería más correcto llamarla PAdESASignature, ya que siempre se
55   * trabajará con sellos de tiempo de documento, que son lo que diferencia
56   * a un PAdES-LTV de un PAdES-A según la norma ETSI TS 119 144-2.<br><br>
57   * 
58   * La norma ETSI TS 119 144-2 exige añadir más información de validación que
59   * la que se establecía en la ETSI TS 102 778-4. Por ello, las firmas realizadas 
60   * con versiones de Arangí anteriores a la 1.1.4 no serán consideradas válidas por 
61   * dicha versión o versiones posteriores. Para evitar este incoveniente a aquellos 
62   * desarrollos que se hayan realizado con versiones antiguas es posible activar un 
63   * flag estático antes de la validación que permitirá que en las validaciones no 
64   * se tenga en cuenta la información extra exigida por la nueva norma:<br><br>
65   * 
66   * <code>
67   * olderVersionsAllowed = true;
68   * </code><br><br>
69   * 
70   * Este tipo de firmas cumplen con los requisitos para ser firmas longevas. En 
71   * ellas se incluye toda la información necesaria para validar los certificados
72   * de las firmas. También se añade un sello de tiempos para el documento que
73   * da garantias sobre la fecha en la que se realizaron las firmas.<br><br>
74   * 
75   * Las últimas versiones de Adobe Acrobat ya utilizan este tipo de firmas, aunque 
76   * existen algunas diferencias entre las firmas PAdES-LTV obtenidas por Adobe y las
77   * normas de la ETSI. En la documentación de Arangí debe haber una explicación de
78   * cuales son estas diferencias.<br><br>
79   * 
80   * Para evitar problemas de saturación de memoria con ficheros PDF muy grandes,
81   * esta clase siempre trabajará sobre un objeto java.io.File. Si el objeto no se 
82   * inicializa con un fichero se creará un archivo temporal en la carpeta temporal 
83   * de Arangi: {@link #getArangiTemporalFolder() getArangiTemporalFolder}.<br><br>
84   * 
85   * Existen dos métodos para obtener una firma PAdES-LTV, dependiendo de si se desea 
86   * una firma visible o invisible. En el caso de las firmas visibles hay que proporcionar 
87   * al método las coordenadas de las esquinas inferior izquierda y superior derecha, así 
88   * como el número de página donde se desea ubicar la firma. También es posible asociar
89   * una imagen a la firma.<br><br>
90   * 
91   * <code>
92   * KeyStoreManager manager = new KeyStoreManager (...,...);<br>
93   * ByteArrayDocument documentPDF = new ByteArrayDocument (...);<br><br>
94   * 
95   * //-- Firma invisible<br>
96   * PAdESLTVSignature signatureInv = PAdESLTVSignature.sign (new ACCVDeviceManager[] {manager},documentPDF, "Porque quiero firmarlo");<br><br>
97   * 
98   * //-- Firma visible<br>
99   * PAdESLTVSignature signatureVis = PAdESLTVSignature.sign (new ACCVDeviceManager[] {manager},documentPDF, "Porque quiero firmarlo",
100  *      true, Util.readStream(ClassLoader.getSystemResourceAsStream("signature/chip.gif")), 100, 100, 300, 200, 1);<br><br>
101  * </code>
102  * 
103  * En la página 1 de la segunda firma, en la ubicación indicada por las coordenadas, se 
104  * verá la imagen chip.gif como una firma realizada sobre el PDF.<br><br>
105  * 
106  * El primer parámetro de la firma es un array de managers, ya que es posible realizar 
107  * varias firmas a la vez. Es importante destacar que una vez se han añadido los campos 
108  * que hacen de un PDF firmado un PAdES-LTV ya no es posible volver a firmar el documento. 
109  * Si un documento se va firmar por varias personas de forma no simultanea lo que se debe 
110  * hacer es realizar las firmas en un PDF con firma simple y, tras la última firma 
111  * completar a PAdES-LTV. Por supuesto, entre el principio y el fin del proceso ninguno 
112  * de los certificados implicados podrá caducar o ser revocado.<br><br>
113  * 
114  * <code>
115  * //-- Primera firma<br>
116  * KeyStoreManager manager1 = new KeyStoreManager (...,...);<br>
117  * ByteArrayDocument documentPDF = new ByteArrayDocument (...);<br>
118  * PDFSignature signature = PDFSignature.sign (new ACCVDeviceManager[] {manager1},documentPDF, "Firma 1");<br><br>
119  * 
120  * //-- Segunda firma (días más tarde)<br>
121  * KeyStoreManager manager2 = new KeyStoreManager (...,...);<br>
122  * documentPDF = new ByteArrayDocument (signature.toByteArray());<br>
123  * signature = PDFSignature.sign (new ACCVDeviceManager[] {manager2},documentPDF, "Firma 2");<br><br>
124  * 
125  * //-- Completar la firma para que sea PAdES-LTV<br>
126  * PAdESLTVSignature padesLTV = PAdESLTVSignature.completeToPAdESLTV(signature);<br><br>
127  * </code><br><br>
128  * 
129  * La validez de una firma longeva se halla limitada a la vida del certificado del último sello 
130  * de tiempos de documento que contiene el PDF, aunque erroneamente se suele pensar que una firma 
131  * longeva puede validarse eternamente. Por ejemplo, el certificado de la TSA de la ACCV caducará 
132  * el 18 de Noviembre de 2016, lo que implica que a partir de esa fecha las firmas PAdES-LTV 
133  * realizadas con la TSA de la ACCV dejarán de ser válidas. Con el objeto de alargar la vida de
134  * una firma longeva será necesario realizar un resellado de la misma cuando se cambie el certificado
135  * de la TSA. Este sería el código para realizar un resellado: <br><br>
136  * 
137  * <code>
138  * ByteArrayDocument document = new ByteArrayDocument (...);<br>
139  * PAdESLTVSignature signature = new PAdESLTVSignature(document);<br>
140  * signature.addDocumentTimeStamp();<br>
141  * signature.save(...);<br>
142  * </code>
143  *  
144  * @author <a href="mailto:jgutierrez@accv.es">José M Gutiérrez</a>
145  */
146 public class PAdESLTVSignature extends es.accv.arangi.base.signature.PAdESLTVSignature {
147 
148 	/**
149 	 * Inicializa el objeto con el contenido de un fichero PDF firmado.
150 	 * 
151 	 * @param pdfContentBytes Array de bytes con el contenido del fichero PDF firmado
152 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
153 	 * 	ser leído
154 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
155 	 * @throws IOException No se puede crear el fichero temporal
156 	 */
157 	public PAdESLTVSignature(byte[] pdfContentBytes) throws PDFDocumentException,
158 			SignatureNotFoundException, IOException {
159 		super(pdfContentBytes);
160 	}
161 
162 	/**
163 	 * Inicializa el objeto con un fichero PDF firmado.
164 	 * 
165 	 * @param pdfFile Fichero PDF firmado
166 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
167 	 * 	ser leído
168 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
169 	 */
170 	public PAdESLTVSignature(File pdfFile) throws PDFDocumentException,
171 			SignatureNotFoundException {
172 		super(pdfFile);
173 	}
174 
175 	/**
176 	 * Inicializa el objeto con un documento que debe contener un fichero PDF firmado.
177 	 * 
178 	 * @param document Documento con el contenido del fichero PDF firmado
179 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
180 	 * 	ser leído
181 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
182 	 * @throws IOException No se puede crear el fichero temporal
183 	 */
184 	public PAdESLTVSignature(IDocument document) throws PDFDocumentException,
185 			SignatureNotFoundException, IOException {
186 		super(document);
187 	}
188 
189 	/**
190 	 * Inicializa el objeto con un stream de lectura al contenido de un fichero PDF firmado.
191 	 * 
192 	 * @param streamPDF Stream de lectura al contenido del fichero PDF firmado
193 	 * @throws PDFDocumentException El fichero no es un PDF correcto o bien no puede 
194 	 * 	ser leído
195 	 * @throws SignatureNotFoundException El fichero es un PDF pero no está firmado
196 	 * @throws IOException No se puede crear el fichero temporal
197 	 */
198 	public PAdESLTVSignature(InputStream streamPDF) throws PDFDocumentException,
199 			SignatureNotFoundException, IOException {
200 		super(streamPDF);
201 	}
202 
203 	/*
204 	 * Sólo para pasar de una firma base a una de arangi
205 	 */
206 	private PAdESLTVSignature(es.accv.arangi.base.signature.PAdESLTVSignature signature) {
207 		super(signature);
208 	}
209 
210 	/**
211 	 * Obtiene un objeto {@link PAdESLTVSignature PDFSignature} tras firmar un documento PDF.
212 	 * La firma es invisible.
213 	 * 
214 	 * @param managers Dispositivos criptográfico que realizarán la firma
215 	 * @param pdfDocument Documento PDF a firmar
216 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
217 	 * 	nulo se escribirá un texto por defecto.
218 	 * @return Documento PDF firmado, con sello de tiempos y respuesta OCSP
219 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
220 	 * 	realizar la firma no existe
221 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
222 	 * 	realizar la firma
223 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
224 	 * @throws SignatureException No se puede realizar la firma
225 	 * @throws RetrieveOCSPException No es posible obtener una respuesta OCSP para
226 	 * 	asociarla a la firma
227 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
228 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
229 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
230 	 * 	el proveedor criptográfico de Arangi
231 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
232 	 * @throws NormalizeCertificateException Alguno de los certificados de firma o de sus cadenas
233 	 * 	de certificación no puede ser normalizado
234 	 */
235 	public static PAdESLTVSignature sign (ACCVDeviceManager managers[], IDocument pdfDocument, String reason) throws AliasNotFoundException, 
236 		LoadingObjectException, PDFDocumentException, SignatureException, RetrieveOCSPException, HashingException, CertificateCANotFoundException, 
237 		InvalidCertificateException, NormalizeCertificateException {
238 		
239 		try {
240 			return sign (managers, pdfDocument, null, reason, false, null, -1, -1, -1, -1, 0);
241 		} catch (AlgorithmNotSuitableException e) {
242 			throw new SignatureException("El algoritmo por defecto no debería provocar este error", e);
243 		}
244 	}
245 	
246 	/**
247 	 * Obtiene un objeto {@link PAdESLTVSignature PDFSignature} tras firmar un documento PDF.<br><br>
248 	 * 
249 	 * Si la firma es visible se le puede asociar una imagen. El punto 0,0 de la página 
250 	 * se encuentra en la esquina inferior izquierda de la misma. Un página tiene 
251 	 * aproximadamente unas dimensiones de 580x850. 
252 	 * 
253 	 * @param managers Dispositivos criptográfico que realizarán la firma
254 	 * @param pdfDocument Documento PDF a firmar
255 	 * @param digitalSignatureAlgorithm Algoritmo de firma (si nulo algoritmo por defecto)
256 	 * @param reason Texto que aparecerá junto a la firma como razón. Si se pasa un valor
257 	 * 	nulo se escribirá un texto por defecto.
258 	 * @param isVisible Si tiene un valor cierto se creará una firma visible.
259 	 * @param image Imagen de la firma. Puede tener un valor nulo.
260 	 * @param llX Posición X de la esquina inferior izquierda de la firma en la página (caso de ser visible)
261 	 * @param llY Posición Y de la esquina inferior izquierda de la firma en la página (caso de ser visible) 
262 	 * @param urX Posición X de la esquina superior derecha de la firma en la página (caso de ser visible)
263 	 * @param urY Posición Y de la esquina superior derecha de la firma en la página (caso de ser visible)
264 	 * @param page Página en la que se situará la firma si ésta es visible (1 es la primera página)
265 	 * @return Documento PDF firmado, con sello de tiempos y respuesta OCSP
266 	 * @throws AliasNotFoundException El alias donde se encuentra la clave privada usada para
267 	 * 	realizar la firma no existe
268 	 * @throws LoadingObjectException No ha sido posible cargar la clave privada usada para
269 	 * 	realizar la firma
270 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
271 	 * @throws SignatureException No se puede realizar la firma
272 	 * @throws RetrieveOCSPException No es posible obtener una respuesta OCSP para
273 	 * 	asociarla a la firma
274 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
275 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
276 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
277 	 * 	el proveedor criptográfico de Arangi
278 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
279 	 * @throws NormalizeCertificateException Alguno de los certificados de firma o de sus cadenas
280 	 * 	de certificación no puede ser normalizado
281 	 * @throws AlgorithmNotSuitableException El algoritmo de firma pasado no sirve para realizar la firma
282 	 */
283 	public static PAdESLTVSignature sign (ACCVDeviceManager[] managers, IDocument pdfDocument, 
284 			String digitalSignatureAlgorithm, String reason, boolean isVisible, byte[] image, 
285 			float llX, float llY, float urX, float urY, int page) throws AliasNotFoundException, LoadingObjectException, PDFDocumentException, 
286 			SignatureException, RetrieveOCSPException, HashingException, CertificateCANotFoundException, InvalidCertificateException, NormalizeCertificateException, AlgorithmNotSuitableException {
287 		
288 		//-- Obtener alias
289 		String[] alias = new String [managers.length];
290 		for (int i = 0; i < managers.length; i++) {
291 			alias[i] = managers[i].getSignatureAlias();
292 		}
293 		
294 		//-- Pasar los managers a objetos de arangí base
295 		DeviceManager[] deviceManagers = new DeviceManager[managers.length];
296 		for (int i = 0; i < deviceManagers.length; i++) {
297 			deviceManagers[i] = (DeviceManager) managers[i];
298 		}
299 		
300 		return new PAdESLTVSignature (sign (deviceManagers, alias, pdfDocument, digitalSignatureAlgorithm,
301 				TimeStamp.getURLACCVTSA(), null, null, ArangiUtil.getACCVCaList(), reason, isVisible, image, 
302 				llX, llY, urX, urY, page));
303 
304 	}
305 	
306 	/**
307 	 * Método que completa un fichero PDF firmado a PAdES-LTV. La firma del PDF firmado ha de ser
308 	 * correcta y los certificados han de ser válidos en este momento.
309 	 * 
310 	 * @param signature PDF firmado
311 	 * @return PAdES-LTV
312 	 * @throws PDFDocumentException El documento no es un fichero PDF o es un PDF mal formado
313 	 * @throws SignatureException Error completando la firma
314 	 * @throws RetrieveOCSPException No es posible obtener una respuesta OCSP para
315 	 * 	asociarla a la firma
316 	 * @throws HashingException Excepción obteniendo el hash que será sellado por la TSA
317 	 * @throws CertificateCANotFoundException La lista de certificado de CA no contiene el 
318 	 * 	emisor del certificado de firma o existe pero tiene un formato no normalizable por 
319 	 * 	el proveedor criptográfico de Arangi
320 	 * @throws InvalidCertificateException El certificado con el que se firma está revocado
321 	 * @throws NormalizeCertificateException Alguno de los certificados de firma o de sus cadenas
322 	 * 	de certificación no puede ser normalizado
323 	 */
324 	public static PAdESLTVSignature completeToPAdESLTV (PDFSignature signature) throws SignatureException, 
325 		RetrieveOCSPException, InvalidCertificateException, NormalizeCertificateException, PDFDocumentException, CertificateCANotFoundException, 
326 		HashingException {
327 		
328 		return new PAdESLTVSignature(completeToPAdESLTV(signature, TimeStamp.getURLACCVTSA(), ArangiUtil.getACCVCaList()));
329 		
330 	}
331 	
332 	/**
333 	 * Añade un sello de tiempos al documento PDF (document time-stamp).
334 	 * 
335 	 * @throws SignatureException Error leyendo o guardando objetos de la firma
336 	 * @throws RetrieveOCSPException No es posible obtener una respuesta OCSP para el 
337 	 * 	certificado del último sello de tiempos del documento
338 	 * @throws ResponseTimeStampException No es posible obtener una respuesta del servidor
339 	 * 	de sello de tiempos
340 	 * @throws CertificateCANotFoundException El certificado del último sello de tiempos del 
341 	 * 	documento no pertenece a ninguna de las Autoridades de Certificación de confianza
342 	 */
343 	public void addDocumentTimeStamp () throws SignatureException, RetrieveOCSPException, ResponseTimeStampException, CertificateCANotFoundException {
344 		addDocumentTimeStamp(TimeStamp.getURLACCVTSA(), ArangiUtil.getACCVCaList());
345 	}
346 	
347 	/**
348 	 * Determina si la firma es válida
349 	 * 
350 	 * @return Cierto si la firma es válida
351 	 * @throws SignatureException Error tratando el objeto firma
352 	 * @throws HashingException Error obteniendo el hash del documento
353 	 * @throws NormalizeCertificateException Alguno de los certificados no puede ser 
354 	 * 	normalizado al formato reconocido por el proveedor criptográfico de Arangi o su 
355 	 * 	firma no es correcta o no puede ser analizada
356 	 */
357 	public ValidationResult[] isValid() throws HashingException, SignatureException, NormalizeCertificateException {
358 		return isValid (ArangiUtil.getACCVCaList());
359 	}
360 	
361 }