iText pdf library
Website search

Digital signatures - chapter 5

These examples were written in the context of Chapter 5 - "Validation of signed documents" of the Digital Signatures for PDF documents eBook.

C5_01_SignatureIntegrity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2020 iText Group NV
    Authors: iText Software.
 
    For more information, please contact iText Software at this address:
    sales@itextpdf.com
 */
/*
 * This class is part of the white paper entitled
 * "Digital Signatures for PDF documents"
 * written by Bruno Lowagie
 *
 * For more info, go to: http://itextpdf.com/learn
 */
package com.itextpdf.samples.signatures.chapter05;
 
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.signatures.PdfPKCS7;
import com.itextpdf.signatures.SignatureUtil;
 
import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.util.List;
 
public class C5_01_SignatureIntegrity {
    public static final String DEST = "./target/test/resources/signatures/chapter05/";
 
    public static final String EXAMPLE1 = "./src/test/resources/pdfs/hello_level_1_annotated.pdf";
    public static final String EXAMPLE2 = "./src/test/resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf";
    public static final String EXAMPLE3 = "./src/test/resources/pdfs/step_6_signed_by_dave_broken_by_chuck.pdf";
 
    public static final String EXPECTED_OUTPUT = "./src/test/resources/pdfs/hello_level_1_annotated.pdf\n" +
            "===== sig =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 1 of 2\n" +
            "Integrity check OK? true\n" +
            "./src/test/resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf\n" +
            "===== sig1 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 1 of 4\n" +
            "Integrity check OK? true\n" +
            "===== sig2 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 2 of 4\n" +
            "Integrity check OK? true\n" +
            "===== sig3 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 3 of 4\n" +
            "Integrity check OK? true\n" +
            "===== sig4 =====\n" +
            "Signature covers whole document: true\n" +
            "Document revision: 4 of 4\n" +
            "Integrity check OK? true\n" +
            "./src/test/resources/pdfs/step_6_signed_by_dave_broken_by_chuck.pdf\n" +
            "===== sig1 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 1 of 5\n" +
            "Integrity check OK? true\n" +
            "===== sig2 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 2 of 5\n" +
            "Integrity check OK? true\n" +
            "===== sig3 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 3 of 5\n" +
            "Integrity check OK? true\n" +
            "===== sig4 =====\n" +
            "Signature covers whole document: false\n" +
            "Document revision: 4 of 5\n" +
            "Integrity check OK? true\n";
 
    public void verifySignatures(String path) throws IOException, GeneralSecurityException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
        SignatureUtil signUtil = new SignatureUtil(pdfDoc);
        List<String> names = signUtil.getSignatureNames();
 
        System.out.println(path);
        for (String name : names) {
            System.out.println("===== " + name + " =====");
            verifySignature(signUtil, name);
        }
 
        pdfDoc.close();
    }
 
    public PdfPKCS7 verifySignature(SignatureUtil signUtil, String name) throws GeneralSecurityException {
        PdfPKCS7 pkcs7 = signUtil.readSignatureData(name);
 
        System.out.println("Signature covers whole document: " + signUtil.signatureCoversWholeDocument(name));
        System.out.println("Document revision: " + signUtil.getRevision(name) + " of " + signUtil.getTotalRevisions());
        System.out.println("Integrity check OK? " + pkcs7.verifySignatureIntegrityAndAuthenticity());
 
        return pkcs7;
    }
 
    public static void main(String[] args) throws IOException, GeneralSecurityException {
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
 
        C5_01_SignatureIntegrity app = new C5_01_SignatureIntegrity();
        app.verifySignatures(EXAMPLE1);
        app.verifySignatures(EXAMPLE2);
        app.verifySignatures(EXAMPLE3);
    }
}
C5_02_SignatureInfo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2020 iText Group NV
    Authors: iText Software.
 
    For more information, please contact iText Software at this address:
    sales@itextpdf.com
 */
/*
 * This class is part of the white paper entitled
 * "Digital Signatures for PDF documents"
 * written by Bruno Lowagie
 *
 * For more info, go to: http://itextpdf.com/learn
 */
package com.itextpdf.samples.signatures.chapter05;
 
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
import com.itextpdf.signatures.CertificateInfo;
import com.itextpdf.signatures.PdfPKCS7;
import com.itextpdf.signatures.SignaturePermissions;
import com.itextpdf.signatures.SignatureUtil;
 
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TimeStampToken;
 
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.List;
 
public class C5_02_SignatureInfo {
    public static final String DEST = "./target/test/resources/signatures/chapter05/";
 
    public static final String EXAMPLE1 = "./src/test/resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf";
    public static final String EXAMPLE2 = "./src/test/resources/pdfs/hello_signed4.pdf";
    public static final String EXAMPLE3 = "./src/test/resources/pdfs/field_metadata.pdf";
 
