Monday, November 24, 2008

How to sign a PDF using iText and iTextSharp

Source: http://itextpdf.sourceforge.net/howtosign.html
====================

How to sign a PDF using iText and iTextSharp


Index


Introduction

iText supports visible and invisible signing using the following modes:

  • Self signed (Adobe.PPKLite)
  • VeriSign plug-in (VeriSign.PPKVS)
  • Windows Certificate Security (Adobe.PPKMS)

Signing and verifying with iText is easy, and it's always done the same way, the difficult part comes with the key and certificate generation. This is a quick guide and doesn't replace the know how you should have on cryptography to make the best use of the technology.


How to sign

Signing is done with this simple code:

KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
stp.close();

How to obtain the private key my_private_key.pfx and the certificates my_certificates.p7b will be explained later.


How to certify

In addition to signing the documents can be certified. Any change to the document that goes against the certification level will make it lose the certified status. The certification level can be: NOT_CERTIFIED, CERTIFIED_NO_CHANGES_ALLOWED, CERTIFIED_FORM_FILLING and CERTIFIED_FORM_FILLING_AND_ANNOTATIONS. To certify a document sign it as in the previous example but add

sap.setCertificationLevel(PdfSignatureApperance.CERTIFIED_NO_CHANGES_ALLOWED);
before closing. This implementation of certification will only work in Acrobat 7 and later.

How to do multiple signatures

Signing as just described will invalidate any existing signatures in the document. To add another signature a new revision is needed. To create a new revision just create the PdfStamper the following way and do the rest as already explained:

PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', null, true);

How to verify

Verifying is a three step process:

  • Was the document changed?
  • What revision does the signature cover? Does the signature cover all the document?
  • Can the signature certificates be verified in your trusted identities store?
Here's an example on how to do it:

KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
PdfReader reader = new PdfReader("my_signed_doc.pdf");
AcroFields af = reader.getAcroFields();
ArrayList names = af.getSignatureNames();
for (int k = 0; k < names.size(); ++k) {
String name = (String)names.get(k);
System.out.println("Signature name: " + name);
System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
System.out.println("Document revision: " + af.getRevision(name) + " of " + af.getTotalRevisions());
// Start revision extraction
FileOutputStream out = new FileOutputStream("revision_" + af.getRevision(name) + ".pdf");
byte bb[] = new byte[8192];
InputStream ip = af.extractRevision(name);
int n = 0;
while ((n = ip.read(bb)) > 0)
out.write(bb, 0, n);
out.close();
ip.close();
// End revision extraction
PdfPKCS7 pk = af.verifySignature(name);
Calendar cal = pk.getSignDate();
Certificate pkc[] = pk.getCertificates();
System.out.println("Subject: " + PdfPKCS7.getSubjectFields(pk.getSigningCertificate()));
System.out.println("Document modified: " + !pk.verify());
Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal);
if (fails == null)
System.out.println("Certificates verified against the KeyStore");
else
System.out.println("Certificate failed: " + fails[1]);
}

Note that if you were using the self signed mode you would have to load kall with the certificate provided by whoever signed the document. The certificate could be loaded this way:

CertificateFactory cf = CertificateFactory.getInstance("X509");
Collection col = cf.generateCertificates(new FileInputStream("self.p7b"));
KeyStore kall = KeyStore.getInstance(KeyStore.getDefaultType());
kall.load(null, null);
for (Iterator it = col.iterator(); it.hasNext();) {
X509Certificate cert = (X509Certificate)it.next();
kall.setCertificateEntry(cert.getSerialNumber().toString(Character.MAX_RADIX), cert);
}

How to generate the keys for the self signed mode

To generate a key to self sign:

keytool -genkey -keyalg RSA -alias myname -keypass password -keystore keystore.ks -dname "cn=Paulo Soares, c=PT"

Answer the questions and at the end you have a self signed certificate at keystore.ks. You can omit the -dname option but dont't forget to put the 2 letter country code when asked to. You can now sign the document with:

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream("keystore.ks"), "keystore_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
stp.close();

