Reputation: 153
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
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
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