iText - 生成不含证书链的PDF散列 [英] iText - Generating PDF hash without certificate chain

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

问题描述

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


  1. 客户端从服务器请求PDF散列。 / li>
  2. 服务器生成PDF文件的哈希值并将其发送给客户端。

  3. 客户端使用其私钥对此哈希值进行签名,

  4. 服务器生成一个新的签名的PDF文件。

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



到目前为止,我发现的所有示例都使用PdfPKCS7.getAuthenticatedAttributeBytes函数来获取要签名的哈希值,但这需要知道客户端证书。我已经看过了Bruno Lowagie的PDF文档的数字签名白皮书,但我没有准确地看到消息是什么信息。



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

  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,'\0',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);不幸的是,这个哈希似乎不正确,签名这个哈希哈希并且基于所签名的哈希生成签名文档导致无效签名。



如果有人可以帮助我改进此代码片段,或者让我对我需要为签名进行摘要的数据有所了解,我将非常感谢。 p>

解决方案

您似乎已经忽略了 DeferredSigning 示例。



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

  public void emptySignature(String src,String dest,String fieldname,证书[]链)throws IOException,DocumentException,GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader,os,'\0');
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] / code>传递给外观,在此示例中,它用于创建视觉外观并创建 PdfPKCS7 object。



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

  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



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

 类MyExternalSignatureContainer实现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()方法:

  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);
}


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

  1. A client requests a PDF-hash from the server.
  2. The server generates the hash of a PDF-file and sends this to the client.
  3. The client signs this hash with his private key and sends the signed hash along with the public part of his own certificate.
  4. The server generates a new, signed PDF file.

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.

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, '\0', 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.

解决方案

It seems that you have overlooked the DeferredSigning example.

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, '\0');
    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);
}

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.

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);

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) {
    }
}

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天全站免登陆