I can sign document using itextpdf library with external signature.
But the problem is that the end user doesn't want to send his document because it may contain any sensitive data. So, I asked the end user to give document hash for signing the hash with external service and send the signed hash back.
But, here comes the problem, when they tried to sign the document with the given signed hash using itextpdf (PdfSignatureAppearance
) the PDF document is getting signed. But when verifying the signature it showing that the signature is invalid.
I've seen the same kinda question here
Sign PDF using an external service and iText .
So, the problem happens because the hash gets invalidated each time when we open the document using PdfSignatureAppearance
(itextpdf library).
Can anyone please tell me whether it is possible to sign the PDF document with previously generated hash and signed hash (which I got from external service) using itextpdf library so that the hash should not get invalidated?
class MyExternalSignatureContainer implements ExternalSignatureContainer {
protected byte[] sig;
protected Certificate[] chain;
protected PrivateKey pk;
public MyExternalSignatureContainer(byte[] sig,Certificate[] chain,PrivateKey pk) {
this.sig = sig;
this.chain=chain;
this.pk=pk;
}
public byte[] sign(InputStream is)throws GeneralSecurityException {
return sig;
}
public void modifySigningDictionary(PdfDictionary signDic) {
}
}
public byte[] emptySignature_hash(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
appearance.setCertificate(chain[0]);
ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
InputStream inp = appearance.getRangeStream();
BouncyCastleDigest digest = new BouncyCastleDigest();
byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
return hash;
}
public byte[] signed_hash(byte[] hash, PrivateKey pk, Certificate[] chain)throws GeneralSecurityException{
PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "SunPKCS11-eToken");
//return extSignature;
BouncyCastleDigest digest = new BouncyCastleDigest();
Calendar cal = Calendar.getInstance();
String hashAlgorithm = signature.getHashAlgorithm();
System.out.println(hashAlgorithm);
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.sign(sh);
System.out.println(signature.getEncryptionAlgorithm());
// Calendar cal = Calendar.getInstance();
sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
return sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
}
public void createSignature(String src, String dest, String fieldname,byte[] hash, PrivateKey pk, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(hash,chain,pk);
MakeSignature.signDeferred(reader, fieldname, os, external);
}
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
byte[] hh = app.emptySignature_hash(SRC, TEMP, "sig1", chain);
byte[] hh_sign = (app.signed_hash(hh, pk, chain));
app.createSignature(TEMP, DEST1, "sig1",hh_sign, pk, chain);
}