gpt4 book ai didi

itext - 使用外部服务和 iText 签署 PDF

转载 作者:行者123 更新时间:2023-12-02 19:59:15 28 4
gpt4 key购买 nike

我有这样的场景。

我有一个生成 PDF 的应用程序,需要对其进行签名。

我们没有用于签署文档的证书,因为它们位于 HSM 中,而我们使用证书的唯一方法是使用 Web 服务。

此网络服务提供两个选项,发送 PDF 文档,并返回签名的 pdf,或发送将签名的哈希值。

第一个选项不可行,因为 PDF 签名时没有时间戳(这是一个非常重要的先决条件),因此选择第二个选项。

这是我们的代码,首先,我们获取签名外观,并计算哈希值:

PdfReader reader = new PdfReader(Base64.decode(pdfB64));
reader.setAppendable(true);
baos = new ByteArrayOutputStream();

PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0', null, true);
appearance = stamper.getSignatureAppearance();
appearance.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
appearance.setVisibleSignature("Representant");
cal = Calendar.getInstance();
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.TYPE, PdfName.SIG);
dic.put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
dic.put(PdfName.SUBFILTER, new PdfName("adbe.pkcs7.detached"));
dic.put(PdfName.M, new PdfDate(cal));
appearance.setCryptoDictionary(dic);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, Integer.valueOf(reservedSpace.intValue() * 2 + 2));
appearance.setCertificationLevel(1);
appearance.preClose(exc);

AbstractChecksum checksum = JacksumAPI.getChecksumInstance("sha1");
checksum.reset();
checksum.update(Utils.streamToByteArray(appearance.getRangeStream()));
hash = checksum.getByteArray();

此时,我们就得到了文档的哈希码。然后我们将哈希发送到网络服务,并获得签名的哈希代码。

最后,我们将签名的哈希值放入 PDF:

byte[] paddedSig = new byte[reservedSpace.intValue()];
System.arraycopy(signedHash, 0, paddedSig, 0, signedHash.length);

PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
appearance.close(dic);

byte[] pdf = baos.toByteArray();

此时,我们得到了一个已签名的 PDF,但签名无效。 Adobe 表示“文档自签名以来已被更改或损坏”。

我认为我们在这个过程中犯了一些错误,而且我们不知道到底会发生什么。

我们感谢您提供这方面的帮助,或者提供替代方法。

谢谢。

<小时/>

已编辑

根据mkl的建议,我遵循了本书的4.3.3部分Digital Signatures for PDF documents ,我的代码如下:

第一部分,当我们计算哈希时:

PdfReader reader = new PdfReader(Base64.decode(pdfB64));
reader.setAppendable(true);
baos = new ByteArrayOutputStream();

PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');
appearance = stamper.getSignatureAppearance();

appearance.setReason("Test");
appearance.setLocation("A casa de la caputeta");
appearance.setVisibleSignature("TMAQ-TSR[0].Pagina1[0].DadesSignatura[0].Representant[0]");
appearance.setCertificate(chain[0]);

PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(appearance.getReason());
dic.setLocation(appearance.getLocation());
dic.setContact(appearance.getContact());
dic.setDate(new PdfDate(appearance.getSignDate()));
appearance.setCryptoDictionary(dic);

HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, new Integer(reservedSpace.intValue() * 2 + 2));
appearance.preClose(exc);

ExternalDigest externalDigest = new ExternalDigest()
{
public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException
{
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};

sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
InputStream data = appearance.getRangeStream();
hash = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));
cal = Calendar.getInstance();

byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
sh = MessageDigest.getInstance("SHA256", "BC").digest(sh);

hashPdf = new String(Base64.encode(sh));

在第二部分中,我们获得签名的哈希值,并将其放入 PDF 中:

sgn.setExternalDigest(Base64.decode(hashSignat), null, "RSA");
byte[] encodedSign = sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
byte[] paddedSig = new byte[reservedSpace.intValue()];
System.arraycopy(encodedSign, 0, paddedSig, 0, encodedSign.length);

PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));

appearance.close(dic2);

byte[] pdf = baos.toByteArray();

现在,Adobe 提出内部加密库错误。错误代码:0x2726,当我们尝试验证签名时。

最佳答案

如果网络服务仅返回签名哈希

In this point, we have the hash code of the document. Then we send the hash to the webservice, and we get the signed hash code.

Finally, we put the signed hash to the PDF:

如果网络服务仅返回签名哈希,则您的 PDF 签名不正确:您将签名 SubFilter 设置为 adbe.pkcs7.detached。这意味着签名内容必须包含完整的 PKCS#7 签名容器,而不仅仅是签名的哈希值。

您可能想要下载Digital Signatures for PDF documentsBruno Lowagie(iText 软件)的白皮书,介绍使用 iText 创建和验证数字 PDF 签名。它特别包含“4.3 用于签名的客户端/服务器架构”部分,其中应包含您的用例。

但是 Web 服务返回一个成熟的 CMS 签名容器

根据上述解释,OP 开始使用上述白皮书第 4.3.3 节中的代码,该代码旨在使用外部生成的签名哈希进行签名。由于这也导致了 Adob​​e Reader 不满意的签名文档,因此他提供了使用此新代码创建的示例文档。

对样本的分析表明,嵌入在文档中的 CMS 签名容器包含另一个 CMS 签名容器,其中应该包含签名属性的签名字节(签名哈希):

2417   13:           SEQUENCE {
2419 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
2430 0: NULL
: }
2432 5387: OCTET STRING, encapsulates {
2436 NDEF: SEQUENCE {
2438 9: OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2)
2449 NDEF: [0] {
2451 NDEF: SEQUENCE {

(签名算法后面的 OCTET STRING 应包含签名字节,并且不能嵌入另一个 SignedData 结构。)

这表明 Web 服务确实已经返回了一个成熟的 CMS 容器。

对于这种情况,原始代码看起来相当不错。该问题可能是由于使用了错误的哈希算法(原始代码使用 SHA1 进行哈希处理)等细节造成的。

可能的问题:BER 编码

嵌入在 CMS 容器中的 Web 服务的 CMS 签名容器是由 iText 从 OP 提供的第一个示例生成的,提示可能存在问题:查看嵌入的外部结构大小上方的 ASN.1 转储CMS 容器通常是 NDEF

这表明这些外部结构是使用不太严格的 BER(基本编码规则)创建的,而不是更严格的 DER(区分编码规则),因为在 DER 中禁止使用 BER 选项来启动结构而不说明其大小。

PDF 规范引用的 CMS 规范 (RFC 3852) 确实允许对容器的外部结构进行任何 BER 编码,而 PDF 规范则要求:

the value of Contents shall be a DER-encoded PKCS#7 binary dataobject containing the signature. The PKCS#7 object shall conform to RFC3852 Cryptographic Message Syntax.

因此,严格来说,嵌入 PDF 中的签名容器需要全部进行 DER 编码。

据我所知,只要签名容器对某些关键元素进行 DER 编码,PDF 签名验证器就不会拒绝此类签名。不过,对于 future 的工具来说,此类签名可能是一个失败点。

关于itext - 使用外部服务和 iText 签署 PDF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28717704/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com