I've been toying with iTextSharp 5.5.7 for a while and can't find the right way to make a valid digital signature for PDF from Smart Card - Adobe Reader always says its signed by and unknown and can't decode signatures' DER data.
I've looked at MakeSignature.cs code for reference and what is does:
Stream data = signatureAppearance.GetRangeStream();
// gets the first hash
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
// gets the second hash or is it not a hash at all ?
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, crlBytes, sigtype);
then, according to "sign" method in IExternalSignature.cs
"@param message the message you want to be hashed and signed"
// looks like externalSignature.Sign() should make another hash out of "sh"
// and use this hash to compute a signature
byte[] extSignature = externalSignature.Sign(sh);
so I understood the procedure of signing as the following:
- source PDF is loaded
- new PDF with empty signature field is created
- Byte range of that field is hashed (by default produces 20 bytes for sha-1, tried 32 bytes with sha-256 too)
- that Hash + some other properties are hashed again (number of bytes varies, why? might not be a hash after all?)
- that second hash is hashed again inside of external signature object
- that third hash is finally sent to a Smart Card to compute a signature
- Signature is inserted into new PDF
When i sign PDF with Adobe Reader, at step 6, the third hash is 32 bytes long. From Smart Card's perspective i do the same steps with both Acrobat and iText, but with iText the signature is invalid, what could be wrong ?
the code i use:
public void StartTest(){
X509Certificate2 cert = new X509Certificate2();
cert.Import("cert.cer"); // certificate obtained from smart card
X509CertificateParser certParse = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { certParse.ReadCertificate(cert.RawData) };
// Reader and stamper
PdfReader pdfReader = new PdfReader("original.pdf");
Stream signedPdf = new FileStream("signed.pdf", FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0', null, false);
// Appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SignatureCreator = "Me";
appearance.Reason = "Testing iText";
appearance.Location = "On my Laptop";
appearance.SignatureGraphic = Image.GetInstance("img.png"); // visual image
appearance.SetVisibleSignature(new Rectangle(50, 50, 250, 100), pdfReader.NumberOfPages, "Signature");
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
// Timestamp
TSAClientBouncyCastle tsc = new TSAClientBouncyCastle("http://ts.cartaodecidadao.pt/tsa/server", "", "");
// Digital signature
IExternalSignature externalSignature = new MyExternalSignature2("SHA-1");
MyMakeSignature.SignDetached(appearance, externalSignature, chain, null, null, tsc, 0, CryptoStandard.CADES);
stamper.Close();
}
external signature implementation (class MyExternalSignature2):
class MyExternalSignature2 : IExternalSignature
{
private String hashAlgorithm;
private String encryptionAlgorithm;
public MyExternalSignature2(String hashAlgorithm)
{
this.encryptionAlgorithm = "RSA";
this.hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigests(hashAlgorithm));
}
public virtual byte[] Sign(byte[] message) {
byte[] hash = null;
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
{
hash = sha1.ComputeHash(message);
}
byte[] sig = MySC.GetSignature(hash);
return sig;
}
public virtual String GetHashAlgorithm() {
return hashAlgorithm;
}
public virtual String GetEncryptionAlgorithm() {
return encryptionAlgorithm;
}
}