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

如何从C#中已签名的PDF中获取签名值?,c#,pdf,itextsharp,digital-signature,C#,Pdf,Itextsharp,Digital Signature,如何从签名的PDF文件中获取签名值?我可以从签名中获取除其值以外的所有其他数据。有没有办法用C#表示 OP澄清签名值是指PKCS#7/CMS签名容器。以下示例方法可以做到这一点: public void showSignatureValues(PdfReader reader) { AcroFields fields = reader.AcroFields; foreach (String name in fields.GetSignatureNames()) {

如何从签名的PDF文件中获取签名值?我可以从签名中获取除其值以外的所有其他数据。有没有办法用C#表示

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签名容器。实际上,只有在大多数情况下,它才是这样一个签名容器,它取决于签名字段值的子过滤器

让我们看一下ISO 32000-1(PDF规范)和ETSI技术规范102778部分(PADS)中定义的子过滤器值:

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

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

  • PKCS#1数据对象不是从裸摘要构建的,而是从包含该摘要和摘要算法OID的结构构建的,以及

  • 根据签名算法的不同,此结构可能不会被加密(作为可以再次解密回摘要的内容),但只能从中派生出一个数字,该数字不能解密回结构,而只能针对所谓的文档摘要结构进行测试

  • 这种格式现在几乎不再使用了

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

  • adbe.pkcs7.sha1ISO 32000-1,ETSI TS 102778-2-内容是一个DER编码的PKCS#7二进制数据对象,对字节范围进行间接签名,即字节范围sha1摘要作为数据放入容器中,该数据反过来被正常签名

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

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


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

按签名值您指的是PKCS#7/CMS签名容器?是的,我指的是从签名pdf中读取base64中的签名file@Sara你的评论不清楚。签名未存储在base64表单中。请用你自己的话解释你说“签名值”是什么意思。在ISO-32000的上下文中,签名值是签名字段中
/V
项的值。该值是一个PDF字典。那本词典很容易弄到。如果您确认您对“签名值”的解释与ISO-32000中描述的签名值相同,则可以提供一个示例。我不太清楚签名是如何保存在pdf中的,或者它在pdf结构中发生了什么变化,但对于签名值,我指的是存储在pdf的/Contents条目中的字节。也许这是一种价值观hexadecimal@Bruno你能帮我找到PDF字典吗?因为现在我读了ISO-32000文件,我想/v条目的值就是我要找的。谢谢@mkl我正在编写iText 5.5.6的发行版,我没有时间回答这个问题。你的答案比我的更完整:谢谢@mkl的解释,我还有一个问题!您编写的这段代码应该只显示签名字典的这一部分(上面图片中用红色签名的部分)还是显示更多,因为当我在代码中尝试时可以看到,它包括证书和一些其他数据。您的图像使用签名类型(子过滤器adbe.x509.rsa_sha1)其中内容仅包含裸pkcs#1签名,其他数据在其他字段中表示。然而,现在主要是在使用中,并且被认为是可互操作的,只有其他类型的内容
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);
        }
    }
}