@@ -22,17 +22,20 @@ This file is part of the iText (R) project.
22
22
*/
23
23
package com .itextpdf .signatures .validation .v1 ;
24
24
25
+ import com .itextpdf .commons .bouncycastle .asn1 .tsp .ITSTInfo ;
25
26
import com .itextpdf .commons .utils .DateTimeUtil ;
26
27
import com .itextpdf .commons .utils .MessageFormatUtil ;
27
28
import com .itextpdf .kernel .pdf .PdfArray ;
28
29
import com .itextpdf .kernel .pdf .PdfDictionary ;
29
30
import com .itextpdf .kernel .pdf .PdfDocument ;
30
31
import com .itextpdf .kernel .pdf .PdfName ;
32
+ import com .itextpdf .kernel .pdf .PdfReader ;
31
33
import com .itextpdf .kernel .pdf .PdfStream ;
32
34
import com .itextpdf .signatures .CertificateUtil ;
33
35
import com .itextpdf .signatures .IssuingCertificateRetriever ;
34
36
import com .itextpdf .signatures .PdfPKCS7 ;
35
37
import com .itextpdf .signatures .SignatureUtil ;
38
+ import com .itextpdf .signatures .TimestampConstants ;
36
39
import com .itextpdf .signatures .validation .v1 .context .CertificateSource ;
37
40
import com .itextpdf .signatures .validation .v1 .context .TimeBasedContext ;
38
41
import com .itextpdf .signatures .validation .v1 .context .ValidationContext ;
@@ -42,18 +45,21 @@ This file is part of the iText (R) project.
42
45
import com .itextpdf .signatures .validation .v1 .report .ValidationReport ;
43
46
44
47
import java .io .ByteArrayInputStream ;
48
+ import java .io .IOException ;
45
49
import java .security .GeneralSecurityException ;
46
50
import java .security .cert .Certificate ;
47
51
import java .security .cert .X509Certificate ;
48
52
import java .util .ArrayList ;
49
53
import java .util .Arrays ;
54
+ import java .util .Collections ;
50
55
import java .util .Date ;
51
56
import java .util .List ;
52
57
53
58
/**
54
59
* Validator class, which is expected to be used for signatures validation.
55
60
*/
56
61
class SignatureValidator {
62
+ public static final String VALIDATING_SIGNATURE_NAME = "Validating signature {0}" ;
57
63
static final String TIMESTAMP_VERIFICATION = "Timestamp verification check." ;
58
64
static final String SIGNATURE_VERIFICATION = "Signature verification check." ;
59
65
static final String CERTS_FROM_DSS = "Certificates from DSS check." ;
@@ -62,44 +68,71 @@ class SignatureValidator {
62
68
static final String CANNOT_VERIFY_SIGNATURE = "Signature {0} cannot be mathematically verified." ;
63
69
static final String DOCUMENT_IS_NOT_COVERED = "Signature {0} doesn't cover entire document." ;
64
70
static final String CANNOT_VERIFY_TIMESTAMP = "Signature timestamp attribute cannot be verified" ;
65
-
66
- private final PdfDocument document ;
71
+ static final String REVISIONS_RETRIEVAL_FAILED = "Wasn't possible to retrieve document revisions." ;
72
+ private static final String TIMESTAMP_EXTRACTION_FAILED = "Unable to extract timestamp from timestamp signature" ;
67
73
private final ValidationContext baseValidationContext ;
68
74
private final CertificateChainValidator certificateChainValidator ;
69
75
private final IssuingCertificateRetriever certificateRetriever ;
70
76
private final SignatureValidationProperties properties ;
77
+ private Date lastKnownPoE = (Date ) TimestampConstants .UNDEFINED_TIMESTAMP_DATE ;
71
78
72
79
/**
73
80
* Create new instance of {@link SignatureValidator}.
74
81
*
75
82
* @param builder See {@link ValidatorChainBuilder}
76
83
*/
77
- SignatureValidator (PdfDocument document , ValidatorChainBuilder builder ) {
78
- this .document = document ;
84
+ SignatureValidator (ValidatorChainBuilder builder ) {
79
85
this .certificateRetriever = builder .getCertificateRetriever ();
80
86
this .properties = builder .getProperties ();
81
87
this .certificateChainValidator = builder .getCertificateChainValidator ();
82
88
this .baseValidationContext = new ValidationContext (ValidatorContext .SIGNATURE_VALIDATOR ,
83
89
CertificateSource .SIGNER_CERT , TimeBasedContext .PRESENT );
84
90
}
85
91
92
+ /**
93
+ * Validate all signatures in the document
94
+ *
95
+ * @param document the document to be validated
96
+ * @return {@link ValidationReport} which contains detailed validation results
97
+ */
98
+ public ValidationReport validateSignatures (PdfDocument document ) {
99
+ ValidationReport report = new ValidationReport ();
100
+ SignatureUtil util = new SignatureUtil (document );
101
+ List <String > signatureNames = util .getSignatureNames ();
102
+ Collections .reverse (signatureNames );
103
+
104
+ for (String fieldName : signatureNames ) {
105
+ try (PdfDocument doc = new PdfDocument (new PdfReader (util .extractRevision (fieldName )))) {
106
+ ValidationReport subReport = validateLatestSignature (doc );
107
+ report .merge (subReport );
108
+ } catch (IOException e ) {
109
+ report .addReportItem (new ReportItem (SIGNATURE_VERIFICATION , REVISIONS_RETRIEVAL_FAILED ,
110
+ e , ReportItemStatus .INDETERMINATE ));
111
+ }
112
+ }
113
+ return report ;
114
+ }
115
+
116
+
86
117
/**
87
118
* Validate the latest signature in the document.
88
119
*
120
+ * @param document the document of which to validate the latest signature
89
121
* @return {@link ValidationReport} which contains detailed validation results
90
122
*/
91
- public ValidationReport validateLatestSignature () {
123
+ public ValidationReport validateLatestSignature (PdfDocument document ) {
92
124
ValidationReport validationReport = new ValidationReport ();
93
- PdfPKCS7 pkcs7 = mathematicallyVerifySignature (validationReport );
125
+ PdfPKCS7 pkcs7 = mathematicallyVerifySignature (validationReport , document );
94
126
if (stopValidation (validationReport , baseValidationContext )) {
95
127
return validationReport ;
96
128
}
97
129
98
- List <Certificate > certificatesFromDss = getCertificatesFromDss (validationReport );
130
+ List <Certificate > certificatesFromDss = getCertificatesFromDss (validationReport , document );
99
131
certificateRetriever .addKnownCertificates (certificatesFromDss );
100
132
101
133
if (pkcs7 .isTsp ()) {
102
- return validateTimestampChain (validationReport , pkcs7 .getCertificates (), pkcs7 .getSigningCertificate ());
134
+ return validateTimestampChain (validationReport , pkcs7 .getTimeStampTokenInfo (), pkcs7 .getCertificates (),
135
+ pkcs7 .getSigningCertificate ());
103
136
}
104
137
105
138
Date signingDate = DateTimeUtil .getCurrentTimeDate ();
@@ -117,7 +150,8 @@ public ValidationReport validateLatestSignature() {
117
150
return validationReport ;
118
151
}
119
152
Certificate [] timestampCertificates = pkcs7 .getTimestampCertificates ();
120
- validateTimestampChain (validationReport , timestampCertificates , (X509Certificate ) timestampCertificates [0 ]);
153
+ validateTimestampChain (validationReport , pkcs7 .getTimeStampTokenInfo (), timestampCertificates ,
154
+ (X509Certificate ) timestampCertificates [0 ]);
121
155
if (stopValidation (validationReport , baseValidationContext )) {
122
156
return validationReport ;
123
157
}
@@ -133,11 +167,14 @@ public ValidationReport validateLatestSignature() {
133
167
signingCertificate , signingDate );
134
168
}
135
169
136
- private PdfPKCS7 mathematicallyVerifySignature (ValidationReport validationReport ) {
170
+ private PdfPKCS7 mathematicallyVerifySignature (ValidationReport validationReport , PdfDocument document ) {
137
171
SignatureUtil signatureUtil = new SignatureUtil (document );
138
172
List <String > signatures = signatureUtil .getSignatureNames ();
139
173
String latestSignatureName = signatures .get (signatures .size () - 1 );
140
174
PdfPKCS7 pkcs7 = signatureUtil .readSignatureData (latestSignatureName );
175
+ validationReport .addReportItem (new ReportItem (SIGNATURE_VERIFICATION ,
176
+ MessageFormatUtil .format (VALIDATING_SIGNATURE_NAME , latestSignatureName ), ReportItemStatus .INFO ));
177
+
141
178
if (!signatureUtil .signatureCoversWholeDocument (latestSignatureName )) {
142
179
validationReport .addReportItem (new ReportItem (SIGNATURE_VERIFICATION ,
143
180
MessageFormatUtil .format (DOCUMENT_IS_NOT_COVERED , latestSignatureName ), ReportItemStatus .INVALID ));
@@ -154,17 +191,32 @@ private PdfPKCS7 mathematicallyVerifySignature(ValidationReport validationReport
154
191
return pkcs7 ;
155
192
}
156
193
157
- private ValidationReport validateTimestampChain (ValidationReport validationReport , Certificate [] knownCerts ,
158
- X509Certificate signingCert ) {
194
+ private ValidationReport validateTimestampChain (ValidationReport validationReport , ITSTInfo timeStampTokenInfo ,
195
+ Certificate [] knownCerts , X509Certificate signingCert ) {
159
196
certificateRetriever .addKnownCertificates (Arrays .asList (knownCerts ));
160
- Date signingDate = DateTimeUtil .getCurrentTimeDate ();
197
+ Date signingDate = lastKnownPoE ;
198
+ if (signingDate == TimestampConstants .UNDEFINED_TIMESTAMP_DATE ) {
199
+ signingDate = DateTimeUtil .getCurrentTimeDate ();
200
+ }
161
201
162
- return certificateChainValidator .validate (validationReport ,
202
+ ValidationReport tsValidationReport = new ValidationReport ();
203
+
204
+ certificateChainValidator .validate (tsValidationReport ,
163
205
baseValidationContext .setCertificateSource (CertificateSource .TIMESTAMP ),
164
206
signingCert , signingDate );
207
+ validationReport .merge (tsValidationReport );
208
+ if (tsValidationReport .getValidationResult () == ValidationReport .ValidationResult .VALID ) {
209
+ try {
210
+ lastKnownPoE = timeStampTokenInfo .getGenTime ();
211
+ } catch (Exception e ) {
212
+ validationReport .addReportItem (new ReportItem (TIMESTAMP_VERIFICATION , TIMESTAMP_EXTRACTION_FAILED , e ,
213
+ ReportItemStatus .INDETERMINATE ));
214
+ }
215
+ }
216
+ return validationReport ;
165
217
}
166
218
167
- private List <Certificate > getCertificatesFromDss (ValidationReport validationReport ) {
219
+ private List <Certificate > getCertificatesFromDss (ValidationReport validationReport , PdfDocument document ) {
168
220
PdfDictionary dss = document .getCatalog ().getPdfObject ().getAsDictionary (PdfName .DSS );
169
221
List <Certificate > certificatesFromDss = new ArrayList <>();
170
222
if (dss != null ) {
@@ -190,3 +242,4 @@ private boolean stopValidation(ValidationReport result, ValidationContext valida
190
242
&& result .getValidationResult () != ValidationReport .ValidationResult .VALID ;
191
243
}
192
244
}
245
+
0 commit comments