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

时间:2015-05-07 08:22:20

标签: 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();

enter image description here

1 个答案:

答案 0 :(得分:4)

OP澄清说签名值旨在引用 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);
        }
    }
}

但需要注意的一点是:您会发现contentBytes通常在签名容器字节后包含大量00个字节(在Base64表示中,它们显示为一长串字母A )。这是因为在准备用于签名的PDF时,通常会对签名容器大小做出慷慨的估计,并且为注入它留出了足够的空间。

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

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

理论上内容的值应为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. 取决于签名算法,这个结构可能不被加密(作为可以再次解密回摘要的东西),而是只能从中导出一个不能解密回结构的数字而只是对所谓的文件摘要结构进行了测试。

    3. 这种格式现在几乎不再使用了。

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

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

    6. ETSI.CAdES.detached ETSI TS 102778-3 - 内容是CMS中指定的DER编码的SignedData对象直接签署字节范围,基本上这是 adbe.pkcs7.detached 的特殊配置变体。

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

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