C# 使用iTextSharp时,数字签名无效

C# 使用iTextSharp时,数字签名无效,c#,pdf,itext,C#,Pdf,Itext,这是我的表格- 下面是代码- private void btnSign_Click(object sender, EventArgs e) { string sbase64 = richTextBox2.Text; byte[] bytes = System.Convert.FromBase64String(sbase64); PdfReader reader = new PdfReader("F:\\test1.pdf"); Fil

这是我的表格-

下面是代码-

private void btnSign_Click(object sender, EventArgs e)
{   
    string sbase64 = richTextBox2.Text;
    byte[] bytes = System.Convert.FromBase64String(sbase64);
    PdfReader reader = new PdfReader("F:\\test1.pdf"); 
    FileStream os = new FileStream("F:\\output.pdf", FileMode.Create);
    IExternalSignatureContainer external1 = new MyExternalSignatureContainer(bytes);
    MakeSignature.SignDeferred(reader, "Signature1", os, external1);
    reader.Close();
    os.Close(); 
}

private class MyExternalSignatureContainer : IExternalSignatureContainer
{
    protected byte[] sig;
    public MyExternalSignatureContainer(byte[] sig)
    {
        this.sig = sig;
    }
    public byte[] Sign(Stream s)
    {
        var certificate = new X509Certificate2();
        certificate.Import("F:\\180332.cer");
        Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
        Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(certificate.RawData) };
        string hashAlgorithm = "SHA-256";
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
        byte[] hash = DigestAlgorithms.Digest(s, hashAlgorithm);
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
        string messageDigest = sha256_hash(sh);
        byte[] signatureAsByte = sig;
        sgn.SetExternalDigest(signatureAsByte, null, "RSA");
        return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
    }
    public void ModifySigningDictionary(PdfDictionary signDic) { }
}

private void btnPreparePDF_Click(object sender, EventArgs e)
{
    //Prepare file .cer
    var certificate = new X509Certificate2();
    certificate.Import("F:\\base64.cer");
    Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
    Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(certificate.RawData) };

    //Create area sign
    PdfReader reader = new PdfReader("F:\\test.pdf");
    FileStream os = new FileStream("F:\\test1.pdf", FileMode.Create);
    PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
    PdfSignatureAppearance appearance = stamper.SignatureAppearance;
    appearance.Certificate = chain[0];
    appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 250, 400), 1, "Signature1");
    IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    MakeSignature.SignExternalContainer(appearance, external, 8192);
    string hashAlgorithm = "SHA-256";
    PdfPKCS7 sgn = new PdfPKCS7(null, chain , hashAlgorithm, false);
    PdfSignatureAppearance appearance2 = stamper.SignatureAppearance;
    Stream stream = appearance2.GetRangeStream();
    byte[] hash = DigestAlgorithms.Digest(stream, hashAlgorithm);
    byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

    //Export chain to sign
    string abc = sha256_hash(sh);
    richtextbox1.Text = abc;
}

private static String sha256_hash(Byte[] value)
{
    using (SHA256 hash = SHA256.Create())
    {
        return String.Concat(hash.ComputeHash(value).Select(item => item.ToString("x2"))).ToUpper();
    }
}
首先,我准备一个PDF文件(test.PDF),在PDF文件上创建一个签名区域。然后输出文件test1.pdf和字符串SHA-256

接下来,我使用上传到服务器的SHA-1字符串进行签名。服务器返回base64字符串

我复制base64并将其粘贴到richtextbox2中,以签名并导出“output.pdf”文件

然后我在FoxitReader(10.0版)上检查文件“output.pdf”,签名是有效的

但当我在Adobe Reader或Adobe Acrobat上检查该文件时,签名无效

即使在Foxit Reader(最新版本10.1)上,签名也是无效的。发生了什么事

我的英语不是很好。对不起

更新-错误

当我在FoxitReader10.0上检查时,这是文件“output.pdf”(它是有效的)-

这是我检查AdobeAcrobat的时候(它是无效的)-

更新。。。有什么错误吗???

示例PDF中至少有两个问题,它们不是由iText相关代码引起的,而是由签名服务或解决方式引起的

问题 使用的混合摘要算法 您可以使用SHA-256计算签名PDF字节范围的摘要。因此,存储在签名信息中的散列算法是SHA-256。作为签名/加密算法,它仅指RSA。但在签名字节中,显式使用SHA-1

