如何使用iText在所有文档页面中显示数字PDF签名? [英] How to show digital PDF signature in all document's Page using iText?

查看:912
本文介绍了如何使用iText在所有文档页面中显示数字PDF签名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经开始研究数字签名功能已有好几天了,现在我已经完成了所有工作,现在是时候尝试在所有页面上打印邮票,但我做得不好......



试图给出快速简历,以显示我做的印章是创建PdfStamper,PdfSignatureAppearance和一个矩形,然后调用

  appearance.setVisibleSignature(rectangle,1,SIGNATURE)

上面的第二个参数1是我想要显示标记的页码,现在可以是1,因为我试图在其他页面中显示标记正在创建PdfStamper的其他实例,PdfSignatureAppearance和一个矩形,但将其设置为第2页。如果它已经工作,我会把它放在循环中并继续更改页面参数。



但为什么不呢工作???好吧,接近结尾我调用一个MakeSignature方法,在参数中我必须通过我创建的一个外观,如果我多次调用它,签名只出现在与我传递给它的最后一个外观有关的页面上。



例如:

  MakeSignature.signDetached(appearance2,digest, pks,chain,null,null,null,0,CryptoStandard.CMS); 
MakeSignature.signDetached(appearance,digest,pks,chain,null,null,null,0,CryptoStandard.CMS);

邮票将仅显示在第一页。



也许我可以在这里得到一些帮助???



这就是整个事情:

  public String signPdfFirstTime(String src,String dest,PrivateKey pk,Certificate [] chain,String providerName,String conteudoBase64,X509Certificate cert,String alias)throws IOException,DocumentException,GeneralSecurityException 
{
byte [] conteudoBinario = Base64.decode(conteudoBase64);

FileOutputStream fos = new FileOutputStream(path + File.separator + src);
fos.write(conteudoBinario);
fos.close();

文件f =新文件(路径+ File.separator + src);
FileInputStream in = new FileInputStream(f);
PdfReader reader = new PdfReader(in);

int qtypages = reader.getNumberOfPages();
FileOutputStream os = new FileOutputStream(path + File.separator + dest);
PdfStamper stamper = PdfStamper.createSignature(reader,os,'\ 0');
//创建外观
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
Rectangle rectangle = new Rectangle(550,50,610,500); // funciona vertical
appearance.setVisibleSignature(rectangle,1,SIGNATURE);

//这里我构建了一个自定义消息......没什么相关的
StringBuilder stampMessage = new StringBuilder();
stampMessage.append(...);
stampMessage.append(别名);
stampMessage.append( - );
//自定义外观图层2以垂直显示文本
PdfTemplate layer2 = appearance.getLayer(2);
layer2.transform(new AffineTransform(0,1,-1,0,rectangle.getWidth(),0));
Font font = new Font();
font.setColor(BaseColor.BLACK);
ColumnText ct2 = new ColumnText(layer2);
ct2.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
ct2.setSimpleColumn(new Phrase(stampMessage.toString(),font),0,0,rectangle.getHeight(),rectangle.getWidth(),15,Element.ALIGN_LEFT);
ct2.go();
appearance.setCertificate(cert);

//这里开始我试图在页面中显示第二个标记2
FileOutputStream fos2 = new FileOutputStream(path + File.separator + src);
fos2.write(conteudoBinario);
fos2.close();
文件f2 =新文件(路径+ File.separator + src);
FileInputStream in2 = new FileInputStream(f2);

PdfReader reader2 = new PdfReader(in2);
FileOutputStream os2 = new FileOutputStream(path + File.separator + dest);

PdfStamper stamper2 = PdfStamper.createSignature(reader2,os2,'\ 0');
//创建外观
PdfSignatureAppearance appearance2 = stamper2.getSignatureAppearance();

矩形rectangle2 = new Rectangle(550,50,610,500); // funciona vertical
appearance2.setVisibleSignature(rectangle2,3,ASSINATURA2);

// Cria a msg que aparece na estampa
StringBuilder stampMessage2 = new StringBuilder();
stampMessage2.append( - );

PdfTemplate layer22 = appearance.getLayer(2);
layer22.transform(new AffineTransform(0,1,-1,0,rectangle2.getWidth(),0));
Font font2 = new Font();
font2.setColor(BaseColor.BLACK);
ColumnText ct22 = new ColumnText(layer22);
ct22.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
ct22.setSimpleColumn(new Phrase(stampMessage2.toString(),font2),0,0,rectangle2.getHeight(),rectangle2.getWidth(),15,Element.ALIGN_LEFT);
ct22.go();
appearance2.setCertificate(cert);

//创建签名
ExternalSignature pks = new PrivateKeySignature(pk,DigestAlgorithms.SHA256,providerName);
ExternalDigest digest = new BouncyCastleDigest();
列表< CrlClient> crlList = new ArrayList< CrlClient>();
crlList.add(new CrlClientOnline());

LtvVerification v = stamper.getLtvVerification();
LtvVerification v2 = stamper2.getLtvVerification();

OcspClient ocspClient = new OcspClientBouncyCastle();

String url = CertificateUtil.getCRLURL(cert);
CertificateFactory cf = CertificateFactory.getInstance(X.509);

X509CRL crl =(X509CRL)cf.generateCRL(new URL(url).openStream());
System.out.println(CRL有效期至:+ crl.getNextUpdate());
System.out.println(证书已撤销:+ crl.isRevoked(chain [0]));

if(crl.isRevoked(chain [0])){

抛出新的GeneralSecurityException(CERTIFICADO REVOGADO!);
}
else {
MakeSignature.processCrl(cert,crlList);

MakeSignature.signDetached(appearance2,digest,pks,chain,null,null,null,0,CryptoStandard.CMS);
MakeSignature.signDetached(appearance,digest,pks,chain,null,null,null,0,CryptoStandard.CMS);
os.close();
byte [] b = this.read(f);
返回Base64.encodeBytes(b);
}
}


