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 }