To generate the certificate that can be validated at the receiving end do:

keytool -export -alias myname -file export.cer -keystore keystore.ks

The generated certificate export.cer can be imported by iText or Acrobat for validation.


How to sign with VeriSign and Microsoft Windows Certificate

To sign documents with VeriSign you need a key that must be certified by VeriSign. You can acquire a 60 day trial key or buy one at https://digitalid.verisign.com/client/class1MS.htm.

The Acrobat VeriSign plug-in (that you can get here) will only work with VeriSign certified keys but the Microsoft Windows Certificate will work with any trusted certificate and in addition to the VeriSign certificate you can also use a free Thawte certificate found at https://www.thawte.com/email/index.html.

After going through all the CA instructions you'll end up with a certificate in your IE. You'll have to export the private key including all the certificated in the certification path. The result is a .pfx file with your private key that can be used to sign the documents.


How to acquire a permanent key from a CA

To acquire a permanent key from a CA such as VeriSign you usually need to go through the following steps:

  1. Generate a self signed key with
    keytool -genkey -keyalg RSA
  2. Generate a certificate signing request with
    keytool -certreq
    the result is base64 data that you must cut and paste in the CA site when prompted
  3. The CA will send a certificate reply chain that you must import with
    keytool -import
    using the same alias

How to sign with an external signature and a standard filter

Some examples follow. Replace with your signing code in the appropriate places.

An example with an external hash in Windows Certificate Mode

KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
sap.setExternalDigest(null, new byte[20], null);
sap.preClose();
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
byte hash[] = messageDigest.digest();
PdfSigGenericPKCS sg = sap.getSigStandard();
PdfLiteral slit = (PdfLiteral)sg.get(PdfName.CONTENTS);
byte[] outc = new byte[(slit.getPosLength() - 2) / 2];
PdfPKCS7 sig = sg.getSigner();
sig.setExternalDigest(null, hash, null);
PdfDictionary dic = new PdfDictionary();
byte[] ssig = sig.getEncodedPKCS7();
System.arraycopy(ssig, 0, outc, 0, ssig.length);
dic.put(PdfName.CONTENTS, new PdfString(outc).setHexWriting(true));
sap.close(dic);

An example with an external hash and signature in Windows Certificate Mode

KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
sap.setExternalDigest(new byte[128], new byte[20], "RSA");
sap.preClose();
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
byte hash[] = messageDigest.digest();
PdfSigGenericPKCS sg = sap.getSigStandard();
PdfLiteral slit = (PdfLiteral)sg.get(PdfName.CONTENTS);
byte[] outc = new byte[(slit.getPosLength() - 2) / 2];
PdfPKCS7 sig = sg.getSigner();
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(key);
sign.update(hash);
sig.setExternalDigest(sign.sign(), hash, "RSA");
PdfDictionary dic = new PdfDictionary();
byte[] ssig = sig.getEncodedPKCS7();
System.arraycopy(ssig, 0, outc, 0, ssig.length);
dic.put(PdfName.CONTENTS, new PdfString(outc).setHexWriting(true));
sap.close(dic);

An example with an external signature in Self Sign Mode

KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);
sap.setReason("I'm the author");
sap.setLocation("Lisbon");
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
sap.setExternalDigest(new byte[128], null, "RSA");
sap.preClose();
PdfPKCS7 sig = sap.getSigStandard().getSigner();
Signature sign = Signature.getInstance("SHA1withRSA");
sign.initSign(key);
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
sign.update(buf, 0, n);
}
sig.setExternalDigest(sign.sign(), null, "RSA");
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.CONTENTS, new PdfString(sig.getEncodedPKCS1()).setHexWriting(true));
sap.close(dic);

How to sign with an external signature dictionary (DSE200 example)

An example with an external signature dictionary with the nCipher DSE 200 (this example is obsolete and should only be used with Acrobat 5)

PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
sap.setLayer2Text("This is some custom made text.");
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.FT, PdfName.SIG);
dic.put(PdfName.FILTER, new PdfName("nCipher.TimeSeal"));
dic.put(PdfName.SUBFILTER, new PdfName("ntse.dse.1"));
sap.setCryptoDictionary(dic);
HashMap exc = new HashMap();
exc.put(PdfName.CONTENTS, new Integer(0x1802));
exc.put(PdfName.M, new Integer(0x19));
exc.put(PdfName.NAME, new Integer(0x1f));
sap.preClose(exc);

MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
byte hash[] = messageDigest.digest();
// The DataImprint object is the "data to be time-stamped"
DataImprint dataImprint = new DataImprint();
dataImprint.setHashAlgorithm(new AlgorithmIdentifier(AlgorithmIdentifier.md5));
dataImprint.setHashedData(hash);

TimeStampRequest tsq = new TimeStampRequest();

tsq.setDataImprint(dataImprint);

// Set the nonce. It allows the client to verify the timeliness
// of the response when no trusted local clock is available.
Random rand = new Random(new Date().getTime());
tsq.setNonce(BigInteger.valueOf(rand.nextLong()));

// Request that the TSA signer certificate be included in the TimeStampToken.
// This allows a TimeStampToken signature to be verified without an
// external TSA certificate.
tsq.setCertReq(true);

// Set the request policy under which this TimeStampToken should be issued.
tsq.setReqPolicy(new PolicyIdentifier(PolicyIdentifier.serviceClass));

// Create the encoded request.
byte[] encodedTSQ = tsq.encodeRequest();

// Send the request to the Trusted Time StampServer
// and get the response (an encoded TimeStampToken)
TimeStampServerTCP tss = new TimeStampServerTCP("dse200 address");
byte[] encodedTST = tss.submitRequest(encodedTSQ, REQUEST_TIMEOUT);
// Decode the token to access the contained information
TimeStampToken tst = new TimeStampToken(encodedTST);

if(!tst.verifySignature())
throw new SignatureException("TimeStampToken signature invalid!");
TSTInfo tstInfo = tst.getTSTInfo();
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
cal.setTime(tstInfo.getDate());
StringTokenizer tk = new StringTokenizer(tstInfo.getTsaName(), ";");
String sn = "";
while (tk.hasMoreTokens()) {
String x;
if ((x = tk.nextToken()).startsWith("CN=")) {
sn = x.substring(3);
break;
}
}

PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(encodedTST).setHexWriting(true));
dic2.put(PdfName.M, new PdfDate(cal));
dic2.put(PdfName.NAME, new PdfString(sn));
sap.close(dic2);

How to sign with an external signature dictionary using BouncyCastle CMS

An example with an external signature dictionary with the BouncyCastle CMS. The detached and encapsulated methods are presented.

import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfDate;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfPKCS7;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfSignature;
import com.lowagie.text.pdf.PdfSignatureAppearance;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfString;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.GregorianCalendar;
import java.util.HashMap;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.cert.Certificate;
import java.security.PrivateKey;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;

public static void SignDetached() {
try {
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("my_private_key.pfx"), "password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);

PdfReader reader = new PdfReader("original.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, new FileOutputStream("signedDetached.pdf"), '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);
sap.setSignDate(new GregorianCalendar());
sap.setCrypto(null, chain, null, null);
sap.setReason("I like to sign");
sap.setLocation("Universe");
sap.setAcro6Layers(true);
sap.setRender(PdfSignatureAppearance.SignatureRenderNameAndDescription);
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setDate(new PdfDate(sap.getSignDate()));
dic.setName(PdfPKCS7.getSubjectFields((X509Certificate)chain[0]).getField("CN"));
if (sap.getReason() != null)
dic.setReason(sap.getReason());
if (sap.getLocation() != null)
dic.setLocation(sap.getLocation());
sap.setCryptoDictionary(dic);
int csize = 4000;
HashMap exc = new HashMap();
exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));
sap.preClose(exc);

CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSigner(key, (X509Certificate)chain[0], CMSSignedDataGenerator.DIGEST_SHA1);

ArrayList list = new ArrayList();
for (int i = 0; i < chain.length; i++) {
list.add(chain[i]);
}
CertStore chainStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(list), "BC");
generator.addCertificatesAndCRLs(chainStore);
CMSProcessable content = new CMSProcessableRange(sap);
CMSSignedData signedData = generator.generate(content, false, "BC");
byte[] pk = signedData.getEncoded();

