iText - 生成没有证书链的 PDF 哈希 [英] iText - Generating PDF hash without certificate chain

查看:37
本文介绍了iText - 生成没有证书链的 PDF 哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建一个发生以下情况的应用程序:

I'm trying to build an application where the following happens:

  1. 客户端向服务器请求 PDF 哈希.
  2. 服务器生成 PDF 文件的哈希并将其发送给客户端.
  3. 客户用他的私钥签署这个散列,并将签名的散列连同他自己证书的公共部分一起发送.
  4. 服务器生成一个新的签名 PDF 文件.

我遇到的问题是:如果没有事先提供客户端的证书,服务器似乎不可能生成待签名的哈希.我真的更喜欢创建一个解决方案,其中服务器不需要知道客户端的证书即可创建文档摘要.

The problem I'm having with this: It seems impossible for the server to generate a to-be-signed hash without having the client's certificate available beforehand. I'd really prefer to create a solution where the server does not need to know the client's certificate in order to create the document digest.

到目前为止,我发现的所有示例都使用 PdfPKCS7.getAuthenticatedAttributeBytes 函数来获取要签名的哈希,但这需要知道客户端证书.我已经查看了 Bruno Lowagie 的PDF 文档的数字签名"白皮书,但我没有看到具体消化了哪些信息.

All the examples I have found so far use the PdfPKCS7.getAuthenticatedAttributeBytes function to get the to-be-signed hash, but this requires a client certificate to be known. I have looked at the "Digital Signatures for PDF documents" white paper by Bruno Lowagie, but I failed to see exactly what information is digested.

这是我当前尝试的代码片段:

Here's a code snippet of my current attempt:

public byte[] simplePresign(String src, String digestAlgorithm) throws IOException, DocumentException, GeneralSecurityException {
    this.digestAlgorithm = digestAlgorithm;
    tsaClient = new CustomTSAClient();

    PdfReader reader = new PdfReader(src);
    os = new ByteArrayOutputStream();
    PdfAStamper stamper = PdfAStamper.createSignature(reader, os, '', PdfAConformanceLevel.PDF_A_1B);
    appearance = stamper.getSignatureAppearance();

    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    appearance.setCryptoDictionary(dic);

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

    InputStream data = appearance.getRangeStream();
    MessageDigest mDigest = DigestAlgorithms.getMessageDigest(digestAlgorithm, null);

    return DigestAlgorithms.digest(data, mDigest);

}

不幸的是,这个散列似乎不正确,签署这个散列并根据签名的散列生成一个签名文档会导致签名无效.

Unfortunately this hash does not seem to be correct, signing this hash and generating a signed document based on the signed hash leads to an invalid signature.

如果有人可以帮助我改进此代码片段,或者让我深入了解我需要消化签名的数据,我将不胜感激.

I would be most grateful if someone can help me improve this code snippet, or otherwise give me some insight in the data that I need to digest for a signature.

推荐答案

看来你忽略了DeferredSigning 示例.

在本例中,我们首先创建一个带有空签名的 PDF:

In this example, we first create a PDF with an empty signature:

public void emptySignature(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '');
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, fieldname);
    appearance.setCertificate(chain[0]);
    ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    MakeSignature.signExternalContainer(appearance, external, 8192);
}

当然,在本例中,公共证书 chain[0] 被传递给 appearance,它用于创建视觉外观和创建 PdfPKCS7 对象.

Granted, the public certificate chain[0] is passed to the appearance in this example, it is used to create the visual appearance and to create the PdfPKCS7 object.

一旦您拥有一个带有空签名的 PDF,您可以在服务器上创建一个 PdfSignatureAppearance 并获取可以发送到客户端进行签名的哈希.这可以使用 getRangeStream() 方法来获取需要散列的 PDF 字节范围.此方法返回一个 InputStream,可以这样使用:

Once you have a PDF with an empty signature, you can create a PdfSignatureAppearance on the server and get the hash that can be sent to the client for signing. This can be done using the getRangeStream() method to get the ranges of PDF bytes that need to be hashed. This method returns an InputStream, that can be used like this:

BouncyCastleDigest digest = new BouncyCastleDigest();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest(hashAlgorithm));
Calendar cal = Calendar.getInstance();
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);

现在您可以将此 sh 发送给客户端进行签名.您将收到另一个 byte[] 这是需要添加到 PDF 的实际签名,假设 byte[] 称为 sig.

Now you can send this sh to the client for signing. You will receive another byte[] which is the actual signature that needs to be added to the PDF, let's say the byte[] is called sig.

您的外部签名容器可以保持非常简单:它只需要返回签名字节:

Your external signature container can be kept very simple: it just needs to return the signature bytes:

class MyExternalSignatureContainer implements ExternalSignatureContainer {
    protected byte[] sig;
    public MyExternalSignatureContainer(byte[] sig) {
        this.sig = sig;
    }
    public byte[] sign(InputStream is) throws Exception {
        return sig;
    }
    public void modifySigningDictionary(PdfDictionary signDic) {
    }
}

您现在可以在服务器上使用 createSignature() 方法:

You can now use the createSignature() method on the server:

public void createSignature(String src, String dest, String fieldname, PrivateKey pk, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
    
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    ExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
    MakeSignature.signDeferred(reader, fieldname, os, external);
}

这篇关于iText - 生成没有证书链的 PDF 哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