自签名于5.5.11开始应用以来,文档已被更改或更改. [英] Document has been altered or corruped since the Signature was applied itext 5.5.11

查看:126
本文介绍了自签名于5.5.11开始应用以来,文档已被更改或更改.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上,我得到的错误是自从应用签名以来,文档已被更改或更改,我遵循了itext网站的示例并适应了我的情况.

  • 准备要签名的文档,添加附加模式,因为它可以已经签名的文件
  • 调用了Web服务对哈希进行签名
  • 将签名的哈希添加到准备好的文档中

我用尽了所有想法,希望能得到一些帮助这是我的代码段:

 公共静态最终字符串src ="DynamicClient \ sample.pdf";公共静态最终String temp ="DynamicClient \ sampleTEMP.pdf";公共静态最终字符串dest ="DynamicClient \ sampleFINAL.pdf";public static void emptySignatureSVC(String src,String dest,String fieldname,Certificate []链)引发IOException,DocumentException,GeneralSecurityException {PdfReader reader =新的PdfReader(src);reader.setAppendable(true);FileOutputStream os = new FileOutputStream(dest);PdfStamper压模= PdfStamper.createSignature(reader,os,'\ 0',null,true);PdfSignatureAppearance外观= stamper.getSignatureAppearance();Appearance.setVisibleSignature(new Rectangle(0,0,0,0),1,字段名称);Appearance.setCertificate(chain [0]);ExternalSignatureContainer external =新的ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE,PdfName.ETSI_CADES_DETACHED);MakeSignature.signExternalContainer(appearance,external,8192);reader.close();}公共静态无效createSignatureSVC(字符串src,字符串目标,字符串字段名,字节[]符号,证书[]链)引发IOException,DocumentException,GeneralSecurityException {PdfReader reader =新的PdfReader(src);FileOutputStream os = new FileOutputStream(dest);ExternalSignatureContainer external =新的MyExternalSignatureContainer(sign,chain);MakeSignature.signDeferred(阅读器,字段名,操作系统,外部);}公共静态void main(String [] args)引发异常{Certificate []链= signWS.getChain();//要获得链的外部WSemptySignatureSVC(src,temp,"signature",chain);InputStream是= new FileInputStream(new File(temp));PdfPKCS7 sgn =新的PdfPKCS7(null,链,"SHA256",null,摘要,false);byte [] hash = DigestAlgorithms.digest(是,digest.getMessageDigest("SHA256"));byte [] sh = sgn.getAuthenticatedAttributeBytes(hash,null,null,CryptoStandard.CADES);byte [] extSignature = signWS(sh);//要签名的外部WSsgn.setExternalDigest(extSignature,null,"RSA");createSignatureSVC(temp,dest,"signature",sgn.getEncodedPKCS7(hash,null,null,null,CryptoStandard.CADES),链);} 

,这是文件:

(顺便说一句,草图不是100%正确,因为签名值周围的尖括号定界符'<'和'>'也不能被散列.)>

您在 temp 下准备的PDF具有相同的结构,只是签名值"还不是一个实际的签名值,而是一个占位符,为8192个十六进制编码的零字节(8192,因为这是您在 MakeSignature.signExternalContainer 中输入的数字).

但是,如您在草图中所见,签名值(或者在您的情况下为占位符)一定不能散列用于签名.

您可以使用@LkbhaiLr在MakeSignature.signExternalContainer 之后由 PdfSignatureAppearance.getRangeStream 返回的数据来计算哈希.stackoverflow.com/a/66776866/1729265>他的答案.

或者,您可以使用(而不是 ExternalBlankSignatureContainer )自定义的 ExternalSignatureContainer 实现,该实现在其 sign 方法中只是对 InputStream进行哈希处理参数并提供该哈希值.

我更喜欢后一种方法,但是前一种方法也得到了广泛使用.

签名不正确

您可以像这样检索裸签名值:

  byte [] sh = sgn.getAuthenticatedAttributeBytes(hash,null,null,CryptoStandard.CADES);byte [] extSignature = signWS(sh);//要签名的外部WS 

即您可以将属性结构原样转发给签名服务,并期望它为它创建一个合适的签名值.

检查您的签名的示例文件,但是很明显,签名服务没有达到您的期望:与使用SHA256withRSA进行签名不同,它仅通过RSA和PKCS#1填充进行加密.因此,它希望您为您完成一半的签名,即使用SHA256进行哈希处理并构建 DigestInfo 结构.

因此,在调用 signWS 之前,您必须对 sgn.getAuthenticatedAttributeBytes 返回的属性结构进行哈希处理,然后将所得的哈希值包装在 DigestInfo 对象.

在其他地方计算哈希时,这对您来说不是问题.对于将哈希值包装在 DigestInfo RFC 8017 9.2节中,请注意1 提供了一个捷径:

对于附录B.1中提到的九种哈希函数, DigestInfo 值的DER编码T等于以下值:

  ...SHA-256:(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||H.... 

即您只需用字节序列 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 作为哈希前缀即可.

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.

  • Prepare the document to be signed, add append mode because it can be a already signed document
  • Called the webservice to sign the hash
  • added signed hash to prepared document

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

解决方案

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.

这篇关于自签名于5.5.11开始应用以来,文档已被更改或更改.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