Hugo Hernandez
Hugo Hernandez

Reputation: 153

Extract PKCS1 from Signed PDF

I have to extract signature fields from PDF signed document to create a printed signature version. Until now I've been able to recover signer certificate, reason, signing date and other fields using this iText code:

PdfReader reader = new PdfReader(signedPdf);
AcroFields af = reader.getAcroFields();
ArrayList<String> names = af.getSignatureNames();

SimpleDateFormat sdf = new SimpleDateFormat(
                    "dd/MM/yyyy 'a las' HH:mm:ss");

for (int i = 0; i < names.size(); ++i) {

    StringBuilder sb = new StringBuilder();
    String name = names.get(i);
    PdfPKCS7 pk = af.verifySignature(name);

    String firmante = CertificateInfo.getSubjectFields(
            pk.getSigningCertificate()).getField("CN");
    sb.append("Nombre del firmante: " + firmante + "\n");

    Date signDate = pk.getSignDate().getTime();
    String sdate = sdf.format(signDate);
    sb.append("Fecha y hora de la firma: " + sdate + "\n");


    String razon = pk.getReason();
    sb.append("proposito: " + razon + "\n");
}

As far as I know, the PDF signature was made with iText PdfPkcs7 class using setExternalDigest method to add a PKCS1 byte array created in an external application. The file looks correctly signed and validated by external tools.

// Create the signature
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA1", "BC", null, false);

//pkcs1Bytes is a byte array with the signed document hash
sgn.setExternalDigest(pkcs1Bytes, null, "RSA");

However, one of the required fields for printed version is a "signature digital stamp" which is a base 64 string of signed document hash or PKCS1.

It's possible to extract that PKCS1 bytes from the signed PDF document?

EDITED: I forgot to mention that when I use PdfPKCS7.getEncodedPKCS1() method after verifying signature it throws ExceptionConverter: java.security.SignatureException: object not initialized for signing

Upvotes: 3

Views: 3224

Answers (2)

pedrofb
pedrofb

Reputation: 39291

I have reviewed the code and seems the class PdfPKCS7 does not allow to access the digest. But, the content is stored in a private member PdfPKCS7.digest. So using reflection will allow you to extract it. I have found a similar example here and here (is basically the same)

PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name);
pdfPkcs7.verify();

Field digestField = PdfPKCS7.class.getDeclaredField("digest");
digestField.setAccessible(true);
byte[] digest = (byte[]) digestField.get(pdfPkcs7);

I think the variable you need is digest because the value is assigned in getEncodedPKCS1 when performing the signature

 public byte[] getEncodedPKCS1() {
   try {
        if (externalDigest != null)
            digest = externalDigest;
        else
            digest = sig.sign();

     //skipped content

And is used in verify() in the following way verifyResult = sig.verify(digest);

Note that digest is a private variable, so the name or content could depend on the version. Review the code of your specific version.

Upvotes: 4

mkl
mkl

Reputation: 96039

Considering your code I assume you are using a 5.x iText version, not a 7.x.

You can either use reflection (cf. this older answer or pedrofb's answer here) or you can simply extract the CMS signature container using iText and then analyze that container using BouncyCastle; a version of BC usually already is present anyways if you use signature related functionality of iText.

As the OP has already observed, PdfPKCS7.getEncodedPKCS7() fails with "ExceptionConverter: java.security.SignatureException: object not initialized for signing". The reason is that this method is meant for retrieving a signature container newly constructed by the PdfPKCS7 instance.

To extract the CMS signature container using iText you can use this code instead:

AcroFields fields = reader.getAcroFields();
PdfDictionary sigDict = fields.getSignatureDictionary(name); 
PdfString contents = sigDict.getAsString(PdfName.CONTENTS);
byte[] contentBytes = contents.getOriginalBytes();

contentBytes now contains the encoded CMS container (plus some trailing bytes, usually null bytes, as the Contents value usually is larger than required for the signature container).

Analyzing this container using BouncyCastle is not difficult but the details may depend on the exact BouncyCastle version you use.

Upvotes: 3

Related Questions