iText使用智能卡的外部签名签署PDF [英] iText signing PDF using external signature with smart card

查看:487
本文介绍了iText使用智能卡的外部签名签署PDF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在用iTextSharp 5.5.7玩弄一段时间而无法找到正确的方法来为智能卡制作PDF的有效数字签名 - Adob​​e Reader总是说它的签名和未知并且无法解码签名的DER数据。

I've been toying with iTextSharp 5.5.7 for a while and can't find the right way to make a valid digital signature for PDF from Smart Card - Adobe Reader always says its signed by and unknown and can't decode signatures' DER data.

我看过 MakeSignature.cs 代码供参考,具体作用如何:

I've looked at MakeSignature.cs code for reference and what is does:

        Stream data = signatureAppearance.GetRangeStream(); 
        // gets the first hash
        byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        // gets the second hash or is it not a hash at all ?
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, crlBytes, sigtype);

然后,根据 IExternalSignature.cs


@ param消息您想要进行哈希和签名的消息

"@param message the message you want to be hashed and signed"



        // looks like externalSignature.Sign() should make another hash out of "sh"
        // and use this hash to compute a signature
        byte[] extSignature = externalSignature.Sign(sh); 

所以我理解签署的程序如下:

so I understood the procedure of signing as the following:


  1. 源PDF已加载

  2. 创建空签名字段的新PDF

  3. 字节范围字段被哈希(默认情况下为sha-1生成20个字节,尝试使用sha-256的32个字节)

  4. 哈希+其他一些属性再次进行哈希处理(字节数
    各不相同,为什么?毕竟可能不是哈希?)

  5. 第二个哈希在外部签名对象内再次进行哈希处理

  6. 第三个哈希值最终发送到智能卡计算
    签名

  7. 签名插入新PDF

  1. source PDF is loaded
  2. new PDF with empty signature field is created
  3. Byte range of that field is hashed (by default produces 20 bytes for sha-1, tried 32 bytes with sha-256 too)
  4. that Hash + some other properties are hashed again (number of bytes varies, why? might not be a hash after all?)
  5. that second hash is hashed again inside of external signature object
  6. that third hash is finally sent to a Smart Card to compute a signature
  7. Signature is inserted into new PDF

当我用Adobe Reader签名PDF时,在步骤6,第三个散列长度为32个字节。
从智能卡的角度来看,我使用Acrobat和iText执行相同的步骤,但是使用iText签名无效,可能出错?

When i sign PDF with Adobe Reader, at step 6, the third hash is 32 bytes long. From Smart Card's perspective i do the same steps with both Acrobat and iText, but with iText the signature is invalid, what could be wrong ?

我使用的代码:

public void StartTest(){
        X509Certificate2 cert = new X509Certificate2();
        cert.Import("cert.cer"); // certificate obtained from smart card

        X509CertificateParser certParse = new Org.BouncyCastle.X509.X509CertificateParser();

        Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { certParse.ReadCertificate(cert.RawData) };

        // Reader and stamper
        PdfReader pdfReader = new PdfReader("original.pdf");
        Stream signedPdf = new FileStream("signed.pdf", FileMode.Create);
        PdfStamper stamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0', null, false);

        // Appearance
        PdfSignatureAppearance appearance = stamper.SignatureAppearance;
        appearance.SignatureCreator = "Me";
        appearance.Reason = "Testing iText";
        appearance.Location = "On my Laptop";
        appearance.SignatureGraphic = Image.GetInstance("img.png"); // visual image
        appearance.SetVisibleSignature(new Rectangle(50, 50, 250, 100), pdfReader.NumberOfPages, "Signature");
        appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;

        // Timestamp
        TSAClientBouncyCastle tsc = new TSAClientBouncyCastle("http://ts.cartaodecidadao.pt/tsa/server", "", "");

        // Digital signature
        IExternalSignature externalSignature = new MyExternalSignature2("SHA-1");
        MyMakeSignature.SignDetached(appearance, externalSignature, chain, null, null, tsc, 0, CryptoStandard.CADES);

        stamper.Close();
}

外部签名实现(类MyExternalSignature2):

external signature implementation (class MyExternalSignature2):

    class MyExternalSignature2 : IExternalSignature
{
    private String hashAlgorithm;
    private String encryptionAlgorithm;

    public MyExternalSignature2(String hashAlgorithm)
    {
        this.encryptionAlgorithm = "RSA";
        this.hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigests(hashAlgorithm));
    }

    public virtual byte[] Sign(byte[] message) {

        byte[] hash = null;
        using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
        {
            hash = sha1.ComputeHash(message);
        }

        byte[] sig = MySC.GetSignature(hash);

        return sig;
    }

    public virtual String GetHashAlgorithm() {
        return hashAlgorithm;
    }

    public virtual String GetEncryptionAlgorithm() {
        return encryptionAlgorithm;
    }
}


推荐答案

OP提供了一些样本签名文档,并对它们进行分析,很明显我的初步答案是基于一个误解。

OP提供了三个由他的代码签名的PDF,起初是这两个:

The OP supplied three PDFs signed by his code, at first these two:

  • ex_signed.pdf (signed with sha-1 and CryptoStandard.CADES)
  • ex_signed_2.pdf (signed with sha-1 and CryptoStandard.CADES)

