C#PKCS7智能卡数字签名-文档自签名以来已被更改或损坏 [英] C# PKCS7 Smartcard Digital Signature - Document has been altered or corrupted since it was signed

查看:111
本文介绍了C#PKCS7智能卡数字签名-文档自签名以来已被更改或损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用智能卡(USB令牌)对pdf文件签名,但是在Adobe中打开签名的pdf文件时遇到"Document has been altered or corrupted since it was signed"错误.该错误不是那么描述性的,我不确定在哪里查看,因为该代码对我来说似乎很好,但显然不是..

I try to sign a pdf file using my smartcard (USB token) but encounter "Document has been altered or corrupted since it was signed" error when I open the signed pdf file in Adobe. The error is not so descriptive and I'm not sure where to look at because the code seems good to me but apparently it's not..

我使用的代码是:

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文件是.

The pdf file that I try to sign is this.

添加签名字段后创建的临时pdf文件为.

Temp pdf file that is created after adding signature fields is this.

签名的pdf文件为.

签名的哈希的Base64格式为:klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

Base64 format of the hash that is signed is: klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

Base64签名的格式为:Uam/J6W0YX99rVP4M9mL9Lg9l6YzC2yiR4OtJ18AH1PtBVaNPteT3oPS7SUc+6ak2LfijgJ6j1RgdLamodDPKl/0E90kbBenry+/g1Ttd1bpO8lqTn1PWJU2TxeGHwyRyaFBOUga2AxpErIHrwxfuKCBcodB7wvAqRjso0jovnyP/4DluyOPm97QWh4na0S+rtUWOdqVmKGOuGJ3sBXuk019ewpvFcqWBX4Mvz7IKV56wcxQVQuJLCiyXsMXoazwyDCvdteaDz05K25IVwgEEjwLrppnc/7Ue9a9KVadFFzXWXfia7ndmUCgyd70r/Z+Oviu9MIAZL8GuTpkD7fJeA==

Base64 format of the signature is: Uam/J6W0YX99rVP4M9mL9Lg9l6YzC2yiR4OtJ18AH1PtBVaNPteT3oPS7SUc+6ak2LfijgJ6j1RgdLamodDPKl/0E90kbBenry+/g1Ttd1bpO8lqTn1PWJU2TxeGHwyRyaFBOUga2AxpErIHrwxfuKCBcodB7wvAqRjso0jovnyP/4DluyOPm97QWh4na0S+rtUWOdqVmKGOuGJ3sBXuk019ewpvFcqWBX4Mvz7IKV56wcxQVQuJLCiyXsMXoazwyDCvdteaDz05K25IVwgEEjwLrppnc/7Ue9a9KVadFFzXWXfia7ndmUCgyd70r/Z+Oviu9MIAZL8GuTpkD7fJeA==

推荐答案

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

klh6CGp7DUzayt62Eusiqjr1BFCcTZT4XdgnMBq7QeY=

十六进制编码中的

等于

in hex encoding is equal to

92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6

简而言之

您的代码对签名的属性进行了两次哈希处理.只需不对GetHashOfPdf中的signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS)返回的字节进行哈希处理,而是使用经过身份验证的属性字节本身作为返回值.

In short

Your code hashes the signed attributes twice. Simply don't hash the bytes returned by signatureContainer.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS) in GetHashOfPdf but instead use the authenticated attribute bytes themselves as return value.

分析示例PDF中的签名,事实证明

Analyzing the signature in your example PDF it turns out that

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

  • indeed the hash of the signed attributes is

92587A086A7B0D4CDACADEB612EB22AA3AF504509C4D94F85DD827301ABB41E6

  • ,但签名的RSA加密DigestInfo对象中的哈希为

  • but the hash in the RSA encrypted DigestInfo object of the signature is

    1DC7CAA50D88243327A9D928D5FB4F1A61CBEFF9E947D393DDA705BD61B67F25
    

  • 原来是前面提到的已签名属性的哈希值.

  • which turns out to be the hash of the before mentioned hash of the signed attributes.

    因此,您的

    var signature = signer.sign(toBeSignedHash);
    

    呼叫似乎再次哈希toBeSignedHash值.

    最简单的解决方法是替换

    The most simple fix would be to replace

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

    作者

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

    中的

    仅具有signer.sign进行哈希处理.

    在您询问的评论中

    您是如何找到所有这些的:)?

    how did you figure all this out :)?

    嗯,您的问题不是第一个使用自定义iText签名过程会导致错误或至少导致不必要的配置文件的问题.

    Well, yours is not the first question with a customized iText signing process resulting in errors or at least unwanted profiles.

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

    In the course of the analysis of those questions the first step usually is to extract the embedded signature container and inspect it in an ASN.1 viewer.

    对于您的PDF,检查的主要结果是这样的签名看起来还不错,并且您签名的属性中不包含任何可变数据.

    In case of your PDF the main result of that inspection was that the signature as such looked ok and that your signed attributes don't contain any variable data.

    如果其中包含一些可变数据(例如,签名时间属性),则可能会导致该问题的原因是您两次构建了签名属性,一次明确地在,一旦在EmbedSignature中隐式包含,对变量数据使用不同的值.但是如上所述,情况并非如此.

    If there had been some variable data (e.g. a signing time attribute) in them, a candidate for the cause of the issue would have been that you build the signed attributes twice, once explicitly in GetHashOfPdf, once implicitly in EmbedSignature, with different values for the variable data. But as mentioned above, this was not the case.

    这里的下一步是实际检查所涉及的哈希.检查文档哈希很简单,可以计算带符号的字节范围哈希,然后将其与MessageDigest签名属性的值进行比较.

    The next step here was to actually check the hashes involved. Checking the document hash is simple, one calculates the signed byte range hash and compares with the value of the MessageDigest signed attribute, cf. the ExtractHash test testSotnSignedpdf (in Java).

    您的PDF结果证明还不错.

    接下来的步骤是更彻底地检查签名容器.在这种情况下,我曾经开始写一些支票,但距离还很远. RSASSA-PKCS1-v1_5 :与许多其他签名算法相比,该算法可以轻松提取签名哈希.

    The following step was to inspect the signature container more thoroughly. In this context I once started to write some checks but did not get very far, cf. the SignatureAnalyzer class. I extended it a bit for the test of the hash of the signed attributes making use of the signature algorithm you used, the old RSASSA-PKCS1-v1_5: In contrast to many other signature algorithms, this one allows to trivially extract the signed hash.

    在这里,您的PDF结果不正确,签名属性的哈希与签名哈希不同.

    这里经常有两种不匹配的原因,

    There are two often seen causes for a mismatch here,

    • 两个已签名的属性都使用错误的编码进行了签名(它必须是常规DER编码,而不是某种任意BER编码,尤其是不是带有存储在签名中的隐式标签的编码-甚至更大的播放器有时也会犯错,例如Docusign,请参阅. DSS-1343 )

    或在签名过程中以某种方式对哈希进行了转换(例如,哈希经过base64编码或再次被哈希).

    or the hash was somehow transformed during signing (e.g. the hash is base64 encoded or hashed again).

    事实证明,后者就是这种情况,哈希又被哈希了.

    这篇关于C#PKCS7智能卡数字签名-文档自签名以来已被更改或损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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