Joao Benevides
Joao Benevides

Reputation: 33

Document has been altered or corruped since the Signature was applied itext 5.5.11

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

Answers (2)

mkl
mkl

Reputation: 95898

There are two issues in your code:

  • you're hashing the wrong data and
  • you're signing incorrectly.

Hashing the wrong data

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.

Signing incorrectly

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

Lkbhai Lr
Lkbhai Lr

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

Related Questions