如何从 C# 中签名的 PDF 中获取签名值?
How to get signature Value from signed PDF in C#?
如何从已签名的 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();
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 时,通常会对签名容器的大小进行慷慨的估计,并为注入它保留足够的 space。
根据规范,由于PKCS#7对象的长度是不可预测的,所以Contents的值在末尾补零。
使用 ASN.1 解析器,您可以确定实际签名容器字节序列的长度以及填充的起始位置。
理论上 Contents 的值应该是 DER 编码的 PKCS#7 二进制数据
对象;由于DER编码规则不允许不定长方法,签名容器的大小应根据前导的前几个字节确定。不幸的是,有许多 PDF 在野外包含仅 BER 编码的签名容器的外层,并且仅包含 DER 编码的某些内部对象。因此,可能需要完整的解析。
事后思考
在上面的回答中我直截了当地声称示例代码returns是一个PKCS#7/CMS签名容器。其实只是在大多数情况下是这样的签名容器,它取决于签名字段值的SubFilter。
让我们看看 ISO 32000-1(PDF 规范)和 ETSI 技术规范 102778 部分 (PAdES) 中定义的 SubFilter 值:
adbe.x509.rsa_sha1 ISO 32000-1 - 在这种情况下,内容实际上是 DER 编码的 PKCS#1 二进制数据对象。 OP 的图形
中描述了这种情况
此处的 OP 将内容称为加密摘要,这只是部分事实,因为
PKCS#1 数据对象不是从纯摘要构造的,而是从包含该摘要和摘要算法的 OID 的结构构造的,并且
根据签名算法,这个结构可能不会被加密(作为可以再次解密回摘要的东西),而是只能从中派生出一个无法解密回的数字结构,但仅针对所谓的文档摘要结构进行了测试。
这种格式现在已经很少用了。
adbe.pkcs7.detached ISO 32000-1, ETSI TS 102778-2 - 内容是 DER 编码的 PKCS#7 二进制数据对象 直接对字节范围进行签名,即通常字节范围摘要位于签名属性 MessageDigest
.
adbe.pkcs7.sha1 ISO 32000-1, ETSI TS 102778-2 - 内容是一个 DER 编码的 PKCS#7 二进制数据对象 对字节范围进行间接签名,即将字节范围 SHA1 摘要作为数据放入容器中,然后正常进行签名。
ETSI.CAdES.detached ETSI TS 102778-3 - 内容是a CMS 中指定的 DER 编码的 SignedData 对象直接对字节范围进行签名,本质上这是 adbe.pkcs7.detached.[=17= 的特殊配置变体]
ETSI.RFC3161 ETSI TS 102778-4 - 内容是 a TimeStampToken 作为RFC 3161 中指定直接标记字节范围;这是一种与 PKCS#7 密切相关的时间戳格式。 (这是一个特殊情况,因为表单字段类型不是 Sig,而是 DocTimeStamp。)
仅在 adbe.x509.rsa_sha1 的情况下,涉及的证书包含在单独的签名字典条目中。在所有其他情况下,证书(以及其他与安全相关的 material)包含在 Contents.
的 SignedData
结构中
如何从已签名的 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();
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 时,通常会对签名容器的大小进行慷慨的估计,并为注入它保留足够的 space。
根据规范,由于PKCS#7对象的长度是不可预测的,所以Contents的值在末尾补零。
使用 ASN.1 解析器,您可以确定实际签名容器字节序列的长度以及填充的起始位置。
理论上 Contents 的值应该是 DER 编码的 PKCS#7 二进制数据 对象;由于DER编码规则不允许不定长方法,签名容器的大小应根据前导的前几个字节确定。不幸的是,有许多 PDF 在野外包含仅 BER 编码的签名容器的外层,并且仅包含 DER 编码的某些内部对象。因此,可能需要完整的解析。
事后思考
在上面的回答中我直截了当地声称示例代码returns是一个PKCS#7/CMS签名容器。其实只是在大多数情况下是这样的签名容器,它取决于签名字段值的SubFilter。
让我们看看 ISO 32000-1(PDF 规范)和 ETSI 技术规范 102778 部分 (PAdES) 中定义的 SubFilter 值:
adbe.x509.rsa_sha1 ISO 32000-1 - 在这种情况下,内容实际上是 DER 编码的 PKCS#1 二进制数据对象。 OP 的图形
中描述了这种情况此处的 OP 将内容称为加密摘要,这只是部分事实,因为
PKCS#1 数据对象不是从纯摘要构造的,而是从包含该摘要和摘要算法的 OID 的结构构造的,并且
根据签名算法,这个结构可能不会被加密(作为可以再次解密回摘要的东西),而是只能从中派生出一个无法解密回的数字结构,但仅针对所谓的文档摘要结构进行了测试。
这种格式现在已经很少用了。
adbe.pkcs7.detached ISO 32000-1, ETSI TS 102778-2 - 内容是 DER 编码的 PKCS#7 二进制数据对象 直接对字节范围进行签名,即通常字节范围摘要位于签名属性
MessageDigest
.adbe.pkcs7.sha1 ISO 32000-1, ETSI TS 102778-2 - 内容是一个 DER 编码的 PKCS#7 二进制数据对象 对字节范围进行间接签名,即将字节范围 SHA1 摘要作为数据放入容器中,然后正常进行签名。
ETSI.CAdES.detached ETSI TS 102778-3 - 内容是a CMS 中指定的 DER 编码的 SignedData 对象直接对字节范围进行签名,本质上这是 adbe.pkcs7.detached.[=17= 的特殊配置变体]
ETSI.RFC3161 ETSI TS 102778-4 - 内容是 a TimeStampToken 作为RFC 3161 中指定直接标记字节范围;这是一种与 PKCS#7 密切相关的时间戳格式。 (这是一个特殊情况,因为表单字段类型不是 Sig,而是 DocTimeStamp。)
仅在 adbe.x509.rsa_sha1 的情况下,涉及的证书包含在单独的签名字典条目中。在所有其他情况下,证书(以及其他与安全相关的 material)包含在 Contents.
的SignedData
结构中