    public static final String EXPECTED_OUTPUT =
            "./src/test/resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf\n" +
                    "===== sig1 =====\n" +
                    "Field on page 1; llx: 36.0, lly: 728.02, urx: 559.0; ury: 779.02\n" +
                    "Signature covers whole document: false\n" +
                    "Document revision: 1 of 4\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: SHA256\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /adbe.pkcs7.detached\n" +
                    "Name of the signer: Alice Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: \n" +
                    "Reason: \n" +
                    "Contact info: \n" +
                    "Signature type: certification\n" +
                    "Filling out fields allowed: true\n" +
                    "Adding annotations allowed: false\n" +
                    "===== sig2 =====\n" +
                    "Field on page 1; llx: 36.0, lly: 629.04, urx: 559.0; ury: 680.04\n" +
                    "Signature covers whole document: false\n" +
                    "Document revision: 2 of 4\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: SHA256\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /adbe.pkcs7.detached\n" +
                    "Name of the signer: Bob Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: \n" +
                    "Reason: \n" +
                    "Contact info: \n" +
                    "Signature type: approval\n" +
                    "Filling out fields allowed: true\n" +
                    "Adding annotations allowed: false\n" +
                    "Lock: /Include[sig1 approved_bob sig2 ]\n" +
                    "===== sig3 =====\n" +
                    "Field on page 1; llx: 36.0, lly: 530.05, urx: 559.0; ury: 581.05\n" +
                    "Signature covers whole document: false\n" +
                    "Document revision: 3 of 4\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: SHA256\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /adbe.pkcs7.detached\n" +
                    "Name of the signer: Carol Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: \n" +
                    "Reason: \n" +
                    "Contact info: \n" +
                    "Signature type: approval\n" +
                    "Filling out fields allowed: true\n" +
                    "Adding annotations allowed: false\n" +
                    "Lock: /Include[sig1 approved_bob sig2 ]\n" +
                    "Lock: /Exclude[approved_dave sig4 ]\n" +
                    "===== sig4 =====\n" +
                    "Field on page 1; llx: 36.0, lly: 431.07, urx: 559.0; ury: 482.07\n" +
                    "Signature covers whole document: true\n" +
                    "Document revision: 4 of 4\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: SHA256\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /adbe.pkcs7.detached\n" +
                    "Name of the signer: Dave Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: \n" +
                    "Reason: \n" +
                    "Contact info: \n" +
                    "Signature type: approval\n" +
                    "Filling out fields allowed: false\n" +
                    "Adding annotations allowed: false\n" +
                    "Lock: /Include[sig1 approved_bob sig2 ]\n" +
                    "Lock: /Exclude[approved_dave sig4 ]\n" +
                    "./src/test/resources/pdfs/hello_signed4.pdf\n" +
                    "===== sig =====\n" +
                    "Field on page 1; llx: 36.0, lly: 648.0, urx: 236.0; ury: 748.0\n" +
                    "Signature covers whole document: true\n" +
                    "Document revision: 1 of 1\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: RIPEMD160\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /ETSI.CAdES.detached\n" +
                    "Name of the signer: Bruno Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: Ghent\n" +
                    "Reason: Test 4\n" +
                    "Contact info: \n" +
                    "Signature type: approval\n" +
                    "Filling out fields allowed: true\n" +
                    "Adding annotations allowed: true\n" +
                    "./src/test/resources/pdfs/field_metadata.pdf\n" +
                    "===== Signature1 =====\n" +
                    "Field on page 1; llx: 46.0674, lly: 472.172, urx: 332.563; ury: 726.831\n" +
                    "Signature covers whole document: true\n" +
                    "Document revision: 1 of 1\n" +
                    "Integrity check OK? true\n" +
                    "Digest algorithm: SHA256\n" +
                    "Encryption algorithm: RSA\n" +
                    "Filter subtype: /adbe.pkcs7.detached\n" +
                    "Name of the signer: Bruno Specimen\n" +
                    "Alternative name of the signer: Bruno L. Specimen\n" +
                    "Signed on: 2016-02-23\n" +
                    "Location: Ghent\n" +
                    "Reason: Test metadata\n" +
                    "Contact info: 555 123 456\n" +
                    "Signature type: approval\n" +
                    "Filling out fields allowed: true\n" +
                    "Adding annotations allowed: true\n";
 
    public SignaturePermissions inspectSignature(PdfDocument pdfDoc, SignatureUtil signUtil, PdfAcroForm form,
            String name, SignaturePermissions perms) throws GeneralSecurityException {
        List<PdfWidgetAnnotation> widgets = form.getField(name).getWidgets();
 
        // Check the visibility of the signature annotation
        if (widgets != null && widgets.size() > 0) {
            Rectangle pos = widgets.get(0).getRectangle().toRectangle();
            int pageNum = pdfDoc.getPageNumber(widgets.get(0).getPage());
            if (pos.getWidth() == 0 || pos.getHeight() == 0) {
                System.out.println("Invisible signature");
            } else {
                System.out.println(String.format("Field on page %s; llx: %s, lly: %s, urx: %s; ury: %s",
                        pageNum, pos.getLeft(), pos.getBottom(), pos.getRight(), pos.getTop()));
            }
        }
 
        /* Find out how the message digest of the PDF bytes was created,
         * how these bytes and additional attributes were signed
         * and how the signed bytes are stored in the PDF
         */
        PdfPKCS7 pkcs7 = verifySignature(signUtil, name);
        System.out.println("Digest algorithm: " + pkcs7.getHashAlgorithm());
        System.out.println("Encryption algorithm: " + pkcs7.getEncryptionAlgorithm());
        System.out.println("Filter subtype: " + pkcs7.getFilterSubtype());
 
        // Get the signing certificate to find out the name of the signer.
        X509Certificate cert = (X509Certificate) pkcs7.getSigningCertificate();
        System.out.println("Name of the signer: " + CertificateInfo.getSubjectFields(cert).getField("CN"));
        if (pkcs7.getSignName() != null) {
            System.out.println("Alternative name of the signer: " + pkcs7.getSignName());
        }
 
        // Get the signing time
        SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd");
 
        /* Mind that the getSignDate() method is not that secure as timestamp
         * because it's based only on signature author claim. I.e. this value can only be trusted
         * if signature is trusted and it cannot be used for signature verification.
         */
        System.out.println("Signed on: " + date_format.format(pkcs7.getSignDate().getTime()));
 
        /* If a timestamp was applied, retrieve information about it.
         * Timestamp is a secure source of signature creation time,
         * because it's based on Time Stamping Authority service.
         */
        if (pkcs7.getTimeStampDate() != null) {
            System.out.println("TimeStamp: " + date_format.format(pkcs7.getTimeStampDate().getTime()));
            TimeStampToken ts = pkcs7.getTimeStampToken();
            System.out.println("TimeStamp service: " + ts.getTimeStampInfo().getTsa());
            System.out.println("Timestamp verified? " + pkcs7.verifyTimestampImprint());
        }
 
        System.out.println("Location: " + pkcs7.getLocation());
        System.out.println("Reason: " + pkcs7.getReason());
 
        /* If you want less common entries than PdfPKCS7 object has, such as the contact info,
         * you should use the signature dictionary and get the properties by name.
         */
        PdfDictionary sigDict = signUtil.getSignatureDictionary(name);
        PdfString contact = sigDict.getAsString(PdfName.ContactInfo);
        if (contact != null) {
            System.out.println("Contact info: " + contact);
        }
 
        /* Every new signature can add more restrictions to a document, but it can't take away previous restrictions.
         * So if you want to retrieve information about signatures restrictions, you need to pass
         * the SignaturePermissions instance of the previous signature, or null if there was none.
         */
        perms = new SignaturePermissions(sigDict, perms);
        System.out.println("Signature type: " + (perms.isCertification() ? "certification" : "approval"));
        System.out.println("Filling out fields allowed: " + perms.isFillInAllowed());
        System.out.println("Adding annotations allowed: " + perms.isAnnotationsAllowed());
        for (SignaturePermissions.FieldLock lock : perms.getFieldLocks()) {
            System.out.println("Lock: " + lock.toString());
        }
 
        return perms;
    }
 
