【问题标题】:Document has been altered or corruped since the Signature was applied itext 5.5.11自应用签名以来,文档已被更改或损坏 itext 5.5.11
【发布时间】:2021-06-20 12:32:11
【问题描述】:

基本上我得到错误 Document has been changed or corrupted since the Signature is applied,我按照 itext 网站的示例并适应了我的情况。

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

我的想法用完了,希望能得到一些帮助 这是我的代码的 sn-p:

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

这里是文件: original one Signed one

编辑: 忘记添加我使用的容器:

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

【问题讨论】:

  • A 你散列了错误的数据;你散列整个准备好的pdf,而你必须散列准备好的pdf,除了要注入的签名容器的占位符。 B您确定需要延期签名吗?
  • A。感谢您的快速回复,我对这个话题有点新手。我虽然签署了错误的数据你有任何关于如何产生正确哈希的例子吗? B - 是的

标签: java pdf itext digital-signature


【解决方案1】:

试试这个

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

【讨论】:

  • 您好,我之前尝试过这种方法,但仍然遇到同样的错误
  • @JoaoBenevides 乍一看,代码看起来不错。您可以分享使用此答案中的方法签名的示例文件以进行分析吗?
  • @JoaoBenevides 啊,我可以在您的原始签名中看到错误。
  • @mkl 当然,我对 signed_hash 进行了一些更改以调用 WS 进行签名,因为我不使用/访问私钥 [drive.google.com/file/d/1RG9fdA4jY66bsWi7-ZIdTTDXSMUdibSq/…(签名文档)
  • @JoaoBenevides 比较我的答案,在那里提到的两个问题中,您显然针对此文件已修复问题 #1,因此您只需修复问题 #2。
【解决方案2】:

您的代码中有两个问题:

  • 您正在散列错误的数据,并且
  • 您的签名不正确。

散列错误数据

您正在像这样散列整个准备好的 PDF:

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

这是不正确的。

签名的 PDF 本质上具有这种结构(请阅读 here 了解更多详细信息):

(顺便说一下,草图不是 100% 正确的,因为签名值周围的尖括号分隔符“”也不能被散列。)

您在temp 准备的 PDF 具有相同的结构,只是“签名值”还不是实际的签名值,而是 8192 个十六进制编码零字节的占位符(8192,因为这是您在 @987654329 中提供的数字@)。

正如您在草图中看到的那样,签名值(或在您的情况下为占位符)不得为签名进行散列。

您可以使用@LkbhaiLr 在his answer 中提出的MakeSignature.signExternalContainer 之后的PdfSignatureAppearance.getRangeStream 返回的数据计算哈希。

或者,您可以使用(而不是ExternalBlankSignatureContainer)自定义ExternalSignatureContainer 实现,它在其sign 方法中简单地对其InputStream 参数进行散列并提供该散列。

我更喜欢后一种方法,但前一种方法也被广泛使用。

签名错误

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

byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign

即您将属性结构按原样转发给您的签名服务,并期望它为其创建适当的签名值。

不过,检查您的 signed example file,很明显该签名服务没有达到您的期望:它没有使用 SHA256withRSA 签名,而只是使用 加密 RSA 和 PKCS#1 填充。因此,它希望您为您完成一半的签名,即使用 SHA256 散列并构建 DigestInfo 结构。

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

当您在其他地方计算哈希时,这对您来说应该不是问题。对于将哈希包装在 DigestInfo RFC 8017 section 9.2 note 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

【讨论】:

  • 非常感谢!!!设法用你所说的签名:)
  • @JoaoBenevides 太棒了!
猜你喜欢
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2021-05-11
  • 2021-02-22
  • 1970-01-01
  • 2018-12-04
  • 2021-11-06
相关资源
最近更新 更多