byte[] outc = new byte[csize];

PdfDictionary dic2 = new PdfDictionary();

System.arraycopy(pk, 0, outc, 0, pk.length);

dic2.put(PdfName.CONTENTS, new PdfString(outc).setHexWriting(true));
sap.close(dic2);
}
catch (Exception e) {
e.printStackTrace();
}
}

public static void signEncapsulated() {
try {
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("my_private_key.pfx"), "password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);

PdfReader reader = new PdfReader("original.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, new FileOutputStream("signedEncapsulated.pdf"), '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);
sap.setSignDate(new GregorianCalendar());
sap.setCrypto(null, chain, null, null);
sap.setReason("I like to sign");
sap.setLocation("Universe");
sap.setAcro6Layers(true);
sap.setRender(PdfSignatureAppearance.SignatureRenderNameAndDescription);
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
dic.setDate(new PdfDate(sap.getSignDate()));
dic.setName(PdfPKCS7.getSubjectFields((X509Certificate)chain[0]).getField("CN"));
if (sap.getReason() != null)
dic.setReason(sap.getReason());
if (sap.getLocation() != null)
dic.setLocation(sap.getLocation());
sap.setCryptoDictionary(dic);
int csize = 4000;
HashMap exc = new HashMap();
exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));
sap.preClose(exc);

MessageDigest md = MessageDigest.getInstance("SHA1");
InputStream s = sap.getRangeStream();
int read = 0;
byte[] buff = new byte[8192];
while ((read = s.read(buff, 0, 8192)) > 0) {
md.update(buff, 0, read);
}

CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSigner(key, (X509Certificate)chain[0], CMSSignedDataGenerator.DIGEST_SHA1);

ArrayList list = new ArrayList();
for (int i = 0; i < chain.length; i++) {
list.add(chain[i]);
}
CertStore chainStore = CertStore.getInstance("Collection", new
CollectionCertStoreParameters(list), "BC");
generator.addCertificatesAndCRLs(chainStore);
CMSProcessable content = new CMSProcessableByteArray(md.digest());
CMSSignedData signedData = generator.generate(content, true, "BC");
byte[] pk = signedData.getEncoded();

byte[] outc = new byte[csize];

PdfDictionary dic2 = new PdfDictionary();

System.arraycopy(pk, 0, outc, 0, pk.length);

dic2.put(PdfName.CONTENTS, new PdfString(outc).setHexWriting(true));
sap.close(dic2);
}
catch (Exception e) {
e.printStackTrace();
}
}

public class CMSProcessableRange implements CMSProcessable {
private PdfSignatureAppearance sap;
private byte[] buf = new byte[8192];

public CMSProcessableRange(PdfSignatureAppearance sap) {
this.sap = sap;
}

public void write(OutputStream outputStream) throws IOException, CMSException {
InputStream s = sap.getRangeStream();
ByteArrayOutputStream ss = new ByteArrayOutputStream();
int read = 0;
while ((read = s.read(buf, 0, buf.length)) > 0) {
outputStream.write(buf, 0, read);
}
}

public Object getContent() {
return sap;
}
}

How to sign with an external signature dictionary (authenticatedAttributes example)

An example with an external signature dictionary with authenticatedAttributes

KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("my_private_key.pfx"),
"my_password".toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);

PdfReader reader = new PdfReader("original.pdf");
FileOutputStream fout = new FileOutputStream("signed.pdf");
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
PdfSignatureAppearance sap = stp.getSignatureAppearance();
// comment next line to have an invisible signature
sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
sap.setLayer2Text("This is some custom made text.\n\nDate: some date");
Calendar cal = Calendar.getInstance();
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.FT, PdfName.SIG);
dic.put(PdfName.FILTER, new PdfName("SAFE.PPKSF"));
dic.put(PdfName.SUBFILTER, new PdfName("adbe.pkcs7.detached"));
dic.put(PdfName.M, new PdfDate(cal));
dic.put(PdfName.NAME, new
PdfString(PdfPKCS7.getSubjectFields((X509Certificate)chain[0]).getField("CN")));
sap.setCryptoDictionary(dic);
HashMap exc = new HashMap();
exc.put(PdfName.CONTENTS, new Integer(0x2502));
sap.preClose(exc);