    public PdfPKCS7 verifySignature(SignatureUtil signUtil, String name) throws GeneralSecurityException {
        PdfPKCS7 pkcs7 = signUtil.readSignatureData(name);
 
        System.out.println("Signature covers whole document: " + signUtil.signatureCoversWholeDocument(name));
        System.out.println("Document revision: " + signUtil.getRevision(name) + " of " + signUtil.getTotalRevisions());
        System.out.println("Integrity check OK? " + pkcs7.verifySignatureIntegrityAndAuthenticity());
 
        return pkcs7;
    }
 
    public void inspectSignatures(String path) throws IOException, GeneralSecurityException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
        PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false);
        SignaturePermissions perms = null;
        SignatureUtil signUtil = new SignatureUtil(pdfDoc);
        List<String> names = signUtil.getSignatureNames();
 
        System.out.println(path);
        for (String name : names) {
            System.out.println("===== " + name + " =====");
            perms = inspectSignature(pdfDoc, signUtil, form, name, perms);
        }
    }
 
    public static void main(String[] args) throws IOException, GeneralSecurityException {
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
 
        C5_02_SignatureInfo app = new C5_02_SignatureInfo();
        app.inspectSignatures(EXAMPLE1);
        app.inspectSignatures(EXAMPLE2);
        app.inspectSignatures(EXAMPLE3);
    }
}
C5_03_CertificateValidation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2020 iText Group NV
    Authors: iText Software.
 
    For more information, please contact iText Software at this address:
    sales@itextpdf.com
 */
/*
 * This class is part of the white paper entitled
 * "Digital Signatures for PDF documents"
 * written by Bruno Lowagie
 *
 * For more info, go to: http://itextpdf.com/learn
 */
package com.itextpdf.samples.signatures.chapter05;
 
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.signatures.CRLVerifier;
import com.itextpdf.signatures.CertificateVerification;
import com.itextpdf.signatures.OCSPVerifier;
import com.itextpdf.signatures.PdfPKCS7;
import com.itextpdf.signatures.SignatureUtil;
import com.itextpdf.signatures.VerificationException;
import com.itextpdf.signatures.VerificationOK;
 
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.FileSystem;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
 
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.LoggerFactory;
 
public class C5_03_CertificateValidation {
    public static final String DEST = "./target/test/resources/signatures/chapter05/";
 
    public static final String ROOT = "./src/test/resources/encryption/rootRsa.cer";
 
    public static final String EXAMPLE = "./src/test/resources/pdfs/signedPAdES-LT.pdf";
 
    public static final String EXPECTED_OUTPUT = "./src/test/resources/pdfs/signedPAdES-LT.pdf\n"
            + "===== Signature1 =====\n"
            + "Signature covers whole document: false\n"
            + "Document revision: 1 of 2\n"
            + "Integrity check OK? true\n"
            + "Certificates verified against the KeyStore\n"
            + "=== Certificate 0 ===\n"
            + "Issuer: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
            + "Subject: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRsaCert01\n"
            + "Valid from: 2017-04-07-15-33\n"
            + "Valid to: 2117-04-07-15-33\n"
            + "The certificate was valid at the time of signing.\n"
            + "The certificate is still valid.\n"
            + "=== Certificate 1 ===\n"
            + "Issuer: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
            + "Subject: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
            + "Valid from: 2017-04-07-13-20\n"
            + "Valid to: 2117-04-07-13-20\n"
            + "The certificate was valid at the time of signing.\n"
            + "The certificate is still valid.\n"
            + "=== Checking validity of the document at the time of signing ===\n"
            + "com.itextpdf.signatures.OCSPVerifier INFO Valid OCSPs found: 0\n"
            + "com.itextpdf.signatures.CRLVerifier INFO Valid CRLs found: 0\n"
            + "The signing certificate couldn't be verified\n"
            + "=== Checking validity of the document today ===\n"
            + "com.itextpdf.signatures.OCSPVerifier INFO Valid OCSPs found: 0\n"
            + "com.itextpdf.signatures.CRLVerifier INFO Valid CRLs found: 0\n"
            + "The signing certificate couldn't be verified"
            +"\n";
 
    private static PrintStream OUT_STREAM = System.out;
    private static AppenderBase<ILoggingEvent> appender;
    private KeyStore ks;
 
    public void verifySignatures(String path) throws IOException, GeneralSecurityException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
        SignatureUtil signUtil = new SignatureUtil(pdfDoc);
        List<String> names = signUtil.getSignatureNames();
 
