Itext pdf deferred signing results in pdf with inv

2019-08-19 01:53发布

问题:

As part of my research for client/server pdf signing , i have tested itext pdf deferred signing example. Unfortunately my resulting pdf ie output of merged empty signature pdf and hash value shows invalid signature.

My code snippets follows

 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;
    }


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();
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
        byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
        Calendar cal = Calendar.getInstance();
        cal1=cal;
        System.out.println(cal);
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);

        return(sh);
    }

public byte[] signed_hash(byte[] hash, PrivateKey pk, Certificate[] chain)throws GeneralSecurityException{
        PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "SunPKCS11-eToken");
        byte[] extSignature = signature.sign(hash);
        //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);
        sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
    return sgn.getEncodedPKCS7(hash, cal1, 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);
    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);

}

something went wrong . i could not figure out. searched a lot for tutorials of the same.

I am using pkcss11 usb token for signing

回答1:

Your architecture is wrong insofar as you use the PdfSignatureAppearance appearance after running MakeSignature.signExternalContainer for it. Both signExternalContainer and the signDetached overloads in MakeSignature close the underlying PdfStamper, PdfSignatureAppearance, and PdfReader instances.

Thus, when you do the following in your method emptySignature_hash

    MakeSignature.signExternalContainer(appearance, external, 8192);
    InputStream inp = appearance.getRangeStream();   

your inp may not necessarily contain anything sensible.

Instead you should access the byte ranges to sign in your external object, it retrieves it as parameter of its sign method. Simple refactor your hash calculation into that method and store the calculated hash in a member of that container to retrieve it in emptySignature_hash.

As you have not shared an example result of your signing code, I cannot try to determine whether there also are other issues in your signing.



回答2:

it was my mistake. i modified the emptySignature_hash method and signed_hash as follows and now it is working fine.

emptySignature_hash method return only digest not authenticatedbytes

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");
       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);

        }

i think the problem was previously i was returning getAuthenticatedAttributeBytes of hash in emptysignature_hash method.

And in signed_hash() method i was passing authenticatedattributebytes to sgn.getEncodedPKCS7.