从签名的 PDF 中提取 PKCS1
Extract PKCS1 from Signed PDF
我必须从 PDF 签名文档中提取签名字段才能创建打印的签名版本。到目前为止,我已经能够使用此 iText 代码恢复签名者证书、原因、签名日期和其他字段:
PdfReader reader = new PdfReader(signedPdf);
AcroFields af = reader.getAcroFields();
ArrayList<String> names = af.getSignatureNames();
SimpleDateFormat sdf = new SimpleDateFormat(
"dd/MM/yyyy 'a las' HH:mm:ss");
for (int i = 0; i < names.size(); ++i) {
StringBuilder sb = new StringBuilder();
String name = names.get(i);
PdfPKCS7 pk = af.verifySignature(name);
String firmante = CertificateInfo.getSubjectFields(
pk.getSigningCertificate()).getField("CN");
sb.append("Nombre del firmante: " + firmante + "\n");
Date signDate = pk.getSignDate().getTime();
String sdate = sdf.format(signDate);
sb.append("Fecha y hora de la firma: " + sdate + "\n");
String razon = pk.getReason();
sb.append("proposito: " + razon + "\n");
}
据我所知,PDF 签名是使用 iText PdfPkcs7 class 使用 setExternalDigest 方法添加在外部应用程序中创建的 PKCS1 字节数组。该文件看起来已通过外部工具正确签名和验证。
// Create the signature
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA1", "BC", null, false);
//pkcs1Bytes is a byte array with the signed document hash
sgn.setExternalDigest(pkcs1Bytes, null, "RSA");
但是,印刷版的必填字段之一是 "signature digital stamp",它是签名文档哈希或 PKCS1 的 base 64 字符串。
是否可以从已签名的 PDF 文档中提取 PKCS1 字节?
已编辑:我忘了提到当我在验证签名后使用 PdfPKCS7.getEncodedPKCS1()
方法时它会抛出 ExceptionConverter: java.security.SignatureException: object not initialized for signing
我查看了 code,class PdfPKCS7
似乎不允许访问摘要。但是,内容存储在私有成员 PdfPKCS7.digest
中。所以使用反射可以让你提取它。
我找到了一个类似的例子here and (基本相同)
PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name);
pdfPkcs7.verify();
Field digestField = PdfPKCS7.class.getDeclaredField("digest");
digestField.setAccessible(true);
byte[] digest = (byte[]) digestField.get(pdfPkcs7);
我觉得你需要的变量是digest
因为在执行签名的时候在getEncodedPKCS1
赋值
public byte[] getEncodedPKCS1() {
try {
if (externalDigest != null)
digest = externalDigest;
else
digest = sig.sign();
//skipped content
并在verify()
中使用如下方式verifyResult = sig.verify(digest);
请注意,digest
是私有变量,因此名称或内容可能取决于版本。查看您的特定版本的代码。
考虑到您的代码,我假设您使用的是 5.x iText 版本,而不是 7.x。
您可以使用反射(参见 或此处 pedrofb 的回答),或者您可以简单地使用 iText 提取 CMS 签名容器,然后使用 BouncyCastle 分析该容器;如果您使用 iText 的签名相关功能,BC 的一个版本通常已经存在。
正如 OP 已经观察到的那样,PdfPKCS7.getEncodedPKCS7()
失败并显示 "ExceptionConverter: java.security.SignatureException: object not initialized for signing"。 原因是此方法用于检索签名容器 由PdfPKCS7
实例新建。
要使用 iText 提取 CMS 签名容器,您可以改用此代码:
AcroFields fields = reader.getAcroFields();
PdfDictionary sigDict = fields.getSignatureDictionary(name);
PdfString contents = sigDict.getAsString(PdfName.CONTENTS);
byte[] contentBytes = contents.getOriginalBytes();
contentBytes
现在包含编码的 CMS 容器(加上一些尾随字节,通常是空字节,因为 Contents 值通常大于签名容器所需的值) .
使用 BouncyCastle 分析此容器并不困难,但详细信息可能取决于您使用的确切 BouncyCastle 版本。
我必须从 PDF 签名文档中提取签名字段才能创建打印的签名版本。到目前为止,我已经能够使用此 iText 代码恢复签名者证书、原因、签名日期和其他字段:
PdfReader reader = new PdfReader(signedPdf);
AcroFields af = reader.getAcroFields();
ArrayList<String> names = af.getSignatureNames();
SimpleDateFormat sdf = new SimpleDateFormat(
"dd/MM/yyyy 'a las' HH:mm:ss");
for (int i = 0; i < names.size(); ++i) {
StringBuilder sb = new StringBuilder();
String name = names.get(i);
PdfPKCS7 pk = af.verifySignature(name);
String firmante = CertificateInfo.getSubjectFields(
pk.getSigningCertificate()).getField("CN");
sb.append("Nombre del firmante: " + firmante + "\n");
Date signDate = pk.getSignDate().getTime();
String sdate = sdf.format(signDate);
sb.append("Fecha y hora de la firma: " + sdate + "\n");
String razon = pk.getReason();
sb.append("proposito: " + razon + "\n");
}
据我所知,PDF 签名是使用 iText PdfPkcs7 class 使用 setExternalDigest 方法添加在外部应用程序中创建的 PKCS1 字节数组。该文件看起来已通过外部工具正确签名和验证。
// Create the signature
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA1", "BC", null, false);
//pkcs1Bytes is a byte array with the signed document hash
sgn.setExternalDigest(pkcs1Bytes, null, "RSA");
但是,印刷版的必填字段之一是 "signature digital stamp",它是签名文档哈希或 PKCS1 的 base 64 字符串。
是否可以从已签名的 PDF 文档中提取 PKCS1 字节?
已编辑:我忘了提到当我在验证签名后使用 PdfPKCS7.getEncodedPKCS1()
方法时它会抛出 ExceptionConverter: java.security.SignatureException: object not initialized for signing
我查看了 code,class PdfPKCS7
似乎不允许访问摘要。但是,内容存储在私有成员 PdfPKCS7.digest
中。所以使用反射可以让你提取它。
我找到了一个类似的例子here and
PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name);
pdfPkcs7.verify();
Field digestField = PdfPKCS7.class.getDeclaredField("digest");
digestField.setAccessible(true);
byte[] digest = (byte[]) digestField.get(pdfPkcs7);
我觉得你需要的变量是digest
因为在执行签名的时候在getEncodedPKCS1
赋值
public byte[] getEncodedPKCS1() {
try {
if (externalDigest != null)
digest = externalDigest;
else
digest = sig.sign();
//skipped content
并在verify()
中使用如下方式verifyResult = sig.verify(digest);
请注意,digest
是私有变量,因此名称或内容可能取决于版本。查看您的特定版本的代码。
考虑到您的代码,我假设您使用的是 5.x iText 版本,而不是 7.x。
您可以使用反射(参见
正如 OP 已经观察到的那样,PdfPKCS7.getEncodedPKCS7()
失败并显示 "ExceptionConverter: java.security.SignatureException: object not initialized for signing"。 原因是此方法用于检索签名容器 由PdfPKCS7
实例新建。
要使用 iText 提取 CMS 签名容器,您可以改用此代码:
AcroFields fields = reader.getAcroFields();
PdfDictionary sigDict = fields.getSignatureDictionary(name);
PdfString contents = sigDict.getAsString(PdfName.CONTENTS);
byte[] contentBytes = contents.getOriginalBytes();
contentBytes
现在包含编码的 CMS 容器(加上一些尾随字节,通常是空字节,因为 Contents 值通常大于签名容器所需的值) .
使用 BouncyCastle 分析此容器并不困难,但详细信息可能取决于您使用的确切 BouncyCastle 版本。