        OUT_STREAM.println(path);
        for (String name : names) {
            OUT_STREAM.println("===== " + name + " =====");
            verifySignature(signUtil, name);
        }
    }
 
    public PdfPKCS7 verifySignature(SignatureUtil signUtil, String name) throws GeneralSecurityException,
            IOException {
        PdfPKCS7 pkcs7 = getSignatureData(signUtil, name);
        Certificate[] certs = pkcs7.getSignCertificateChain();
 
        // Timestamp is a secure source of signature creation time,
        // because it's based on Time Stamping Authority service.
        Calendar cal = pkcs7.getTimeStampDate();
 
        // If there is no timestamp, use the current date
        if (cal == null) {
            cal = Calendar.getInstance();
        }
 
        // Check if the certificate chain, presented in the PDF, can be verified against
        // the created key store.
        List<VerificationException> errors = CertificateVerification.verifyCertificates(certs, ks, cal);
        if (errors.size() == 0) {
            OUT_STREAM.println("Certificates verified against the KeyStore");
        } else {
            OUT_STREAM.println(errors);
        }
 
        // Find out if certificates were valid on the signing date, and if they are still valid today
        for (int i = 0; i < certs.length; i++) {
            X509Certificate cert = (X509Certificate) certs[i];
            OUT_STREAM.println("=== Certificate " + i + " ===");
            showCertificateInfo(cert, cal.getTime());
        }
 
        // Take the signing certificate
        X509Certificate signCert = (X509Certificate) certs[0];
 
        // Take the certificate of the issuer of that certificate (or null if it was self-signed).
        X509Certificate issuerCert = (certs.length > 1 ? (X509Certificate) certs[1] : null);
 
        OUT_STREAM.println("=== Checking validity of the document at the time of signing ===");
        checkRevocation(pkcs7, signCert, issuerCert, cal.getTime());
 
        OUT_STREAM.println("=== Checking validity of the document today ===");
        checkRevocation(pkcs7, signCert, issuerCert, new Date());
 
        return pkcs7;
    }
 
    public PdfPKCS7 getSignatureData(SignatureUtil signUtil, String name) throws GeneralSecurityException {
        PdfPKCS7 pkcs7 = signUtil.readSignatureData(name);
 
        OUT_STREAM.println("Signature covers whole document: " + signUtil.signatureCoversWholeDocument(name));
        OUT_STREAM.println("Document revision: " + signUtil.getRevision(name) + " of " + signUtil.getTotalRevisions());
        OUT_STREAM.println("Integrity check OK? " + pkcs7.verifySignatureIntegrityAndAuthenticity());
 
        return pkcs7;
    }
 
    public void showCertificateInfo(X509Certificate cert, Date signDate) {
        OUT_STREAM.println("Issuer: " + cert.getIssuerDN());
        OUT_STREAM.println("Subject: " + cert.getSubjectDN());
        SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
        date_format.setTimeZone(TimeZone.getTimeZone("Universal"));
        OUT_STREAM.println("Valid from: " + date_format.format(cert.getNotBefore()));
        OUT_STREAM.println("Valid to: " + date_format.format(cert.getNotAfter()));
 
        // Check if a certificate was valid on the signing date
        try {
            cert.checkValidity(signDate);
            OUT_STREAM.println("The certificate was valid at the time of signing.");
        } catch (CertificateExpiredException e) {
            OUT_STREAM.println("The certificate was expired at the time of signing.");
        } catch (CertificateNotYetValidException e) {
            OUT_STREAM.println("The certificate wasn't valid yet at the time of signing.");
        }
 
        // Check if a certificate is still valid now
        try {
            cert.checkValidity();
            OUT_STREAM.println("The certificate is still valid.");
        } catch (CertificateExpiredException e) {
            OUT_STREAM.println("The certificate has expired.");
        } catch (CertificateNotYetValidException e) {
            OUT_STREAM.println("The certificate isn't valid yet.");
        }
    }
 
    public static void checkRevocation(PdfPKCS7 pkcs7, X509Certificate signCert, X509Certificate issuerCert, Date date)
            throws GeneralSecurityException, IOException {
        List<BasicOCSPResp> ocsps = new ArrayList<BasicOCSPResp>();
        if (pkcs7.getOcsp() != null) {
            ocsps.add(pkcs7.getOcsp());
        }
 
        // Check if the OCSP responses in the list were valid for the certificate on a specific date.
        OCSPVerifier ocspVerifier = new OCSPVerifier(null, ocsps);
        List<VerificationOK> verification = ocspVerifier.verify(signCert, issuerCert, date);
 
        // If that list is empty, we can't verify using OCSP, and we need to look for CRLs.
        if (verification.size() == 0) {
            List<X509CRL> crls = new ArrayList<X509CRL>();
            if (pkcs7.getCRLs() != null) {
                for (CRL crl : pkcs7.getCRLs()) {
                    crls.add((X509CRL) crl);
                }
            }
 
            // Check if the CRLs in the list were valid on a specific date.
            CRLVerifier crlVerifier = new CRLVerifier(null, crls);
            verification.addAll(crlVerifier.verify(signCert, issuerCert, date));
        }
 
        if (verification.size() == 0) {
            OUT_STREAM.println("The signing certificate couldn't be verified");
        } else {
            for (VerificationOK v : verification) {
                OUT_STREAM.println(v);
            }
        }
    }
 
    public static void main(String[] args) throws IOException, GeneralSecurityException {
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        C5_03_CertificateValidation app = new C5_03_CertificateValidation();
 
        Logger ocspLogger = (Logger) LoggerFactory.getLogger(OCSPVerifier.class);
        Logger clrLogger = (Logger) LoggerFactory.getLogger(CRLVerifier.class);
 
        /* Add a custom appender to the specified logger.
         * Mind that if you have any added console appenders, then log messages in the console
         * could be shown multiple times.
         */
        setUpLogger(ocspLogger);
        setUpLogger(clrLogger);
 
        // Create your own root certificate store and add certificates
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, null);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        try (FileInputStream stream = new FileInputStream(ROOT)) {
            ks.setCertificateEntry("root", cf.generateCertificate(stream));
        }
 
        app.setKeyStore(ks);
 
        app.verifySignatures(EXAMPLE);
 
        // Detach the custom appender from the logger.
        resetLogger(ocspLogger);
        resetLogger(clrLogger);
    }
 
    private void setKeyStore(KeyStore ks) {
        this.ks = ks;
    }
 
    private static void setUpLogger(Logger logger) {
        appender = new CustomListAppender<ILoggingEvent>(OUT_STREAM);
        appender.setName("customAppender");
        appender.start();
        logger.addAppender(appender);
    }
 
    private static void resetLogger(Logger logger) {
        appender.stop();
        logger.detachAppender(appender);
 
    }
 
    // Custom log appender to write log messages to the specific print stream
    private static class CustomListAppender<E> extends AppenderBase<E> {
        private PrintStream stream;
 
        public CustomListAppender(PrintStream stream) {
            this.stream = stream;
        }
 
        @Override
        protected void append(E e) {
            ILoggingEvent event = (ILoggingEvent) e;
            stream.println(event.getLoggerName() + " " + event.getLevel() + " " + event.getMessage());
        }
    }
}
C5_01_SignatureIntegrity.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
 
This file is part of the iText (R) project.
Copyright (c) 1998-2020 iText Group NV
 