这是一个bug,因为散列算法应用于对消息数据和签名属性进行散列

这是
SignerInfo
对象的转储

。序列{
…整数1
…[…SID…]
…序列{
……对象标识符sha-256(2 16 840 1 101 3 4 2 1)
…….(NIST算法)
无效的
. . . . . . }
. . . . . [0] {
……顺序{
……对象标识符contentType(1 2 840 113549 1 9 3)
………(PKCS#9)
……一套{
……对象标识符数据(12840 113549 1 7 1)
(PKCS#7)
. . . . . . . . }
. . . . . . . }
……顺序{
……对象标识符messageDigest(1 2 840 113549 1 9 4)
………(PKCS#9)
……一套{
……八进制字符串
……2F AA 90 5F A3 3B 2E 74/
…3F 78 7A C1 85 F8 EF B7?xz。。。。。
…20 AE 73 2B F4 2F B2 80.s+/。。
……87 3C 24 50 F9 A8 3F 8B
. . . . . . . . }
. . . . . . . }
. . . . . . }
…序列{
……对象标识符RSA加密(12840 113549 1)
…….(PKCS#1)
无效的
. . . . . . }
…八位组字符串
……签名字节……]
. . . . . }
. . . . }
您可以轻松识别SHA-256和RSA OID;这些是由iText类根据您的输入添加的
PdfPKCS7

这是签名字节中解密的
DigestInfo
对象的转储:

序列(2个元素){
.序列(2个元素){
..对象标识符1.3.14.3.2.26 sha1(OIW)
无效的
. . }
.八位字节字符串(20字节)AD002EA501871117250DE3BF42D51B4FD2B1C5A0
. }
您可以清楚地看到SHA-1 OID;此外,20字节散列值是可能的SHA-1散列,但不是SHA-256散列

显然,签名服务根据您的请求创建了一个带有RSA签名的SHA1

有符号哈希值不正确 正如您在上面解密的
DigestInfo
对象转储中所看到的,签名的哈希值为

AD002EA501871117250DE3BF42D51B4FD2B1C5A0
但是,即使上面提到的一个假定混合算法用法是正确的,这个值是不正确的,您的
SignerInfo
中已签名属性的正确SHA-1哈希值也是正确的

851AD7BF24D159C02A705FD4ABB20EBA9DE2AD2B
考虑到您只向签名服务发送了SHA-256摘要,这种不匹配实际上并不令人惊讶。可能该服务只使用了32字节SHA-256摘要值的大约20字节部分,或者它使用您的输入作为普通数据对其进行签名和散列

后续行动 在一个回答中,莱布明问道

1/我可以将摘要算法更改为SHA1并发送到签名服务器吗

2/如果签名服务器将我的输入用作普通数据,我是否可以向其发送原始数据(无哈希)

3/为什么当我用Foxit reader(10.0)检查输出pdf时,它说有效,但在最新版本的Foxit或Adobe中,它说无效

改变算法 1/我可以将摘要算法更改为SHA1并发送到签名服务器吗

当然,您可以使用SHA-1而不是SHA-2变体,只需在代码中切换算法名称字符串即可

但你不应该。PDF签名环境中的SHA-1已被证明是不合格的,参见,因此国际上认为不推荐使用。因此,除非您只签署不需要任何签名的文档,否则切换到SHA-1是个坏主意。头脑正常的人不会相信用SHA-1哈希签名的文档,除非它非常不重要

相反,您应该研究如何从签名服务请求基于SHA-2哈希的签名,或者切换到其他服务

发送原始数据 2/如果签名服务器将我的输入用作普通数据,我是否可以向其发送原始数据(无哈希)

如果它使用您的输入作为尚未散列的数据进行签名,那么这很可能是一个很好的调查选择。在
btnPreparePDF_中单击
只需不消化
字节[]sh
,而是按原样使用其内容

为什么Foxit10说它是有效的? 3/为什么当我用Foxit reader(10.0)检查输出pdf时,它说有效,但在最新版本的Foxit或Adobe中,它说无效

正如我在上文“问题”一节中所示,签名已被破坏。因此,如果某个验证器说签名是有效的,那么您就是其中存在严重安全缺陷的见证人

显然Foxit已经识别出了这个bug