解决方案

这实际上是对可用选项的讨论...



打印签名的方法根本不同em>所有页面中的标记:




  • 创建一个包含多个可视化的单一签名字段,每页一个。

  • 在单个页面上创建单个可视化字段,例如最后一个文档页面,并在所有其他页面上创建具有相同内容的图像。

  • 每页创建一个签名字段,每个页面上都有一个可视化文件。



PDF签名字段的可视化是与该字段直接关联的小部件;特别是可以点击它们打开一个签名验证对话框。与这些小部件相比,第二个选项中的图像仅仅是没有相关动作的图像。



单个签名,多个可视化



这很可能是OP在他心目中的选择。特别是这是一个更好的选择,至少乍一看:




  • 每个页面上签名图像处于活动状态并允许打开签名验证对话框;

  • 但仍然只需要创建一个数字签名,这意味着


    • 签名上只有一个条目Adobe Reader左侧的面板,

    • 只有一个要验证的签名容器(因此,没有明确的验证结果),而

    • 只使用一次私钥,因此PIN不需要多次输入,并且如果按签名付费签名服务只需要支付一个签名事件。




但是有许多缺点:




  • 同一签名的多个可视化可能会对该签名的合法价值产生负面影响。



    Adob​​e因此决定几年前没有用multi创建签名字段在他们的软件中进行可视化,参见例如


    文档中签名的位置可能与其法律含义有关。因此,
    签名字段从不引用多个注释。如果多个位置与
    签名相关联,则含义可能会变得模糊不清。





    有效该面板上没有相关条目(具有正确的页码)的签名字段可能会被怀疑。




单一签名,最后一页上的单一可视化,其他页面上的非活动图像



如果使用此选项,则先前选项的缺点不适用或至少仅适用于程度较低。特别是如果仅通过提示指示它们是副本的图像与可视化略有不同,则最终的真实可视化可能被视为绑定签名位置。



但是,此选项的主要缺点是,对于已签名的文档,不允许向内容添加纯粹的图像。因此,此选项不能用于文档的第二个或第三个签名者,但OP已指示解决方案最终必须允许将文档签名给多个人



可以考虑将这些图像添加为注释,而不是内容;对于某些类型的集成PDF签名,在签名后添加和删除注释是允许的操作。但是,如果允许添加这些注释,通常也允许在签名后再次删除它们,使这些签名图像非常不稳定。



多个签名(每页一个)单个可视化每个



此选项没有其他选项的缺点,因为每个可视化对应于不同的数字签名。因此,最后一个保证签名者在整个文档中受法律约束。



它确实有自己的缺点:




  • 在验证过程中,所有这些签名都将得到验证。这可能意味着高资源需求,甚至更糟糕的结果(如果某些验证失败并且有些成功)。

  • Adob​​e Reader签名面板上充斥着条目。

  • 私钥被多次使用,在签名付费签名服务的情况下是昂贵的,并且在SSCD(特别是智能卡或令牌)的情况下可能需要多次输入PIN并且还需要相当长的时间



实施选项



iText允许实施第二个和第三个选项开箱即用的相当直接的方式。



使用iText可以实现第一个选项,但需要使用低级API和Java反射或或者稍微修补iText。






考虑到每个选项的问题,我建议不要这样做,签名内容末尾的一个签名是最不明确的签署方式。