*/
/*
* This class is part of the white paper entitled
* "Digital Signatures for PDF documents"
* written by Bruno Lowagie
*
* For more info, go to: http://itextpdf.com/learn
*/
 
using System;
using System.Collections.Generic;
using iText.Kernel.Pdf;
using iText.Signatures;
 
namespace iText.Samples.Signatures.Chapter05
{
    public class C5_01_SignatureIntegrity
    {
        public static readonly string DEST = "signatures/chapter05/";
        
        public static readonly string EXAMPLE1 = "../../resources/pdfs/hello_level_1_annotated.pdf";
 
        public static readonly string EXAMPLE2 = "../../resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf";
 
        public static readonly string EXAMPLE3 = "../../resources/pdfs/step_6_signed_by_dave_broken_by_chuck.pdf";
 
        public const String EXPECTED_OUTPUT = "../../resources/pdfs/hello_level_1_annotated.pdf\n"
                                             + "===== sig =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 1 of 2\n"
                                             + "Integrity check OK? True\n"
                                             + "../../resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf\n"
                                             + "===== sig1 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 1 of 4\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig2 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 2 of 4\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig3 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 3 of 4\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig4 =====\n"
                                             + "Signature covers whole document: True\n"
                                             + "Document revision: 4 of 4\n"
                                             + "Integrity check OK? True\n"
                                             + "../../resources/pdfs/step_6_signed_by_dave_broken_by_chuck.pdf\n"
                                             + "===== sig1 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 1 of 5\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig2 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 2 of 5\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig3 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 3 of 5\n"
                                             + "Integrity check OK? True\n"
                                             + "===== sig4 =====\n"
                                             + "Signature covers whole document: False\n"
                                             + "Document revision: 4 of 5\n"
                                             + "Integrity check OK? True\n";
 
        public void VerifySignatures(String path)
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
            SignatureUtil signUtil = new SignatureUtil(pdfDoc);
            IList<String> names = signUtil.GetSignatureNames();
 
            Console.WriteLine(path);
            foreach (String name in names)
            {
                Console.Out.WriteLine("===== " + name + " =====");
                VerifySignature(signUtil, name);
            }
            
            pdfDoc.Close();
        }
 
        public PdfPKCS7 VerifySignature(SignatureUtil signUtil, String name)
        {
            PdfPKCS7 pkcs7 = signUtil.ReadSignatureData(name);
 
            Console.Out.WriteLine("Signature covers whole document: " + signUtil.SignatureCoversWholeDocument(name));
            Console.Out.WriteLine("Document revision: " + signUtil.GetRevision(name) + " of "
                                  + signUtil.GetTotalRevisions());
            Console.Out.WriteLine("Integrity check OK? " + pkcs7.VerifySignatureIntegrityAndAuthenticity());
            return pkcs7;
        }
 
        public static void Main(String[] args)
        {
            C5_01_SignatureIntegrity app = new C5_01_SignatureIntegrity();
            app.VerifySignatures(EXAMPLE1);
            app.VerifySignatures(EXAMPLE2);
            app.VerifySignatures(EXAMPLE3);
        }
    }
}
C5_02_SignatureInfo.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 
This file is part of the iText (R) project.
Copyright (c) 1998-2020 iText Group NV
 
*/
/*
* This class is part of the white paper entitled
* "Digital Signatures for PDF documents"
* written by Bruno Lowagie
*
* For more info, go to: http://itextpdf.com/learn
*/
 
using System;
using System.Collections.Generic;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.X509;
using iText.Forms;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Signatures;
 
namespace iText.Samples.Signatures.Chapter05
{
    public class C5_02_SignatureInfo
    {
        public static readonly string DEST = "signatures/chapter05/";
 
        public static readonly string EXAMPLE1 = "../../resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf";
        public static readonly string EXAMPLE2 = "../../resources/pdfs/hello_signed4.pdf";
        public static readonly string EXAMPLE3 = "../../resources/pdfs/field_metadata.pdf";
 
        public const String EXPECTED_OUTPUT = "../../resources/pdfs/step_4_signed_by_alice_bob_carol_and_dave.pdf\n"
                                              + "===== sig1 =====\n"
                                              + "Field on page 1; llx: 36, lly: 728.02, urx: 559; ury: 779.02\n"
                                              + "Signature covers whole document: False\n"
                                              + "Document revision: 1 of 4\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: SHA256\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /adbe.pkcs7.detached\n"
                                              + "Name of the signer: Alice Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: \n"
                                              + "Reason: \n"
                                              + "Contact info: \n"
                                              + "Signature type: certification\n"
                                              + "Filling out fields allowed: True\n"
                                              + "Adding annotations allowed: False\n"
                                              + "===== sig2 =====\n"
                                              + "Field on page 1; llx: 36, lly: 629.04, urx: 559; ury: 680.04\n"
                                              + "Signature covers whole document: False\n"
                                              + "Document revision: 2 of 4\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: SHA256\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /adbe.pkcs7.detached\n"
                                              + "Name of the signer: Bob Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: \n"
                                              + "Reason: \n"
                                              + "Contact info: \n"
                                              + "Signature type: approval\n"
                                              + "Filling out fields allowed: True\n"
                                              + "Adding annotations allowed: False\n"
                                              + "Lock: /Include[sig1 approved_bob sig2 ]\n"
                                              + "===== sig3 =====\n"
                                              + "Field on page 1; llx: 36, lly: 530.05, urx: 559; ury: 581.05\n"
                                              + "Signature covers whole document: False\n"
                                              + "Document revision: 3 of 4\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: SHA256\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /adbe.pkcs7.detached\n"
                                              + "Name of the signer: Carol Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: \n"
                                              + "Reason: \n"
                                              + "Contact info: \n"
                                              + "Signature type: approval\n"
                                              + "Filling out fields allowed: True\n"
                                              + "Adding annotations allowed: False\n"
                                              + "Lock: /Include[sig1 approved_bob sig2 ]\n"
                                              + "Lock: /Exclude[approved_dave sig4 ]\n"
                                              + "===== sig4 =====\n"
                                              + "Field on page 1; llx: 36, lly: 431.07, urx: 559; ury: 482.07\n"
                                              + "Signature covers whole document: True\n"
                                              + "Document revision: 4 of 4\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: SHA256\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /adbe.pkcs7.detached\n"
                                              + "Name of the signer: Dave Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: \n"
                                              + "Reason: \n"
                                              + "Contact info: \n"
                                              + "Signature type: approval\n"
                                              + "Filling out fields allowed: False\n"
                                              + "Adding annotations allowed: False\n"
                                              + "Lock: /Include[sig1 approved_bob sig2 ]\n"
                                              + "Lock: /Exclude[approved_dave sig4 ]\n"
                                              + "../../resources/pdfs/hello_signed4.pdf\n"
                                              + "===== sig =====\n"
                                              + "Field on page 1; llx: 36, lly: 648, urx: 236; ury: 748\n"
                                              + "Signature covers whole document: True\n"
                                              + "Document revision: 1 of 1\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: RIPEMD160\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /ETSI.CAdES.detached\n"
                                              + "Name of the signer: Bruno Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: Ghent\n"
                                              + "Reason: Test 4\n"
                                              + "Contact info: \n"
                                              + "Signature type: approval\n"
                                              + "Filling out fields allowed: True\n"
                                              + "Adding annotations allowed: True\n"
                                              + "../../resources/pdfs/field_metadata.pdf\n"
                                              + "===== Signature1 =====\n"
                                              + "Field on page 1; llx: 46.0674, lly: 472.172, urx: 332.563; ury: 726.831\n"
                                              + "Signature covers whole document: True\n"
                                              + "Document revision: 1 of 1\n"
                                              + "Integrity check OK? True\n"
                                              + "Digest algorithm: SHA256\n"
                                              + "Encryption algorithm: RSA\n"
                                              + "Filter subtype: /adbe.pkcs7.detached\n"
                                              + "Name of the signer: Bruno Specimen\n"
                                              + "Alternative name of the signer: Bruno L. Specimen\n"
                                              + "Signed on: 2016-02-23\n"
                                              + "Location: Ghent\n"
                                              + "Reason: Test metadata\n"
                                              + "Contact info: 555 123 456\n"
                                              + "Signature type: approval\n"
                                              + "Filling out fields allowed: True\n"
                                              + "Adding annotations allowed: True\n";
 
