当前位置: 首页 > 知识库问答 >
问题:

C#PKCS7智能卡数字签名-文档签名后已被更改或损坏

焦信鸥
2023-03-14

我尝试使用智能卡(USB令牌)对pdf文件进行签名,但在Adobe中打开签名的pdf文件时,遇到“Document has been altered or corrupted since it was signed”错误。这个错误不是描述性的,我也不知道该去哪里看,因为代码对我来说似乎很好,但显然不是…

我使用的代码是:

var signer = smartCardManager.getSigner("myTokenPassword");
var toBeSignedHash = GetHashOfPdf(File.ReadAllBytes(@"xxx\pdf.pdf"), cert.asX509Certificate2().RawData, "dsa", null, false);
var signature = signer.sign(toBeSignedHash);
var signedPdf = EmbedSignature(cert.getBytes(), signature);
File.WriteAllBytes(@"xxx\signedpdf.pdf", signedPdf);

public byte[] GetHashOfPdf(byte[] unsignedFile, byte[] userCertificate, string signatureFieldName, List<float> location, bool append)
{
    byte[] result = null;

    var chain = new List<Org.BouncyCastle.X509.X509Certificate>
    {
        Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(userCertificate))
    };
    Org.BouncyCastle.X509.X509Certificate certificate = chain.ElementAt(0);
    using (PdfReader reader = new PdfReader(unsignedFile))
    {
        using (var os = new MemoryStream())
        {
            PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0', null, append);
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;
            appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(0,0,0,0), 1, signatureFieldName);
            appearance.Certificate = certificate;
            IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            MakeSignature.SignExternalContainer(appearance, external, 8192);
            Stream data = appearance.GetRangeStream();
            byte[] hash = DigestAlgorithms.Digest(data, "SHA256");
            var signatureContainer = new PdfPKCS7(null, chain, "SHA256", false);
            byte[] signatureHash = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
            result = DigestAlgorithms.Digest(new MemoryStream(signatureHash), "SHA256");
            this.hash = hash;
            this.os = os.ToArray();
            File.WriteAllBytes(@"xxx\temp.pdf", this.os);
        }
    }

    return result;
}

public byte[] EmbedSignature(byte[] publicCert, byte[] sign)
{
    var chain = new List<Org.BouncyCastle.X509.X509Certificate>
    {
        Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(new X509Certificate2(publicCert))
    };
    var signatureContainer = new PdfPKCS7(null, chain, "SHA256", false);
    using (var reader = new PdfReader(this.os))
    {
        using (var os2 = new MemoryStream())
        {
            signatureContainer.SetExternalDigest(sign, null, "RSA");
            byte[] encodedSignature = signatureContainer.GetEncodedPKCS7(this.hash, null, null, null, CryptoStandard.CMS);
            IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSignature);
            MakeSignature.SignDeferred(reader, "dsa", os2, external);
            return os2.ToArray();
        }
    }
}

我尝试签名的pdf文件是这个。

添加签名字段后创建的临时pdf文件如下。

签名的Base64格式为

共有1个答案

陈德泽
2023-03-14

我在这里使用字节数组的十六进制编码。您的base64编码哈希

klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

十六进制编码等于

92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6

您的代码对签名的属性哈希两次。只是不要对gethashofpdf中的signaturecontainer.getauthenticatedattributeBytes(hash,null,null,cryptostandard.cms)返回的字节进行哈希,而是使用经过身份验证的属性字节本身作为返回值。

>

  • 实际上,签名属性的哈希值是

    92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6
    

    但是签名的RSA加密digestinfo对象中的哈希是

    1DC7CAA50D88243327A9D928D5FB4F1A61CBEFF9E947D393DDA705BD61B67F25
    

    它是前面提到的带符号属性的哈希的哈希。

    var signature = signer.sign(toBeSignedHash);
    

    最简单的解决方法是替换

    byte[] signatureHash = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
    result = DigestAlgorithms.Digest(new MemoryStream(signatureHash), "SHA256");
    

    result = signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
    

    gethashofpdf中只有signer.sign执行哈希。

    那么,您的问题并不是定制的iText签名过程导致错误或至少不需要的配置文件的第一个问题。

    在分析这些问题的过程中,第一步通常是提取嵌入式签名容器并在ASN.1查看器中检查它。

    在您的PDF中,检查的主要结果是签名本身看起来还可以,并且签名的属性不包含任何变量数据。

    接下来的一个步骤是实际检查所涉及的散列。检查文档哈希很简单,只需计算带符号的字节范围哈希,并与messagedigest带符号属性的值进行比较。ExtractHash测试testsotnsignedpdf(Java格式)。

    您的PDF结果为OK。

    接下来的步骤是更彻底地检查签名容器。在这种情况下,我曾经开始写一些支票,但没有走得很远。SignatureAnalyzer类。为了测试签名属性的散列,我使用了您使用的签名算法,即旧的RSASSA-PKCS1-V1_5:与许多其他签名算法不同,这个算法允许提取签名散列。

    在这里,PDF的结果不是ok,签名属性的哈希与签名哈希不同。

    这里有两个常见的不匹配原因,

    >

  • 签名属性的签名编码是错误的(它必须是常规的DER编码,而不是某些任意的BER编码,特别是不是带有存储在签名中的值的隐式标记的编码---甚至更大的播放器有时也会错误地执行此操作,例如Docusign,参见DSS-1343)

    或者哈希在签名过程中以某种方式被转换(例如,哈希被base64编码或再次哈希)。

    结果是,这里的情况是后者,散列被再次散列。

  •  类似资料:
    • 对于一个关于签名数据被哈希两次的C#问题,我看到了一个类似的答复,但是我不知道为什么我的签名数据会出现在这里。 C#PKCS7 Smartchard数字签名损坏

    • 我正在使用PDFbox-1.8.8在PDF文件上做签名功能。 当我签署一份文件的时候 下面是我的代码:

    • 我试图对pdf文件进行签名,但在Adobe中打开签名的pdf文件时,遇到“文档自签名后已被更改或损坏”错误。 这个错误不是那么描述性的,我不确定应该在哪里查看,因为代码对我来说似乎很好,但显然不是。。 我使用的代码是: 签名的哈希的Base64格式为(tmp文件sha_前缀): 签名(AMA)的Base64格式为: 有人能帮忙吗?

    • 我正在按代码创建一个签名PDF,但由于某种原因,我收到此错误: 至少一个签名无效-文档签名后已被更改或损坏 我将DSS属性与VRI、Certs和CRL一起使用。重要的一点是,我使用的版本与我在讨论此过程的其他文章中看到的版本相同。所以我有第一部分的签名(内容<>和byterange[])和第二部分与他的孩子的DSS。我注意到,如果删除第二个生成的部分,Adobe Acrobat将给出: 已签名且所

    • 我已经研究了所有类似的问题,但找不到一个应用itextsharp延迟签名的案例。 基本上,我的应用程序使用签名对pdf文档进行签名,该签名是由远程web服务创建的。 我的应用程序向这个web服务发送原始文档的哈希(添加空签名字段后可签名字节的哈希),并接收一个Base64编码的签名文件。 我将此签名嵌入到先前生成的临时pdf文件中,该文件具有空签名字段。 最后,我的签名未被验证,因为Adobe R