用SHA1检查iText CAdES签名的特殊性成为上诉者:对于 CryptoStandard.CADES 即使对于SHA1,iText也使用 SigningCertificateV2 属性,但规格建议使用 SigningCe取而代之的是rtificate 属性。为了防止这种特性干扰,OP提供了第三个文件

Inspecting them a peculiarity of iText CAdES signing with SHA1 became appearant: for CryptoStandard.CADES iText uses the SigningCertificateV2 attribute even for SHA1 but specifications recommend using the SigningCertificate attribute there instead. To prevent this peculiarity from interfering, the OP supplied the third file

  • ex_signed_3.pdf (signed with sha-256 and CryptoStandard.CMS)

但事实证明,这个怪癖不是OP观察的原因,Adobe Reader仍然报告加密库错误。

It turned out, though, that this quirk was not the cause of the OP's observations, Adobe Reader still reports cryptography library errors.

因此,回到分析。

由于签名算法是SHA1withRSA / 2048和SHA256withRSA / 2048,因此可以使用相应证书中的公钥简单地解密内部签名值。

As the signature algorithms were SHA1withRSA/2048 and SHA256withRSA/2048, one can simply decrypt the inner signature value using the public key from the respective certificate.

ex_signed.pdf成功但ex_signed_2.pdf或ex_signed_3.pdf成功。

This succeeded for ex_signed.pdf but not for ex_signed_2.pdf or ex_signed_3.pdf.

OP同时在评论中指出:

The OP meanwhile indicated in a comment:


这些文件之间的差异是用于签名的证书,在第一个我使用证书进行身份验证时,是(我认为是可以接受但我有另一个专门用于数字签名的证书,我在第二和第三档使用。

the difference between those files are certificates used for signatures, in the first i used a certificate for authentication, which is (i think is) acceptable but i have another certificate specifically for digital signatures, which i used in 2nd and 3rd file.

所以我尝试过使用第一个文件中的证书解码第二个和第三个文件中的签名值,实际上,这是有效的!因此,第二个和第三个文件声称由与该替代证书关联的私钥签名,但实际上是使用与前一个证书关联的私钥进行签名。

So I tried to decode the signature value in the second and third file using the certificate from the first file, and indeed, that worked! So the second and third file claim to be signed by the private key associated with that alternative certificate but actually are signed using the private key associated with the former certificate.

因此: 问题1:文件2和3中的签名使用错误的私钥/智能卡上的错误应用程序签名。

进一步分析问题,我查看了使用身份验证证书成功解码的签名值:

To further analyze the issue, I looked at the signature values successfully decoded with the authentication certificate:


  • ex_signed.pdf:

  • ex_signed.pdf:

2a8945abe450b2c1cd232249b8f811d352ad0d29


  • ex_signed_2.pdf

  • ex_signed_2.pdf

    cc24acc848002df63733941e34437f8aef1c746c
    


  • ex_signed_3.pdf

  • ex_signed_3.pdf

    45f8e451f8b9f39f0c1f59eea8b6308fba22176ac62ebd14bbf07e5407aed7e8
    


  • 因此SHA1有20个字节,SHA-256有32个字节。这正是哈希值的大小,因此很可能这些只是裸哈希值。

    So for SHA1 there are 20 bytes and for SHA-256 there are 32 bytes. This is exactly the size of the hash values, so most likely these simply are the naked hash values.

    这是错误的,但XXXwithRSA签名应包含加密结构,其中包含散列算法的OID和散列,采用ASN.1表示法:

    This is wrong, though, XXXwithRSA signatures are expected to contain an encrypted structure which contains an OID of the hashing algorithm and the hash, in ASN.1 notation:

    DigestInfo ::= SEQUENCE {
      digestAlgorithm DigestAlgorithmIdentifier, 
      digest Digest
      }
    
    DigestAlgorithmIdentifier ::= AlgorithmIdentifier
    
    Digest ::= OCTET STRING
    

    背景cf. RFC 3447

    这解释了OP的观察结果:

    This explains the OP's observation:


    第一个错误被描述为BER解码错误,

    the error in the first is described as "BER decoding error",

    验证者试图将裸散列解释为使用明显失败的BER编码的ASN.1序列。

    The verifier tries to interpret the naked hash as an ASN.1 sequence encoded using the BER which obviously fails.

    因此:问题2:智能卡加密裸哈希值但必须加密 DigestInfo 封装该哈希的对象。

    Thus: Problem 2: The smart card encrypts the naked hash value but has to encrypt a DigestInfo object encapsulating that hash.

    如果你的 MySC.GetSignature 在签署数据时调用哈希值(并且预计之前数据不会被哈希),您应该替换

    If your MySC.GetSignature call hashes while signing the data (and does not expect data to be already hashed before), you should replace

    public virtual byte[] Sign(byte[] message)
    {
        byte[] hash = null;
        using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
        {
            hash = sha1.ComputeHash(message);
        }
    
        byte[] sig = MySC.GetSignature(hash);
    
        return sig;
    }
    

    类似

    public virtual byte[] Sign(byte[] message)
    {
        byte[] sig = MySC.GetSignature(message);
    
        return sig;
    }
    

    这篇关于iText使用智能卡的外部签名签署PDF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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