        public SignaturePermissions InspectSignature(PdfDocument pdfDoc, SignatureUtil signUtil, PdfAcroForm form,
            String name, SignaturePermissions perms)
        {
            IList<PdfWidgetAnnotation> widgets = form.GetField(name).GetWidgets();
 
            // Check the visibility of the signature annotation
            if (widgets != null && widgets.Count > 0)
            {
                Rectangle pos = widgets[0].GetRectangle().ToRectangle();
                int pageNum = pdfDoc.GetPageNumber(widgets[0].GetPage());
 
                if (pos.GetWidth() == 0 || pos.GetHeight() == 0)
                {
                    Console.Out.WriteLine("Invisible signature");
                }
                else
                {
                    Console.Out.WriteLine(String.Format("Field on page {0}; llx: {1}, lly: {2}, urx: {3}; ury: {4}",
                        pageNum, pos.GetLeft(), pos.GetBottom(), pos.GetRight(), pos.GetTop()));
                }
            }
 
            /* Find out how the message digest of the PDF bytes was created,
             * how these bytes and additional attributes were signed
             * and how the signed bytes are stored in the PDF
             */
            PdfPKCS7 pkcs7 = VerifySignature(signUtil, name);
            Console.Out.WriteLine("Digest algorithm: " + pkcs7.GetHashAlgorithm());
            Console.Out.WriteLine("Encryption algorithm: " + pkcs7.GetEncryptionAlgorithm());
            Console.Out.WriteLine("Filter subtype: " + pkcs7.GetFilterSubtype());
 
            // Get the signing certificate to find out the name of the signer.
            X509Certificate cert = (X509Certificate) pkcs7.GetSigningCertificate();
            Console.Out.WriteLine("Name of the signer: "
                                  + iText.Signatures.CertificateInfo.GetSubjectFields(cert).GetField("CN"));
            if (pkcs7.GetSignName() != null)
            {
                Console.Out.WriteLine("Alternative name of the signer: " + pkcs7.GetSignName());
            }
 
            /* Get the signing time.
             * Mind that the getSignDate() method is not that secure as timestamp
             * because it's based only on signature author claim. I.e. this value can only be trusted
             * if signature is trusted and it cannot be used for signature verification.
             */
            Console.Out.WriteLine("Signed on: " + pkcs7.GetSignDate().ToUniversalTime().ToString("yyyy-MM-dd"));
 
            /* If a timestamp was applied, retrieve information about it.
             * Timestamp is a secure source of signature creation time,
             * because it's based on Time Stamping Authority service.
             */
            if (pkcs7.GetTimeStampDate() != DateTime.MaxValue)
            {
                Console.Out.WriteLine("TimeStamp: " +
                                      pkcs7.GetTimeStampDate().ToUniversalTime().ToString("yyyy-MM-dd"));
                TimeStampToken ts = pkcs7.GetTimeStampToken();
                Console.Out.WriteLine("TimeStamp service: " + ts.TimeStampInfo.Tsa);
                Console.Out.WriteLine("Timestamp verified? " + pkcs7.VerifyTimestampImprint());
            }
 
            Console.Out.WriteLine("Location: " + pkcs7.GetLocation());
            Console.Out.WriteLine("Reason: " + pkcs7.GetReason());
 
            /* If you want less common entries than PdfPKCS7 object has, such as the contact info,
             * you should use the signature dictionary and get the properties by name.
             */
            PdfDictionary sigDict = signUtil.GetSignatureDictionary(name);
            PdfString contact = sigDict.GetAsString(PdfName.ContactInfo);
            if (contact != null)
            {
                Console.Out.WriteLine("Contact info: " + contact);
            }
 
            /* Every new signature can add more restrictions to a document, but it can’t take away previous restrictions.
             * So if you want to retrieve information about signatures restrictions, you need to pass
             * the SignaturePermissions instance of the previous signature, or null if there was none.
             */
            perms = new SignaturePermissions(sigDict, perms);
            Console.Out.WriteLine("Signature type: " + (perms.IsCertification() ? "certification" : "approval"));
            Console.Out.WriteLine("Filling out fields allowed: " + perms.IsFillInAllowed());
            Console.Out.WriteLine("Adding annotations allowed: " + perms.IsAnnotationsAllowed());
            foreach (SignaturePermissions.FieldLock Lock in perms.GetFieldLocks())
            {
                Console.Out.WriteLine("Lock: " + Lock);
            }
 
            return perms;
        }
 
