iText使用智能卡外部签名签署PDF

时间:2015-10-08 15:11:20

标签: pdf itextsharp digital-signature smartcard itextpdf

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

我已查看MakeSignature.cs代码以供参考,以及做了什么:

        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消息您想要进行哈希并签名"

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

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

  1. 源PDF已加载
  2. 创建带有空签名字段的新PDF
  3. 该字段的字节范围经过哈希处理(默认情况下为sha-1生成20个字节,尝试使用sha-256生成32个字节)
  4. Hash +其他一些属性再次进行哈希处理(字节数) 不一样,为什么?毕竟可能不是哈希?)
  5. 第二个哈希在外部签名对象
  6. 内再次进行哈希处理
  7. 最后将第三个哈希发送到智能卡以计算a 签名
  8. 签名插入新PDF
  9. 当我用Adobe Reader签名PDF时,在步骤6,第三个散列长度为32个字节。 从智能卡的角度来看,我使用Acrobat和iText执行相同的步骤,但使用iText签名无效,可能出错?

    我使用的代码:

    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类):

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

1 个答案:

答案 0 :(得分:0)

OP提供了许多签名样本文件,并对其进行分析后,很明显我的初步答案是基于误解。

分析提供的签名PDF

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

检查iText CAdES与SHA1签名的特殊性变得更具吸引力:对于CryptoStandard.CADES iText甚至对SHA1使用SigningCertificateV2属性,但规范建议使用SigningCertificate属性。为了防止这种特性干扰,OP提供了第三个文件

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

因此,回到分析。

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

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

OP同时在评论中指出:

  

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

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

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

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

  • ex_signed.pdf:

    2a8945abe450b2c1cd232249b8f811d352ad0d29
    
  • ex_signed_2.pdf

    cc24acc848002df63733941e34437f8aef1c746c
    
  • ex_signed_3.pdf

    45f8e451f8b9f39f0c1f59eea8b6308fba22176ac62ebd14bbf07e5407aed7e8
    

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

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

DigestInfo ::= SEQUENCE {
  digestAlgorithm DigestAlgorithmIdentifier, 
  digest Digest
  }

DigestAlgorithmIdentifier ::= AlgorithmIdentifier

Digest ::= OCTET STRING

对于背景cf. RFC 3447

这解释了OP的观察结果:

  

第一个中的错误被描述为“BER解码错误”,

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

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

初步的,过时的答案

如果您在签署数据时MySC.GetSignature调用哈希值(并且之前不希望数据已经过哈希处理),则应替换

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