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
Checking the validity of a signature:
JAVA
JAVA
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);
}
}
C#
C#
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
Retrieving information from a signature:
JAVA
JAVA
package com.itextpdf.samples.signatures.chapter05;
import com.itextpdf.bouncycastle.asn1.tsp.TSTInfoBC;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfString;
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 com.itextpdf.signatures.TimestampConstants;
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;
import org.bouncycastle.asn1.tsp.TSTInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
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.getDigestAlgorithmName());
System.out.println("Encryption algorithm: " + pkcs7.getSignatureAlgorithmName());
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 (TimestampConstants.UNDEFINED_TIMESTAMP_DATE != pkcs7.getTimeStampDate()) {
System.out.println("TimeStamp: " + date_format.format(pkcs7.getTimeStampDate().getTime()));
TSTInfo ts = ((TSTInfoBC) pkcs7.getTimeStampTokenInfo()).getTstInfo();
System.out.println("TimeStamp service: " + ts.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);
}
}
C#
C#
using System;
using System.Collections.Generic;
using iText.Bouncycastle.Asn1.Tsp;
using iText.Commons.Bouncycastle.Cert;
using iText.Forms;
using iText.Forms.Fields;
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.GetDigestAlgorithmName());
Console.Out.WriteLine("Encryption algorithm: " + pkcs7.GetSignatureAlgorithmName());
Console.Out.WriteLine("Filter subtype: " + pkcs7.GetFilterSubtype());
// Get the signing certificate to find out the name of the signer.
IX509Certificate cert = 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 (TimestampConstants.UNDEFINED_TIMESTAMP_DATE != pkcs7.GetTimeStampDate())
{
Console.Out.WriteLine("TimeStamp: " +
pkcs7.GetTimeStampDate().ToUniversalTime().ToString("yyyy-MM-dd"));
TstInfoBC ts = (TstInfoBC)pkcs7.GetTimeStampTokenInfo();
Console.Out.WriteLine("TimeStamp service: " + ts.GetTstInfo().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 = PdfFormCreator.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
Validating the certificates of a signature:
JAVA
JAVA
package com.itextpdf.samples.signatures.chapter05;
import com.itextpdf.bouncycastle.cert.ocsp.BasicOCSPRespBC;
import com.itextpdf.commons.bouncycastle.cert.ocsp.IBasicOCSPResp;
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.TimestampConstants;
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.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.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: CN=iTextTestRoot, OU=test, O=iText, L=Minsk, C=BY\n"
+ "Subject: CN=iTextTestRsaCert01, OU=test, O=iText, L=Minsk, C=BY\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: CN=iTextTestRoot, OU=test, O=iText, L=Minsk, C=BY\n"
+ "Subject: CN=iTextTestRoot, OU=test, O=iText, L=Minsk, C=BY\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 (TimestampConstants.UNDEFINED_TIMESTAMP_DATE == cal) {
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<IBasicOCSPResp> ocsps = new ArrayList<>();
if (pkcs7.getOcsp() != null) {
ocsps.add(new BasicOCSPRespBC(((BasicOCSPRespBC) pkcs7.getOcsp()).getBasicOCSPResp()));
}
// 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());
}
}
}
C#
C#
using System;
using System.Collections.Generic;
using System.IO;
using iText.Bouncycastle.Cert;
using iText.Bouncycastle.X509;
using iText.Commons;
using iText.Commons.Bouncycastle.Asn1.Ocsp;
using iText.Commons.Bouncycastle.Cert;
using iText.Kernel.Pdf;
using iText.Signatures;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.X509;
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 ILoggerFactory defaultLoggerFactory;
private List<IX509Certificate> 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);
IX509Certificate[] 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();
// If there is no timestamp, use the current date
if (TimestampConstants.UNDEFINED_TIMESTAMP_DATE == cal) {
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++)
{
IX509Certificate cert = certs[i];
OUT_STREAM.WriteLine("=== Certificate " + i + " ===");
ShowCertificateInfo(cert, cal.ToUniversalTime());
}
// Take the signing certificate
IX509Certificate signCert = certs[0];
// Take the certificate of the issuer of that certificate (or null if it was self-signed).
IX509Certificate issuerCert = (certs.Length > 1 ? 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(IX509Certificate cert, DateTime signDate)
{
OUT_STREAM.WriteLine("Issuer: " + ((X509CertificateBC)cert).GetCertificate().IssuerDN);
OUT_STREAM.WriteLine("Subject: " + ((X509CertificateBC)cert).GetCertificate().SubjectDN);
OUT_STREAM.WriteLine("Valid from: " + ((X509CertificateBC)cert).GetCertificate().NotBefore.ToUniversalTime().ToString("yyyy-MM-dd"));
OUT_STREAM.WriteLine("Valid to: " + ((X509CertificateBC)cert).GetCertificate().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
{
((X509CertificateBC)cert).GetCertificate().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, IX509Certificate signCert, IX509Certificate issuerCert,
DateTime date)
{
IList<IBasicOcspResponse> ocsps = new List<IBasicOcspResponse>();
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<IX509Crl> crls = new List<IX509Crl>();
if (pkcs7.GetCRLs() != null)
{
foreach (IX509Crl crl in pkcs7.GetCRLs())
{
crls.Add((IX509Crl) 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<IX509Certificate> ks = new List<IX509Certificate>();
var parser = new X509CertificateParser();
IX509Certificate rootCert;
using (FileStream stream = new FileStream(ROOT, FileMode.Open, FileAccess.Read))
{
rootCert = new X509CertificateBC(parser.ReadCertificate(stream));
}
ks.Add(rootCert);
app.SetKeyStore(ks);
app.VerifySignatures(EXAMPLE);
// Reset logger to the default value
ResetLogger();
}
private void SetKeyStore(List<IX509Certificate> ks)
{
this.ks = ks;
}
private static void SetUpLogger()
{
defaultLoggerFactory = ITextLogManager.GetLoggerFactory();
ILoggerFactory customLoggerFactory = new LoggerFactory();
customLoggerFactory.AddProvider(new CustomLoggerProvider(OUT_STREAM));
ITextLogManager.SetLoggerFactory(customLoggerFactory);
}
private static void ResetLogger()
{
ITextLogManager.SetLoggerFactory(defaultLoggerFactory);
}
// Custom log provider to write log messages to the specific text writer
private sealed class CustomLoggerProvider : ILoggerProvider
{
private readonly TextWriter writer;
public CustomLoggerProvider(TextWriter writer)
{
this.writer = writer;
}
public ILogger CreateLogger(string categoryName) => new CustomLogger(categoryName, writer);
public void Dispose()
{
// no need to release any resources
}
}
private class CustomLogger : ILogger
{
private readonly string _name;
private readonly TextWriter _writer;
public CustomLogger(
string name,
TextWriter writer) =>
(_name, _writer) = (name, writer);
public IDisposable BeginScope<TState>(TState state)
{
// no need to use scope logic
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
// we allow all log levels in this simple example
return true;
}
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
_writer.WriteLine(_name + ": " + formatter(state, exception));
}
}
}
}