        public PdfPKCS7 VerifySignature(SignatureUtil signUtil, String name)
        {
            PdfPKCS7 pkcs7 = signUtil.ReadSignatureData(name);
 
            Console.Out.WriteLine("Signature covers whole document: " + signUtil.SignatureCoversWholeDocument(name));
            Console.Out.WriteLine("Document revision: " + signUtil.GetRevision(name) + " of "
                                  + signUtil.GetTotalRevisions());
            Console.Out.WriteLine("Integrity check OK? " + pkcs7.VerifySignatureIntegrityAndAuthenticity());
            return pkcs7;
        }
 
        public virtual void InspectSignatures(String path)
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
            PdfAcroForm form = PdfAcroForm.GetAcroForm(pdfDoc, false);
            SignaturePermissions perms = null;
            SignatureUtil signUtil = new SignatureUtil(pdfDoc);
            IList<String> names = signUtil.GetSignatureNames();
 
            Console.WriteLine(path);
            foreach (String name in names)
            {
                Console.Out.WriteLine("===== " + name + " =====");
                perms = InspectSignature(pdfDoc, signUtil, form, name, perms);
            }
        }
 
        public static void Main(String[] args)
        {
            C5_02_SignatureInfo app = new C5_02_SignatureInfo();
            app.InspectSignatures(EXAMPLE1);
            app.InspectSignatures(EXAMPLE2);
            app.InspectSignatures(EXAMPLE3);
        }
    }
}
C5_03_CertificateValidation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
 
This file is part of the iText (R) project.
Copyright (c) 1998-2020 iText Group NV
 
*/
/*
* This class is part of the white paper entitled
* "Digital Signatures for PDF documents"
* written by Bruno Lowagie
*
* For more info, go to: http://itextpdf.com/learn
*/
 
using System;
using System.Collections.Generic;
using System.IO;
using Common.Logging;
using Common.Logging.Simple;
using iText.Kernel.Pdf;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.X509;
using iText.Signatures;
using Org.BouncyCastle.Security.Certificates;
 
namespace iText.Samples.Signatures.Chapter05
{
    public class C5_03_CertificateValidation
    {
        public static readonly string DEST = "signatures/chapter05/";
 
        public static readonly string ROOT = "../../resources/encryption/rootRsa.cer";
 
        public static readonly string EXAMPLE = "../../resources/pdfs/signedPAdES-LT.pdf";
 
        public const String EXPECTED_OUTPUT = "../../resources/pdfs/signedPAdES-LT.pdf\n"
                                              + "===== Signature1 =====\n"
                                              + "Signature covers whole document: False\n"
                                              + "Document revision: 1 of 2\n"
                                              + "Integrity check OK? True\n"
                                              + "Certificates verified against the KeyStore\n"
                                              + "=== Certificate 0 ===\n"
                                              + "Issuer: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
                                              + "Subject: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRsaCert01\n"
                                              + "Valid from: 2017-04-07\n"
                                              + "Valid to: 2117-04-07\n"
                                              + "The certificate was valid at the time of signing.\n"
                                              + "The certificate is still valid.\n"
                                              + "=== Certificate 1 ===\n"
                                              + "Issuer: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
                                              + "Subject: C=BY,L=Minsk,O=iText,OU=test,CN=iTextTestRoot\n"
                                              + "Valid from: 2017-04-07\n"
                                              + "Valid to: 2117-04-07\n"
                                              + "The certificate was valid at the time of signing.\n"
                                              + "The certificate is still valid.\n"
                                              + "=== Checking validity of the document at the time of signing ===\n"
                                              + "iText.Signatures.OCSPVerifier: Valid OCSPs found: 0\n"
                                              + "iText.Signatures.CRLVerifier: Valid CRLs found: 0\n"
                                              + "The signing certificate couldn't be verified\n"
                                              + "=== Checking validity of the document today ===\n"
                                              + "iText.Signatures.OCSPVerifier: Valid OCSPs found: 0\n"
                                              + "iText.Signatures.CRLVerifier: Valid CRLs found: 0\n"
                                              + "The signing certificate couldn't be verified"
                                              +"\n";
 
        public static TextWriter OUT_STREAM = Console.Out;
        private static ILoggerFactoryAdapter defaultLogAdapter;
        private List<X509Certificate> ks;
 
        public void VerifySignatures(String path)
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(path));
            SignatureUtil signUtil = new SignatureUtil(pdfDoc);
            IList<String> names = signUtil.GetSignatureNames();
 
