如何在C#中从签名的PDF获取签名值?

Sar*_*ara 2 c# pdf itextsharp digital-signature

如何从签名的PDF文件中获取签名值?我可以从签名中获取除其值以外的所有其他数据。有什么办法可以在C#中获得它吗?

PdfPKCS7 pk;
PdfReader reader = new PdfReader(PdfFilename);
AcroFields af = reader.AcroFields;

var names = af.GetSignatureNames();
foreach (string name in names)
{
    pk = af.VerifySignature(name);

    var CN_signer = iTextSharp.text.pdf.security.CertificateInfo.GetSubjectFields(pk.SigningCertificate).GetField("CN");
    var C_signer = iTextSharp.text.pdf.security.CertificateInfo.GetSubjectFields(pk.SigningCertificate).GetField("C");
    var CN_issuer = iTextSharp.text.pdf.security.CertificateInfo.GetIssuerFields(pk.SigningCertificate).GetField("CN");
    var OU_issuer = iTextSharp.text.pdf.security.CertificateInfo.GetIssuerFields(pk.SigningCertificate).GetField("OU");
    var O_issuer= iTextSharp.text.pdf.security.CertificateInfo.GetIssuerFields(pk.SigningCertificate).GetField("O");
    var C_issuer = iTextSharp.text.pdf.security.CertificateInfo.GetIssuerFields(pk.SigningCertificate).GetField("C");
    var nr_serial = pk.SigningCertificate.SerialNumber;
    var date = pk.SignDate.ToString();
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

mkl*_*mkl 5

OP阐明了签名Value旨在引用PKCS#7 / CMS签名容器。下面的示例方法可以做到这一点:

public void showSignatureValues(PdfReader reader)
{
    AcroFields fields = reader.AcroFields;
    foreach (String name in fields.GetSignatureNames())
    {
        Console.Write(" Signature {0}\n", name);

        PdfDictionary sigDict = fields.GetSignatureDictionary(name);
        PdfName subFilter = sigDict.GetAsName(PdfName.SUBFILTER);
        Console.Write("  SubFilter {0}\n", subFilter);

        PdfString contents = sigDict.GetAsString(PdfName.CONTENTS);
        if (contents != null)
        {
            byte[] contentBytes = contents.GetOriginalBytes();
            string contentBas64 = Convert.ToBase64String(contentBytes);
            // contentBytes contains the actual signature container as is,
            // contentBas64 contains it encoded using Base64 for better printability
            Console.Write("  Content {0}\n", contentBas64);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不过,请注意以下几点:您会发现contentBytes通常00在签名容器字节之后包含许多字节(在Base64表示中,它们显示为长字母字符串A)。这是因为在准备用于签名的PDF时,通常会对签名容器的大小进行大量的估计,并且会留出足够的空间来注入它。

根据规范,由于PKCS#7对象的长度不是完全可预测的,因此Contents的值应在末尾用零填充

使用ASN.1解析器,您可以确定实际签名容器字节序列的长度以及填充的起始位置。

从理论上讲,Contents的值应为DER编码的PKCS#7二进制数据对象;由于DER编码规则不允许使用不确定长度的方法,因此签名容器的大小应根据前几个字节来确定。不幸的是,有大量的PDF,其中包含仅BER编码的签名容器的外层和仅DER编码的某些内部对象。因此,可能需要完整的解析。


事后思考

在上面的答案中,我直言宣称该示例代码返回了PKCS#7 / CMS签名容器。实际上,仅在大多数情况下才是这样的签名容器,它取决于SubFilter签名字段值。

让我们看一下ISO 32000-1(PDF规范)和ETSI技术规范102778零件(PAdES)中定义的SubFilter值:

  • adbe.x509.rsa_sha1 ISO 32000-1-在这种情况下,内容实际上是DER编码的PKCS#1二进制数据对象。OP的图形中就是这种情况

    OP在这里将内容称为加密摘要,这只是事实的一部分,因为

    1. PKCS#1数据对象不是由裸摘要构成的,而是由包含摘要和摘要算法的OID的结构构成的,并且

    2. 根据签名算法的不同,此结构可能不会被加密(因为可以将其再次解密回摘要),但只能从中得出一个数字,不能解密回该结构,而只能根据所谓的文档摘要进行测试结构体。

    如今,这种格式已不再使用。

  • adbe.pkcs7.detached ISO 32000-1,ETSI TS 102778-2-内容是直接对字节范围进行签名的DER编码的PKCS#7二进制数据对象,即,字节范围摘要通常位于signed属性中MessageDigest

  • adbe.pkcs7.sha1 ISO 32000-1,ETSI TS 102778-2-内容是DER编码的PKCS#7二进制数据对象,间接对字节范围进行签名,即,将字节范围SHA1摘要作为数据放入容器中转弯正常签署。

  • ETSI.CAdES.detached ETSI TS 102778-3-内容是在CMS中指定的直接对字节范围进行签名的DER编码的SignedData对象,本质上,这是adbe.pkcs7.detached的经过特殊配置的变体。

  • ETSI.RFC3161 ETSI TS 102778-4-内容是一个TimeStampToken,如RFC 3161中所述,直接标记字节范围;这是与PKCS#7紧密相关的时间戳格式。(这是一种特殊情况,因为表单字段类型不是Sig而是DocTimeStamp。)

仅在adbe.x509.rsa_sha1的情况下,所涉及的证书才包含在单独的签名字典条目中。在所有其他情况下SignedData内容的结构中都包含证书(和其他与安全性相关的材料)。