Reputation: 33
Basically I get the error Document has been altered or corruped since the Signature was applied, I followed the example of itext website and adapted to my situation.
I ran out of ideas and hope I can get some help here's a snippet of my code:
public static final String src = "DynamicClient\sample.pdf";
public static final String temp= "DynamicClient\sampleTEMP.pdf";
public static final String dest= "DynamicClient\sampleFINAL.pdf";
public static void emptySignatureSVC(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
reader.setAppendable(true);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), 1, fieldname);
appearance.setCertificate(chain[0]);
ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ETSI_CADES_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
reader.close();
}
public static void createSignatureSVC(String src, String dest, String fieldname, byte[] sign, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(sign, chain);
MakeSignature.signDeferred(reader, fieldname, os, external);
}
public static void main(String[] args) throws Exception {
Certificate[] chain = signWS.getChain();//External WS to get chain
emptySignatureSVC(src, temp, "signature", chain);
InputStream is = new FileInputStream(new File(temp));
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA256"));
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
sgn.setExternalDigest(extSignature, null, "RSA");
createSignatureSVC(temp, dest, "signature", sgn.getEncodedPKCS7(hash,null, null, null, CryptoStandard.CADES), chain);
}
and here are the files: original one Signed one
EDIT: Forgot to add the container I use:
class MyExternalSignatureContainer implements ExternalSignatureContainer {
protected byte[] sig;
protected Certificate[] chain;
public MyExternalSignatureContainer(byte[] sig,Certificate[] chain) {
this.sig = sig;
this.chain=chain;
}
public byte[] sign(InputStream is)throws GeneralSecurityException {
return sig;
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
// TODO Auto-generated method stub
}
}
Upvotes: 3
Views: 822
Reputation: 95898
There are two issues in your code:
You are hashing the whole prepared PDF like this:
InputStream is = new FileInputStream(new File(temp));
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA256"));
This is not correct.
A signed PDF essentially has this structure (read here for more details):
(By the way, the sketch is not 100% correct as the angled bracket delimiters '<' and '>' around the signature value must also not be hashed.)
Your prepared PDF at temp
has the same structure, merely the "signature value" is not yet an actual signature value but instead a placeholder of 8192 hex-encoded zero bytes (8192 because that's the number you gave in MakeSignature.signExternalContainer
).
As you can see in the sketch, though, the signature value (or in your case, the placeholder) must not be hashed for signing.
You can calculate the hash using the data returned by PdfSignatureAppearance.getRangeStream
after the MakeSignature.signExternalContainer
as proposed by @LkbhaiLr in his answer.
Alternatively you can use (instead of the ExternalBlankSignatureContainer
) a custom ExternalSignatureContainer
implementation which in its sign
method simply hashes its InputStream
parameter and provides that hash.
I'd prefer the latter approach but the former approach also is in wide use.
You retrieve the naked signature value like this:
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
I.e. you forward the attributes structure as is to your signing service and expect it to create a proper signature value for it.
Inspecting your signed example file, though, it becomes clear that that signing service does not live up to your expectations: Instead of signing with SHA256withRSA, it merely encrypts with RSA and PKCS#1 padding. Thus, it expects you to do half the signing for you, i.e. hashing with SHA256 and building a DigestInfo
structure.
Thus, before calling signWS
you have to hash that attributes structure returned by sgn.getAuthenticatedAttributeBytes
and then wrap the resulting hash value in a DigestInfo
object.
As you calculate hashes elsewhere, that shouldn't be an issue for you. And for wrapping a hash in a DigestInfo
RFC 8017 section 9.2 note 1 offers a short cut:
For the nine hash functions mentioned in Appendix B.1, the DER encoding T of the
DigestInfo
value is equal to the following:... SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H. ...
I.e. you only have to prefix your hash with the byte sequence 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
.
Upvotes: 4
Reputation: 264
Try this
public static final String src = "DynamicClient\sample.pdf";
public static final String temp= "DynamicClient\sampleTEMP.pdf";
public static final String dest= "DynamicClient\sampleFINAL.pdf";
public byte[] emptySignatureSVC(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
reader.setAppendable(true);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), 1, fieldname);
appearance.setCertificate(chain[0]);
ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ETSI_CADES_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
InputStream inp = appearance.getRangeStream();
BouncyCastleDigest digest = new BouncyCastleDigest();
reader.close();
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");
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());
sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
return sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
}
public static void createSignatureSVC(String src, String dest, String fieldname, byte[] sign, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(sign, chain);
MakeSignature.signDeferred(reader, fieldname, os, external);
}
public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
byte[] hh = emptySignatureSVC(src, temp, "sig1", chain);
byte[] hh_sign = (app.signed_hash(hh, pk, chain));
createSignatureSVC(temp, dest, "sig1",hh_sign, chain);
}
Upvotes: 2