            OUT_STREAM.WriteLine(path);
            foreach (String name in names)
            {
                OUT_STREAM.WriteLine("===== " + name + " =====");
                VerifySignature(signUtil, name);
            }
        }
 
        public PdfPKCS7 VerifySignature(SignatureUtil signUtil, String name)
        {
            PdfPKCS7 pkcs7 = GetSignatureData(signUtil, name);
            X509Certificate[] certs = pkcs7.GetSignCertificateChain();
            
            // Timestamp is a secure source of signature creation time,
            // because it's based on Time Stamping Authority service.
            DateTime cal = pkcs7.GetTimeStampDate();
            
            // The value, which is returned if there is no timestamp
            DateTime noTimestampValue = DateTime.MaxValue;
            
            // If there is no timestamp, use the current date
            if (cal.Equals(noTimestampValue)) {
                cal = new DateTime();
            }
 
            // Check if the certificate chain, presented in the PDF, can be verified against
            // the created key store.
            IList<VerificationException> errors = CertificateVerification.VerifyCertificates(certs, ks, cal);
            if (errors.Count == 0)
            {
                OUT_STREAM.WriteLine("Certificates verified against the KeyStore");
            }
            else
            {
                OUT_STREAM.WriteLine(errors);
            }
 
            // Find out if certificates were valid on the signing date, and if they are still valid today
            for (int i = 0; i < certs.Length; i++)
            {
                X509Certificate cert = (X509Certificate) certs[i];
                OUT_STREAM.WriteLine("=== Certificate " + i + " ===");
                ShowCertificateInfo(cert, cal.ToUniversalTime());
            }
 
            // Take the signing certificate
            X509Certificate signCert = (X509Certificate) certs[0];
 
            // Take the certificate of the issuer of that certificate (or null if it was self-signed).
            X509Certificate issuerCert = (certs.Length > 1 ? (X509Certificate) certs[1] : null);
 
            OUT_STREAM.WriteLine("=== Checking validity of the document at the time of signing ===");
            CheckRevocation(pkcs7, signCert, issuerCert, cal.ToUniversalTime());
 
            OUT_STREAM.WriteLine("=== Checking validity of the document today ===");
            CheckRevocation(pkcs7, signCert, issuerCert, new DateTime());
 
            return pkcs7;
        }
 
        public PdfPKCS7 GetSignatureData(SignatureUtil signUtil, String name)
        {
            PdfPKCS7 pkcs7 = signUtil.ReadSignatureData(name);
 
            OUT_STREAM.WriteLine("Signature covers whole document: " + signUtil.SignatureCoversWholeDocument(name));
            OUT_STREAM.WriteLine("Document revision: " + signUtil.GetRevision(name) + " of "
                                 + signUtil.GetTotalRevisions());
            OUT_STREAM.WriteLine("Integrity check OK? " + pkcs7.VerifySignatureIntegrityAndAuthenticity());
            return pkcs7;
        }
 
        public void ShowCertificateInfo(X509Certificate cert, DateTime signDate)
        {
            OUT_STREAM.WriteLine("Issuer: " + cert.IssuerDN);
            OUT_STREAM.WriteLine("Subject: " + cert.SubjectDN);
            OUT_STREAM.WriteLine("Valid from: " + (cert.NotBefore.ToUniversalTime().ToString("yyyy-MM-dd")));
            OUT_STREAM.WriteLine("Valid to: " + cert.NotAfter.ToUniversalTime().ToString("yyyy-MM-dd"));
 
            // Check if a certificate was valid on the signing date
            try
            {
                cert.CheckValidity(signDate);
                OUT_STREAM.WriteLine("The certificate was valid at the time of signing.");
            }
            catch (CertificateExpiredException)
            {
                OUT_STREAM.WriteLine("The certificate was expired at the time of signing.");
            }
            catch (CertificateNotYetValidException)
            {
                OUT_STREAM.WriteLine("The certificate wasn't valid yet at the time of signing.");
            }
 
            // Check if a certificate is still valid now
            try
            {
                cert.CheckValidity();
                OUT_STREAM.WriteLine("The certificate is still valid.");
            }
            catch (CertificateExpiredException)
            {
                OUT_STREAM.WriteLine("The certificate has expired.");
            }
            catch (CertificateNotYetValidException)
            {
                OUT_STREAM.WriteLine("The certificate isn't valid yet.");
            }
        }
 
        private static void CheckRevocation(PdfPKCS7 pkcs7, X509Certificate signCert, X509Certificate issuerCert,
            DateTime date)
        {
            IList<BasicOcspResp> ocsps = new List<BasicOcspResp>();
            if (pkcs7.GetOcsp() != null)
            {
                ocsps.Add(pkcs7.GetOcsp());
            }
 
            // Check if the OCSP responses in the list were valid for the certificate on a specific date.
            OCSPVerifier ocspVerifier = new OCSPVerifier(null, ocsps);
            IList<VerificationOK> verification = ocspVerifier.Verify(signCert, issuerCert, date);
 
            // If that list is empty, we can’t verify using OCSP, and we need to look for CRLs.
            if (verification.Count == 0)
            {
                IList<X509Crl> crls = new List<X509Crl>();
                if (pkcs7.GetCRLs() != null)
                {
                    foreach (X509Crl crl in pkcs7.GetCRLs())
                    {
                        crls.Add((X509Crl) crl);
                    }
                }
 
                // Check if the CRLs in the list were valid on a specific date.
                CRLVerifier crlVerifier = new CRLVerifier(null, crls);
                IList<VerificationOK> verificationOks = crlVerifier.Verify(signCert, issuerCert, date);
                foreach (VerificationOK verOK in verificationOks)
                {
                    verification.Add(verOK);
                }
            }
 
            if (verification.Count == 0)
            {
                OUT_STREAM.WriteLine("The signing certificate couldn't be verified");
            }
            else
            {
                foreach (VerificationOK v in verification)
                {
                    OUT_STREAM.WriteLine(v);
                }
            }
        }
 
        public static void Main(String[] args)
        {
            C5_03_CertificateValidation app = new C5_03_CertificateValidation();
 
            // Set up logger to show log messages from OCSPVerifier and CLRVerifier classes 
            SetUpLogger();
 
            // Create your own root certificate store and add certificates
            List<X509Certificate> ks = new List<X509Certificate>();
            var parser = new X509CertificateParser();
            X509Certificate rootCert;
            using (FileStream stream = new FileStream(ROOT, FileMode.Open, FileAccess.Read))
            {
                rootCert = parser.ReadCertificate(stream);
            }
 
            ks.Add(rootCert);
            app.SetKeyStore(ks);
 
            app.VerifySignatures(EXAMPLE);
 
            // Reset logger to the default value
            ResetLogger();
        }
 
        private void SetKeyStore(List<X509Certificate> ks)
        {
            this.ks = ks;
        }
 
        private static void SetUpLogger()
        {
            defaultLogAdapter = LogManager.Adapter;
            LogManager.Adapter = new CustomMemoryAdapter(OUT_STREAM);
        }
 
        private static void ResetLogger()
        {
            LogManager.Adapter = defaultLogAdapter;
        }
 
        // Custom log adapter to write log messages to the specific text writer
        private class CustomMemoryAdapter : CapturingLoggerFactoryAdapter
        {
            private TextWriter writer;
 
            public CustomMemoryAdapter(TextWriter writer)
            {
                this.writer = writer;
            }
 
            public override void AddEvent(CapturingLoggerEvent le)
            {
                writer.WriteLine(le.Source.Name + ": " + le.RenderedMessage);
                if (le.Level >= LogLevel.Warn)
                {
                    base.AddEvent(le);
                }
            }
        }
    }
}


Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now