I've been working on a digital signature function for some days, now that I have everything working it is time to try to print the stamp in all pages but I'm not doing great...

Trying to give a quick resume, to show the stamp what I do is creating PdfStamper, PdfSignatureAppearance and a Rectangle, then call the

 appearance.setVisibleSignature(rectangle, 1, "SIGNATURE")

The second parameter "1" above is the page number that I want to show the stamp, it is okay to be 1 by now, as I tried to do in order to show stamp in other page was creating other instances of PdfStamper, PdfSignatureAppearance and a Rectangle but set it to page 2. If it had worked I would have put it within a loop and keep changing the page parameter.

But why didn't it work??? Well, near the end I call a method of MakeSignature and among the parameters I have to pass one of the appearences I created, if I call it more than once the signature appears only on the page related to the last appearence I pass to it.

For example:

    MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
    MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);

The stamp will be showed on the first page only.

Maybe I could get some help here???

Here it goes the whole thing:

public String signPdfFirstTime(String src, String dest, PrivateKey pk, Certificate[] chain, String providerName, String conteudoBase64, X509Certificate cert, String alias) throws IOException, DocumentException, GeneralSecurityException
{
    byte[] conteudoBinario = Base64.decode(conteudoBase64);

    FileOutputStream fos = new FileOutputStream(path + File.separator + src);
    fos.write(conteudoBinario);
    fos.close();

    File f = new File(path + File.separator + src);
    FileInputStream in = new FileInputStream(f);
    PdfReader reader = new PdfReader(in);

    int qtypages = reader.getNumberOfPages(); 
    FileOutputStream os = new FileOutputStream(path + File.separator + dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    Rectangle rectangle = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance.setVisibleSignature(rectangle, 1, "SIGNATURE");

    //Here I build a custom message...nothing relevant
    StringBuilder stampMessage = new StringBuilder();
    stampMessage.append("...");
    stampMessage.append(alias);
    stampMessage.append(" - ");
        // customize appearance layer 2 to display text vertically
    PdfTemplate layer2 = appearance.getLayer(2);
    layer2.transform(new AffineTransform(0, 1, -1, 0, rectangle.getWidth(), 0));
    Font font = new Font();
    font.setColor(BaseColor.BLACK);
    ColumnText ct2 = new ColumnText(layer2);
    ct2.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct2.setSimpleColumn(new Phrase(stampMessage.toString(), font), 0, 0, rectangle.getHeight(), rectangle.getWidth(), 15, Element.ALIGN_LEFT);
    ct2.go();
    appearance.setCertificate(cert);

    //Here starts where I tried to make a second stamp to show in the page 2
    FileOutputStream fos2 = new FileOutputStream(path + File.separator + src);
    fos2.write(conteudoBinario);
    fos2.close();
    File f2 = new File(path + File.separator + src);
    FileInputStream in2 = new FileInputStream(f2);

    PdfReader reader2 = new PdfReader(in2);
    FileOutputStream os2 = new FileOutputStream(path + File.separator + dest);

    PdfStamper stamper2 = PdfStamper.createSignature(reader2, os2, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance2 = stamper2.getSignatureAppearance();

    Rectangle rectangle2 = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance2.setVisibleSignature(rectangle2, 3, "ASSINATURA2");

    //Cria a msg que aparece na estampa
    StringBuilder stampMessage2 = new StringBuilder();
    stampMessage2.append(" - ");

    PdfTemplate layer22 = appearance.getLayer(2);
    layer22.transform(new AffineTransform(0, 1, -1, 0, rectangle2.getWidth(), 0));
    Font font2 = new Font();
    font2.setColor(BaseColor.BLACK);
    ColumnText ct22 = new ColumnText(layer22);
    ct22.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct22.setSimpleColumn(new Phrase(stampMessage2.toString(), font2), 0, 0, rectangle2.getHeight(), rectangle2.getWidth(), 15, Element.ALIGN_LEFT);
    ct22.go();
    appearance2.setCertificate(cert);

    // Creating the signature
    ExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, providerName);
    ExternalDigest digest = new BouncyCastleDigest();
    List<CrlClient> crlList = new ArrayList<CrlClient>();
    crlList.add(new CrlClientOnline());

    LtvVerification v = stamper.getLtvVerification();
    LtvVerification v2 = stamper2.getLtvVerification();

    OcspClient ocspClient = new OcspClientBouncyCastle();

    String url = CertificateUtil.getCRLURL(cert);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    X509CRL crl = (X509CRL) cf.generateCRL(new URL(url).openStream());
    System.out.println("CRL valid until: " + crl.getNextUpdate());
    System.out.println("Certificate revoked: " + crl.isRevoked(chain[0]));

    if (crl.isRevoked(chain[0])) {

        throw new GeneralSecurityException("CERTIFICADO REVOGADO!");
    }
    else {
        MakeSignature.processCrl(cert, crlList);

        MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        os.close();
        byte[] b = this.read(f);
        return Base64.encodeBytes(b);
    }
}

解决方案

This actually is a discussion of the available options...

There are fundamentally different ways to print the signature stamp in all pages:

  • Create a single signature field with multiple visualizations, one on each page.
  • Create a single signature field with a single visualization on a single page, e.g. the last document page, and furthermore create images with the same content on all the other pages.
  • Create one signature field per page, each with a single visualization on its page.

"Visualizations" of a PDF signature field are widgets directly associated with the field; in particular they can be clicked to have a signature validation dialog open up. In contrast to these widgets the "images" in the second option are mere images without such an associated action.

Single signature, multiple visualizations

This most likely is the option the OP has on his mind. In particular this is the preferable option, at least at first glance:

  • on each page the signature image is active and allows opening the signature validation dialog;
  • but still only one digital signature has to be created, which means
    • only one entry on the signature panel on the left in Adobe Reader,
    • only one signature container to verify (therefore, no unambiguous verification results), and
    • only one use of the private key, so a PIN does not need to be entered multiple times, and in case of pay-per-signature signing services only a single signing event to pay.

There are a number of disadvantages, though:

  • Multiple visualizations of the same signature may have a negative impact on the legal value of that signature.

    Adobe, therefore, decided years ago not to create signature fields with multiple visualizations in their software, cf. for example

    The location of a signature within a document can have a bearing on its legal meaning. For this reason, signature fields never refer to more than one annotation. If more than one location is associated with a signature, the meaning may become ambiguous.

    (Digital Signature Appearances whitepaper for Adobe Acrobat version 9 dated May 5, 2008)

    E.g. in Germany jurisdiction concerning written signatures vertically confines the part of the document the signer has legally signed, he generally is not legally bound by anything written below the signature. Similar jurisdiction may exist in other legal systems, too.

    In case of electronic signatures with visualizations in the signed documents, such jurisdiction might hold analogously (or one at least has to make quite an effort to explain the differences). In case of multiple visualization of the same signature this might mean that only everything up to the first visualization is considered signed.

    (I am not a lawyer, so please do not consider this legal consultation.)

  • Because of such potential legal issues signature fields in the upcoming PDF 2.0 standard will only be allowed to have a single widget. Thus, signatures with multiple widgets likely will be considered invalid according to that standard.

  • Already now the signature panel of Adobe Reader contains "the page the signature is on", cf. the last line of this screen shot:

    Active signature fields without an associated entry (with the correct page number) on that panel might be doubted outright.

Single signature, single visualization on last page, inactive images on the other pages

In case of using this option, the disadvantages of the prior option do not apply or at least only to a lesser degree. In particular if the mere images slightly differ from the visualization by a hint indicating that they are copies, the final, true visualization will likely be considered the binding signature location.

The major disadvantage of this option, though, is that adding mere images to the content is not allowed for already signed documents. Thus, this option cannot be used for the second or third signer of a document, but the OP has indicated that the solution will eventually have to allow the documents to be signed to more than one person.

One can consider adding those images as annotations, not as content; for certain types of integrated PDF signatures adding and removing annotations after signing is an allowed operation. But if adding those annotations was allowed, usually removing them again after signing is allowed, too, making those signature images quite volatile.

Multiple signatures (one per page) with a single visualization each

This option does not have the disadvantages of the other options as each visualization corresponds to a different digital signature. Thus, the final one guarantees that the signer is legally bound by the whole document.

It does have disadvantages of its own, though:

  • During verification all these signatures will be verified. This might mean high resource requirements, and even worse ambiguous results (if some verifications fail and some succeed).
  • The Adobe Reader signature panel is flooded with entries.
  • The private key is used multiple times which in case of pay-per-signatures signing services is expensive and in case of SSCDs (in particular smart cards or tokens) might require entering the PIN multiple times and also take quite some time

Implementing the options

iText allows implementing the second and third option in a fairly straight-forward manner out-of-the-box.

Implementing the first option is possible with iText but requires the use of low-level APIs and Java reflection or alternatively patching iText a bit.


Considering the problems of each option, though, I'd advice against doing this at all, one signature right at the end of the content to sign is the least ambiguous way to sign.

这篇关于如何使用iText在所有文档页面中显示数字PDF签名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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