PdfPKCS7 pk7 = new PdfPKCS7(key, chain, null, "SHA1", null, false);
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
byte buf[] = new byte[8192];
int n;
InputStream inp = sap.getRangeStream();
while ((n = inp.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
byte hash[] = messageDigest.digest();
byte sh[] = pk7.getAuthenticatedAttributeBytes(hash, cal);
pk7.update(sh, 0, sh.length);
PdfDictionary dic2 = new PdfDictionary();
byte sg[] = pk7.getEncodedPKCS7(hash, cal);
byte out[] = new byte[0x2500 / 2];
System.arraycopy(sg, 0, out, 0, sg.length);
dic2.put(PdfName.CONTENTS, new PdfString(out).setHexWriting(true));
sap.close(dic2);

How to verify with iTextSharp

How to verify a signature using the certificates in the Windows certificate store using WSE.

using wse = Microsoft.Web.Services.Security.X509;
using Org.BouncyCastle.X509;

wse.X509CertificateStore store = new wse.X509CertificateStore(wse.X509CertificateStore.StoreProvider.System, wse.X509CertificateStore.StoreLocation.LocalMachine, wse.X509CertificateStore.RootStore);
store.Open();
wse.X509CertificateCollection c = store.Certificates;
System.Console.Out.WriteLine("There are " + c.Count + " certificates in the Windows certificate store");
X509CertificateParser parser = new X509CertificateParser();
ArrayList kall = new ArrayList();
foreach (wse.X509Certificate cert in c) {
X509Certificate c2 = parser.ReadCertificate(cert.GetRawCertData());
kall.Add(c2);
}
PdfReader reader = new PdfReader("signed.pdf");
AcroFields af = reader.AcroFields;
ArrayList names = af.GetSignatureNames();
for (int k = 0; k < names.Count; ++k) {
String name = (String)names[k];
System.Console.Out.WriteLine("Signature name: " + name);
System.Console.Out.WriteLine("Signature covers whole document: " + af.SignatureCoversWholeDocument(name));
System.Console.Out.WriteLine("Document revision: " + af.GetRevision(name) + " of " + af.TotalRevisions);
PdfPKCS7 pk = af.VerifySignature(name);
DateTime cal = pk.SignDate;
X509Certificate[] pkc = pk.Certificates;
System.Console.Out.WriteLine("Subject: " + PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("CN"));
System.Console.Out.WriteLine("Document modified: " + !pk.Verify());
Object[] fails = PdfPKCS7.VerifyCertificates(pkc, kall, null, cal);
if (fails == null)
System.Console.Out.WriteLine("Certificates verified against the KeyStore");
else
System.Console.Out.WriteLine("Certificate failed: " + fails[1]);
}

How to sign with a smartcard using an external signature dictionary with iTextSharp, CAPICOM and .NET 1.1

This example allows the signing with smartcards. The signing is done using the Windows mode and includes the hash in the signature. This example requires .NET 1.1 and CAPICOM 2.0 as a COM Interop (select the CAPICOM as a reference and Visual Studio will create the C# interfaces and classes).

using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using CAPICOM;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace CCapi {
class Class1 {
static void Main(string[] args) {
Certificate cer = GetCertificate();
Org.BouncyCastle.X509.X509Certificate[] chain = GetChain(cer);
PdfReader reader = new PdfReader("c:\\hello.pdf");
PdfStamper stp = PdfStamper.CreateSignature(reader, new FileStream("c:\\hello_hashedcapi.pdf", FileMode.Create), '\0');
PdfSignatureAppearance sap = stp.SignatureAppearance;
sap.SetVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);
sap.SignDate = DateTime.Now;
sap.SetCrypto(null, chain, null, null);
sap.Reason = "I like to sign";
sap.Location = "Universe";
sap.Acro6Layers = true;
sap.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = PdfPKCS7.GetSubjectFields(chain[0]).GetField("CN");
if (sap.Reason != null)
dic.Reason = sap.Reason;
if (sap.Location != null)
dic.Location = sap.Location;
sap.CryptoDictionary = dic;
int csize = 4000;
Hashtable exc = new Hashtable();
exc[PdfName.CONTENTS] = csize * 2 + 2;
sap.PreClose(exc);

HashAlgorithm sha = new SHA1CryptoServiceProvider();

Stream s = sap.RangeStream;
int read = 0;
byte[] buff = new byte[8192];
while ((read = s.Read(buff, 0, 8192)) > 0) {
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
byte[] pk = SigMsg(sha.Hash, cer, false);
byte[] outc = new byte[csize];

PdfDictionary dic2 = new PdfDictionary();

Array.Copy(pk, 0, outc, 0, pk.Length);

dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
sap.Close(dic2);
}

public static byte[] SigMsg(byte[] msg, Certificate cert, bool detached) {
SignerClass signer = new SignerClass();

// check for private key in the certificate
if (cert.HasPrivateKey())
signer.Certificate = cert;
else
throw new ArgumentException("Certificate has no private key");
UtilitiesClass ut = new UtilitiesClass();
SignedDataClass sd = new SignedDataClass();
string gg = ut.ByteArrayToBinaryString(msg);
sd.Content = gg;
string signedData = sd.Sign(signer, detached, CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64);
return Convert.FromBase64String(signedData);
}

public static Certificate GetCertificate() {
StoreClass store = new StoreClass();
// can also be CAPICOM_STORE_LOCATION.CAPICOM_CURRENT_USER_STORE
store.Open(CAPICOM_STORE_LOCATION.CAPICOM_SMART_CARD_USER_STORE,
"My", CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_READ_ONLY);
Certificate cc = null;
// choose the certificate. May also choose by serial id, etc.
foreach (Certificate cert in store.Certificates) {
if (cert.SubjectName.IndexOf("Paulo Soares") > 0) {
cc = cert;
break;
}
}
return cc;
}

public static Org.BouncyCastle.X509.X509Certificate[] GetChain(Certificate cer) {
ICertContext context = cer as ICertContext;
IntPtr ptr = new IntPtr(context.CertContext);
X509Certificate xCert = new X509Certificate(ptr);
Org.BouncyCastle.X509.X509CertificateParser cp =
new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain =
new Org.BouncyCastle.X509.X509Certificate[]{cp.ReadCertificate(xCert.GetRawCertData())};
return chain;
}
}
}

How to sign with a smartcard using an external signature dictionary with iTextSharp and .NET 2.0

This example allows the signing with smartcards. Two functions are provided: SignHashed() signs using the Windows mode and includes the hash in the signature and SignDetached() signs in detached mode but needs more memory to do it. This example requires .NET 2.0.

using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography;
using iTextSharp.text;
using iTextSharp.text.pdf;

public static void SignHashed() {
X509Certificate2 card = GetCertificate();
Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[]{cp.ReadCertificate(card.RawData)};

PdfReader reader = new PdfReader("c:\\hello.pdf");
PdfStamper stp = PdfStamper.CreateSignature(reader, new FileStream("c:\\hello_hashed.pdf", FileMode.Create), '\0');
PdfSignatureAppearance sap = stp.SignatureAppearance;
sap.SetVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);
sap.SignDate = DateTime.Now;
sap.SetCrypto(null, chain, null, null);
sap.Reason = "I like to sign";
sap.Location = "Universe";
sap.Acro6Layers = true;
sap.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = PdfPKCS7.GetSubjectFields(chain[0]).GetField("CN");
if (sap.Reason != null)
dic.Reason = sap.Reason;
if (sap.Location != null)
dic.Location = sap.Location;
sap.CryptoDictionary = dic;
int csize = 4000;
Hashtable exc = new Hashtable();
exc[PdfName.CONTENTS] = csize * 2 + 2;
sap.PreClose(exc);

HashAlgorithm sha = new SHA1CryptoServiceProvider();

Stream s = sap.RangeStream;
int read = 0;
byte[] buff = new byte[8192];
while ((read = s.Read(buff, 0, 8192)) > 0) {
sha.TransformBlock(buff, 0, read, buff, 0);
}
sha.TransformFinalBlock(buff, 0, 0);
byte[] pk = SignMsg(sha.Hash, card, false);

byte[] outc = new byte[csize];

PdfDictionary dic2 = new PdfDictionary();

Array.Copy(pk, 0, outc, 0, pk.Length);

dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
sap.Close(dic2);
}

public static void SignDetached() {
X509Certificate2 card = GetCertificate();
Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[]{cp.ReadCertificate(card.RawData)};

PdfReader reader = new PdfReader("c:\\hello.pdf");
PdfStamper stp = PdfStamper.CreateSignature(reader, new FileStream("c:\\hello_detached.pdf", FileMode.Create), '\0');
PdfSignatureAppearance sap = stp.SignatureAppearance;
sap.SetVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);
sap.SignDate = DateTime.Now;
sap.SetCrypto(null, chain, null, null);
sap.Reason = "I like to sign";
sap.Location = "Universe";
sap.Acro6Layers = true;
sap.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = PdfPKCS7.GetSubjectFields(chain[0]).GetField("CN");
if (sap.Reason != null)
dic.Reason = sap.Reason;
if (sap.Location != null)
dic.Location = sap.Location;
sap.CryptoDictionary = dic;
int csize = 4000;
Hashtable exc = new Hashtable();
exc[PdfName.CONTENTS] = csize * 2 + 2;
sap.PreClose(exc);

Stream s = sap.RangeStream;
MemoryStream ss = new MemoryStream();
int read = 0;
byte[] buff = new byte[8192];
while ((read = s.Read(buff, 0, 8192)) > 0) {
ss.Write(buff, 0, read);
}
byte[] pk = SignMsg(ss.ToArray(), card, true);

byte[] outc = new byte[csize];

PdfDictionary dic2 = new PdfDictionary();

Array.Copy(pk, 0, outc, 0, pk.Length);

dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
sap.Close(dic2);
}

// Sign the message with the private key of the signer.
static public byte[] SignMsg(Byte[] msg, X509Certificate2 signerCert, bool detached) {
// Place message in a ContentInfo object.
// This is required to build a SignedCms object.
ContentInfo contentInfo = new ContentInfo(msg);

// Instantiate SignedCms object with the ContentInfo above.
// Has default SubjectIdentifierType IssuerAndSerialNumber.
SignedCms signedCms = new SignedCms(contentInfo, detached);

// Formulate a CmsSigner object for the signer.
CmsSigner cmsSigner = new CmsSigner(signerCert);

// Include the following line if the top certificate in the
// smartcard is not in the trusted list.
cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;

// Sign the CMS/PKCS #7 message. The second argument is
// needed to ask for the pin.
signedCms.ComputeSignature(cmsSigner, false);

// Encode the CMS/PKCS #7 message.
return signedCms.Encode();
}

public static X509Certificate2 GetCertificate() {
X509Store st = new X509Store(StoreName.My, StoreLocation.CurrentUser);
st.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = st.Certificates;
X509Certificate2 card = null;
X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(col, "Certificates", "Select one to sign", X509SelectionFlag.SingleSelection);
if (sel.Count > 0) {
X509Certificate2Enumerator en = sel.GetEnumerator();
en.MoveNext();
card = en.Current;
}
st.Close();
return card;
}

2 comments:

Parveen said...

One of my junior in office is used to get confused among public key and private key.I gave the link of your post to him and hope that it will really help him while signing the PDF using iText

lauren said...

Although a lots of code is involved in the post but surely I'll try to use it a i need to sign the PDFs using iText and iTextSharp.moreover my performance measures will be calculated on successfully completing this assignment given at office
electronic signatures