1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.FileNotFoundException;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.security.cert.CertificateEncodingException;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36
37 import org.apache.log4j.Logger;
38 import org.bouncycastle.asn1.x509.KeyPurposeId;
39 import org.bouncycastle.cert.ocsp.BasicOCSPResp;
40
41 import com.itextpdf.text.pdf.AcroFields;
42 import com.itextpdf.text.pdf.PdfName;
43 import com.itextpdf.text.pdf.PdfPKCS7;
44 import com.itextpdf.text.pdf.PdfReader;
45
46 import es.accv.arangi.base.certificate.Certificate;
47 import es.accv.arangi.base.certificate.validation.CertificateOCSPResponse;
48 import es.accv.arangi.base.certificate.validation.OCSPResponse;
49 import es.accv.arangi.base.certificate.validation.ValidateCertificate;
50 import es.accv.arangi.base.document.IDocument;
51 import es.accv.arangi.base.exception.CertificateException;
52 import es.accv.arangi.base.exception.certificate.NormalizeCertificateException;
53 import es.accv.arangi.base.exception.document.HashingException;
54 import es.accv.arangi.base.exception.signature.SignatureException;
55 import es.accv.arangi.base.exception.timestamp.MalformedTimeStampException;
56 import es.accv.arangi.base.timestamp.TimeStamp;
57 import es.accv.arangi.base.util.Util;
58 import es.accv.arangi.base.util.validation.ValidationResult;
59
60
61
62
63
64 public abstract class BasePDFSignature extends Signature {
65
66 static Logger logger = Logger.getLogger(BasePDFSignature.class);
67
68
69
70
71 protected static final String DIGITALLY_SIGNED_TEXT = "Firmado digitalmente por";
72
73
74
75
76 protected static final String STATUS_TEXT = "Estado";
77
78
79
80
81 protected File pdfFile;
82
83
84
85
86
87
88 public Certificate[] getCertificates() {
89 logger.debug("[PDFSignature.getCertificates]::Entrada");
90
91 PdfReader reader;
92 try {
93 reader = new PdfReader(this.pdfFile.getAbsolutePath());
94 } catch (IOException e) {
95
96 logger.info("[PDFSignature.getCertificates]::No se puede leer el contenido de este objeto", e);
97 return null;
98 }
99 AcroFields af = reader.getAcroFields();
100 List<String> names = getRealSignatureNames(af);
101 List<Certificate> result = new ArrayList<Certificate>();
102 for (int i = 0; i < names.size(); i++) {
103 String name = (String)names.get(i);
104
105
106 PdfPKCS7 pkcs7 = af.verifySignature(name, CRYPTOGRAPHIC_PROVIDER_NAME);
107 try {
108 Certificate signingCertificate = getSigningCertificate(pkcs7.getCertificates());
109 if (signingCertificate != null) {
110 result.add(getSigningCertificate (pkcs7.getCertificates()));
111 }
112 } catch (NormalizeCertificateException e) {
113 logger.info("[PDFSignature.getCertificates]::No se puede normalizar el certificado::" + pkcs7.getCertificates()[0], e);
114 }
115 }
116
117 reader.close();
118
119 return result.toArray(new Certificate[0]);
120 }
121
122
123
124
125 public ValidationResult[] isValidSignatureOnly() throws SignatureException {
126
127 logger.debug("[BasePDFSignature.isValidSignatureOnly]::Entrada");
128
129 PdfReader reader;
130 try {
131 reader = new PdfReader(this.pdfFile.getAbsolutePath());
132 } catch (IOException e) {
133
134 logger.info("[BasePDFSignature.isValidSignatureOnly]::No se puede leer el contenido de este objeto", e);
135 return null;
136 }
137 AcroFields af = reader.getAcroFields();
138 ArrayList names = getRealSignatureNames(af);
139 ValidationResult[] result = new ValidationResult [names.size()];
140 for (int i = 0; i < names.size(); i++) {
141 String name = (String)names.get(i);
142 PdfPKCS7 pkcs7 = af.verifySignature(name, CRYPTOGRAPHIC_PROVIDER_NAME);
143 try {
144 if (pkcs7.verify()) {
145 result [i] = new ValidationResult (pkcs7.getSigningCertificate());
146 } else {
147 result [i] = new ValidationResult (ValidationResult.RESULT_SIGNATURE_NOT_MATCH_DATA, pkcs7.getSigningCertificate());
148 }
149 } catch (java.security.SignatureException e) {
150 logger.info("[BasePDFSignature.isValidSignatureOnly]::Error en una de las firmas del PDF", e);
151 throw new SignatureException ("Error en una de las firmas del PDF", e);
152 }
153 }
154
155 return result;
156 }
157
158
159
160
161
162
163
164 public ValidationResult[] isValidSignatureOnly(IDocument document)
165 throws HashingException, SignatureException {
166 return isValidSignatureOnly();
167 }
168
169
170
171
172
173
174
175 public void save (File file) throws IOException {
176 logger.debug ("[PDFSignature.save]::Entrada::" + file);
177
178 FileInputStream fis = null;
179 try {
180 fis = new FileInputStream(this.pdfFile);
181 Util.saveFile(file, fis);
182 } finally {
183 if (fis != null) {
184 fis.close();
185 }
186 }
187 }
188
189
190
191
192
193
194
195 public void save (OutputStream out) throws IOException {
196 logger.debug ("[PDFSignature.save]::Entrada::" + out);
197
198 FileInputStream fis = null;
199 try {
200 fis = new FileInputStream(this.pdfFile);
201 Util.save(out, fis);
202 } finally {
203 if (fis != null) {
204 fis.close();
205 }
206 }
207 }
208
209
210
211
212
213
214
215 public byte[] toByteArray () throws IOException {
216 return Util.loadFile(this.pdfFile);
217 }
218
219
220
221
222
223
224
225
226
227 public static BasePDFSignature getPDFObject (byte[] signature) throws SignatureException {
228 return getPDFObject(new ByteArrayInputStream(signature));
229 }
230
231
232
233
234
235
236
237
238
239
240 public static BasePDFSignature getPDFObject (File signature) throws SignatureException, FileNotFoundException {
241 return getPDFObject(new FileInputStream(signature));
242 }
243
244
245
246
247
248
249
250
251
252 public static BasePDFSignature getPDFObject (InputStream signature) throws SignatureException {
253 logger.debug("[BasePDFSignature.getPDFObject]::Entrada::" + signature);
254
255
256 FileOutputStream fos = null;
257 File fileTemp;
258 try {
259 fileTemp = getFileTemp ();
260 Util.saveFile(fileTemp, signature);
261 } catch (IOException e) {
262 logger.info("[PAdESLTVSignature(byte[])::No se puede crear el fichero temporal o no se puede escribir en él", e);
263 throw new SignatureException ("No se puede crear el fichero temporal o no se puede escribir en él", e);
264 } finally {
265 if (fos != null) {
266 try {
267 fos.close();
268 } catch (IOException e) {
269 logger.info("[PAdESLTVSignature(byte[])::No se puede cerrar el stream de lectura al fichero temporal", e);
270 throw new SignatureException ("No se puede cerrar el stream de lectura al fichero temporal", e);
271 }
272 }
273 }
274
275 PdfReader reader;
276 try {
277 reader = new PdfReader(fileTemp.getAbsolutePath());
278 } catch (IOException e) {
279
280 logger.info("[BasePDFSignature.getPDFObject]::La firma no parece ser un PDF", e);
281 throw new SignatureException("La firma no parece ser un PDF", e);
282 }
283
284 try {
285 AcroFields af = reader.getAcroFields();
286 ArrayList names = af.getSignatureNames();
287 if (names == null || names.isEmpty()) {
288 logger.info("[BasePDFSignature.getPDFObject]::El documento PDF no está firmado");
289 throw new SignatureException ("El documento PDF no está firmado");
290 }
291 if (names.size() == 1) {
292 logger.debug("[BasePDFSignature.getPDFObject]::El documento PDF no es PAdES-LTV: sólo hay una firma y debería haber firma y sello de tiempos");
293 return new PDFSignature(fileTemp);
294 }
295
296
297 if (reader.getCatalog().get(new PdfName("DSS")) == null) {
298 logger.debug("[BasePDFSignature.getPDFObject]::El documento PDF no es PAdES-LTV: no contiene un diccionario Document Security Store (DSS)");
299 return new PDFSignature(fileTemp);
300 }
301
302
303 boolean encontrado = false;
304 for (Iterator iterator = names.iterator(); iterator.hasNext();) {
305 String name = (String) iterator.next();
306 if (af.getSignatureDictionary(name) != null && af.getSignatureDictionary(name).get(PdfName.SUBFILTER) != null &&
307 af.getSignatureDictionary(name).get(PdfName.SUBFILTER).equals(new PdfName("ETSI.RFC3161"))) {
308 encontrado = true;
309 break;
310 }
311 }
312 if (!encontrado) {
313 logger.debug("[BasePDFSignature.getPDFObject]::El documento PDF no es PAdES-LTV: no contiene un sello de tiempos para todo el documento)");
314 return new PDFSignature(fileTemp);
315 } else {
316 return new PAdESLTVSignature(fileTemp);
317 }
318
319 } catch (Exception e) {
320 logger.info("[BasePDFSignature.getPDFObject]::Ha ocurrido un error cargando el objeto", e);
321 throw new SignatureException ("El documento PDF no está firmado");
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335 public static ISignature getSignatureInstance (byte[] bSignature) throws Exception {
336 return getPDFObject(bSignature);
337 }
338
339
340
341
342
343 public void close() {
344 logger.debug("[BasePDFSignature.close]::Eliminando el fichero temporal: " + pdfFile);
345 pdfFile.delete();
346 logger.debug("[BasePDFSignature.close]::Fichero temporal eliminado: " + pdfFile);
347 }
348
349
350
351
352
353
354 protected static Certificate getSigningCertificate(java.security.cert.Certificate[] arrayCertificates) throws NormalizeCertificateException {
355
356
357 if (arrayCertificates.length == 1) {
358 Certificate cert;
359 try {
360 cert = new Certificate (Util.getCertificate(arrayCertificates[0].getEncoded()));
361 } catch (CertificateEncodingException e) {
362 throw new NormalizeCertificateException("No es posible obtener el certificado", e);
363 }
364 if (cert.isSelfSigned()) {
365 logger.debug("El certificado de firma es un autofirmado");
366 return cert;
367 }
368 }
369
370
371 HashMap<String, Certificate> hmCertificates = new HashMap<String, Certificate>();
372 Certificate[] certificates = new Certificate[arrayCertificates.length];
373 for (int i = 0; i < arrayCertificates.length; i++) {
374 try {
375 certificates[i] = new Certificate (Util.getCertificate(arrayCertificates[i].getEncoded()));
376 } catch (CertificateEncodingException e) {
377 throw new NormalizeCertificateException("No es posible obtener el certificado", e);
378 }
379 hmCertificates.put(certificates[i].getSubjectDN(), certificates[i]);
380 }
381
382
383 for (int i = 0; i < certificates.length; i++) {
384 hmCertificates.remove(certificates[i].getIssuerDN());
385
386
387 try {
388 List<String> extendedKeyUsages = certificates[i].getExtendedKeyUsage();
389 if (extendedKeyUsages.contains(KeyPurposeId.id_kp_OCSPSigning.getId())) {
390 hmCertificates.remove(certificates[i].getSubjectDN());
391 }
392 } catch (CertificateException e) {
393 logger.debug("No es posible obtener los usos de clave extendidos del certificado: " + certificates[i].getCommonName());
394 }
395 }
396
397
398 if (hmCertificates.isEmpty()) {
399 return null;
400 }
401
402 return hmCertificates.get(hmCertificates.keySet().iterator().next());
403 }
404
405
406
407
408
409
410
411
412 protected static ArrayList<String> getRealSignatureNames(AcroFields af) {
413
414 ArrayList<String> originalNames = af.getSignatureNames();
415 ArrayList<String> realNames = new ArrayList<String>();
416 for (int i = originalNames.size() - 1; i > -1; i--) {
417 String name = (String) originalNames.get(i);
418 if (af.getSignatureDictionary(name).get(PdfName.SUBFILTER) == null ||
419 !af.getSignatureDictionary(name).get(PdfName.SUBFILTER).equals(new PdfName("ETSI.RFC3161"))) {
420 realNames.add(name);
421 }
422
423 }
424
425 return realNames;
426 }
427
428
429
430
431
432 protected static File getFileTemp() throws IOException {
433 File temp = File.createTempFile("signed", "pdf", getArangiTemporalFolder());
434 temp.deleteOnExit();
435
436 return temp;
437 }
438
439
440
441
442
443 protected static int validateFromCAdES(List<BasicOCSPResp> basicOcspResponses, Certificate certificadoAValidar,
444 Certificate certificadoFirma, TimeStamp ts) throws MalformedTimeStampException {
445 Certificate certificadoTS = ts.getSignatureCertificate();
446 if (basicOcspResponses != null) {
447 for(BasicOCSPResp bResp : basicOcspResponses) {
448 CertificateOCSPResponse[] responses = OCSPResponse.getResponses(bResp);
449 if (responses != null && responses.length > 0 && responses[0].match(certificadoAValidar)) {
450
451 if (responses[0].getValidityPeriodBeginning().after(ts.getTime()) ||
452 responses[0].getValidityPeriodEnd().before(ts.getTime())) {
453 return ValidationResult.RESULT_TIMESTAMP_AFTER_VALIDITY_ITEM;
454 }
455
456
457 if (responses[0].getStatus() != ValidationResult.RESULT_VALID) {
458 if(certificadoAValidar.equals(certificadoFirma) || (certificadoTS != null && certificadoAValidar.equals(certificadoTS))) {
459 return responses[0].getStatus();
460 } else {
461 return ValidationResult.RESULT_CERTIFICATE_CHAIN_VALIDATION_INVALID;
462 }
463 }
464
465 if (responses[0].getValidityPeriodEnd() != null &&
466 responses[0].getValidityPeriodEnd().before(ts.getTime())) {
467
468
469 logger.debug("[PAdESLTVSignature.isValid]::La fecha de la respuesta OCSP (" + responses[0].getValidityPeriodEnd() +
470 ") es anterior a la fecha de comprobación (" + ts.getTime() + ")");
471 if(certificadoAValidar.equals(certificadoFirma) || (certificadoTS != null && certificadoAValidar.equals(certificadoTS))) {
472 return ValidationResult.RESULT_TIMESTAMP_AFTER_VALIDITY_ITEM;
473 } else {
474 return ValidationResult.RESULT_CERTIFICATE_CHAIN_VALIDATION_INVALID;
475 }
476 }
477
478 logger.debug("[PAdESLTVSignature.isValid]::La respuesta OCSP es correcta para el certificado de CN=" + certificadoFirma.getCommonName());
479 return ValidationResult.RESULT_VALID;
480 }
481 }
482 }
483
484 return ValidationResult.RESULT_CERTIFICATE_CANNOT_BE_VALIDATED;
485
486 }
487 }