使用itext实现pdf图片签章

使用itext实现pdf图片签章

1:配置环境
首先配置开发环境,本人使用itext5生成pdf,除了,itext5的一些基础包外,org.bouncycastle的两个包是itext生成证书所依赖的包,必须添加,否则会提示ClassNotFound,我百度查到的解决方案,详细见
解决方案

当然网上还有文章说将bcprov-jdk15on-149.jar放到jdk安装目录jre/lib/ext 见 [其他解决思路](http://blog.youkuaiyun.com/moxuelang/article/details/17415147)
只能解决当前问题,还会出现其他类找不到的情况,按照以下只在pom文件中添加依赖的org.bouncycastle包即可。

 
 
    <dependency>
          <groupId>com.itextpdf</groupId>
          <artifactId>itextpdf</artifactId>
          <version>5.5.9</version>
        </dependency>
    
        <dependency>
          <groupId>com.itextpdf</groupId>
          <artifactId>itext-pdfa</artifactId>
          <version>5.5.9</version>
        </dependency>
    
        <dependency>
          <groupId>com.itextpdf</groupId>
          <artifactId>itext-xtra</artifactId>
          <version>5.5.9</version>
        </dependency>
    
        <dependency>
          <groupId>com.itextpdf.tool</groupId>
          <artifactId>xmlworker</artifactId>
          <version>5.5.9</version>
        </dependency>
    
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
     
     

      2:生成p12自签名数字证书
      签章需要用到数字证书,有两种方式,1.去CA申请,2.自己生成,CA端申请的证书的好处是能验证加签证书的有效性,自己生成pdf工具无法验证有效性,但不影响加签效果。
      我们使用 jdk tool 工具实现,只要电脑安装了jdk就可以操作:
      1.keytool支持交互的方式提供证书信息。要生成一个p12证书,必须了解这样几个参数:
      ● -genkeypair 生成证书
      ● -keystore 生成证书的路径和文件名
      ● -storetype 生成的证书类型,使用pkcs12指定p12格式证书
      ● -validity 有效期的天数,用一个足够大的值跳转到2034年
      下面是cmd窗口操作视图样例:
      keytool -genkeypair -keystore test.p12 -storetype pkcs12 -validity 8050
      输入密钥库口令:
      再次输入新口令:

      密钥库口令,就是证书的密码,用于获取密钥,此步设置为 111111
      参考文章如下 http://blog.youkuaiyun.com/cui130/article/details/52487604

      3:编码
      接下来,准备好一个pdf,一个签章用的图片,就可以开始编码了。

      a.创建一个类MainWindow

      public class MainWindow {
      }
       
       

        b.往类中添加常量参数

        public static final String KEYSTORE = "c:\\pdf\\test.p12";
        public static final char[] PASSWORD = "111111".toCharArray();//keystory密码
        public static final String SRC = "c:\\pdf\\zqzr.pdf";
        public static final String DEST = "c:\\pdf\\signed_zqzr.pdf";
         
         

          c.往类中添加加签章方法

          public void sign(String src  //需要签章的pdf文件路径
                      , String dest  // 签完章的pdf文件路径
                      , Certificate[] chain //证书链
                      , PrivateKey pk //签名私钥
                      , String digestAlgorithm  //摘要算法名称,例如SHA-1
                      , String provider  // 密钥算法提供者,可以为null
                      , CryptoStandard subfilter //数字签名格式,itext有2种
                      , String reason  //签名的原因,显示在pdf签名属性中,随便填
                      , String location) //签名的地点,显示在pdf签名属性中,随便填
                              throws GeneralSecurityException, IOException, DocumentException {
                  //下边的步骤都是固定的,照着写就行了,没啥要解释的
                  // Creating the reader and the stamper,开始pdfreader
                  PdfReader reader = new PdfReader(src);
                  //目标文件输出流
                  FileOutputStream os = new FileOutputStream(dest);
                  //创建签章工具PdfStamper ,最后一个boolean参数 
                  //false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
                  //true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
                  PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
                  // 获取数字签章属性对象,设定数字签章的属性
                  PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
                  appearance.setReason(reason);
                  appearance.setLocation(location);
                  //设置签名的位置,页码,签名域名称,多次追加签名的时候,签名预名称不能一样
                  //签名的位置,是图章相对于pdf页面的位置坐标,原点为pdf页面左下角
                  //四个参数的分别是,图章左下角x,图章左下角y,图章右上角x,图章右上角y
                  appearance.setVisibleSignature(new Rectangle(200, 200, 300, 300), 1, "sig1");
                  //读取图章图片,这个image是itext包的image
                  Image image = Image.getInstance("c:\\pdf\\gongzhang.png"); 
                  appearance.setSignatureGraphic(image); 
                  appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
                  //设置图章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)
                  appearance.setRenderingMode(RenderingMode.GRAPHIC);
          
                  // 这里的itext提供了2个用于签名的接口,可以自己实现,后边着重说这个实现
                  // 摘要算法
                  ExternalDigest digest = new BouncyCastleDigest();
                  // 签名算法
                  ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, null);
                  // 调用itext签名方法完成pdf签章
                  MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
              }
           
           

            d.最后编写main方法执行加签章生成加签pdf代码

            public static void main(String[] args)  {
                    try {
                    //读取keystore ,获得私钥和证书链
                        KeyStore ks = KeyStore.getInstance("PKCS12");
                        ks.load(new FileInputStream(KEYSTORE), PASSWORD);
                        String alias = (String)ks.aliases().nextElement();
                        PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
                        Certificate[] chain = ks.getCertificateChain(alias);
                        //new一个上边自定义的方法对象,调用签名方法
                        MainWindow app = new MainWindow();
                        app.sign(SRC, String.format(DEST, 3), chain, pk, DigestAlgorithms.SHA1, null, CryptoStandard.CMS, "pdf", "CN");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        JOptionPane.showMessageDialog(null, e.getMessage());
                        e.printStackTrace();
                    } 
                }
             
             

              以上的路径,根据个人电脑文件路径进行修改,直接执行main方法即可,查看加签成功后的效果为
              这里写图片描述

              执行过程有任何问题先可比较下引入的类是否正确,参照如下:

              import java.io.FileInputStream;
              import java.io.FileOutputStream;
              import java.io.IOException;
              import java.security.GeneralSecurityException;
              import java.security.KeyStore;
              import java.security.PrivateKey;
              import java.security.cert.Certificate;
              
              import javax.swing.JOptionPane;
              
              import com.itextpdf.text.DocumentException;
              import com.itextpdf.text.Image;
              import com.itextpdf.text.Rectangle;
              import com.itextpdf.text.pdf.PdfReader;
              import com.itextpdf.text.pdf.PdfSignatureAppearance;
              import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
              import com.itextpdf.text.pdf.PdfStamper;
              import com.itextpdf.text.pdf.security.BouncyCastleDigest;
              import com.itextpdf.text.pdf.security.DigestAlgorithms;
              import com.itextpdf.text.pdf.security.ExternalDigest;
              import com.itextpdf.text.pdf.security.ExternalSignature;
              import com.itextpdf.text.pdf.security.MakeSignature;
              import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
              import com.itextpdf.text.pdf.security.PrivateKeySignature;
               
               

                最后,感谢全文中参考文章和最后图片的作者,大家在使用过程中有任何问题,请及时留言。

                •                     <li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
                                          <use xlink:href="#csdnc-thumbsup"></use>
                                      </svg><span class="name">点赞</span>
                                      <span class="count">1</span>
                                      </a></li>
                                      <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                                          <use xlink:href="#icon-csdnc-Collection-G"></use>
                                      </svg><span class="name">收藏</span></a></li>
                                      <li class="tool-item tool-active is-share"><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;1582594662_002&quot;}"><svg class="icon" aria-hidden="true">
                                          <use xlink:href="#icon-csdnc-fenxiang"></use>
                                      </svg>分享</a></li>
                                      <!--打赏开始-->
                                                              <!--打赏结束-->
                                                              <li class="tool-item tool-more">
                                          <a>
                                          <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                                          </a>
                                          <ul class="more-box">
                                              <li class="item"><a class="article-report">文章举报</a></li>
                                          </ul>
                                      </li>
                                                          </ul>
                              </div>
                                          </div>
                          <div class="person-messagebox">
                              <div class="left-message"><a href="https://blog.youkuaiyun.com/FU250">
                                  <img src="https://profile.csdnimg.cn/4/8/4/3_fu250" class="avatar_pic" username="FU250">
                                                          <img src="https://g.csdnimg.cn/static/user-reg-year/1x/8.png" class="user-years">
                                                  </a></div>
                              <div class="middle-message">
                                                      <div class="title"><span class="tit"><a href="https://blog.youkuaiyun.com/FU250" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">_再见阿郎_</a></span>
                                                          </div>
                                  <div class="text"><span>发布了79 篇原创文章</span> · <span>获赞 52</span> · <span>访问量 86万+</span></div>
                              </div>
                                              <div class="right-message">
                                                          <a href="https://bbs.youkuaiyun.com/topics/395532067" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-messageboard">他的留言板
                                      </a>
                                                                          <a class="btn btn-sm  bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">关注</a>
                                                  </div>
                                          </div>
                                  </div>
                  </article>
                  
                评论 4
                添加红包

                请填写红包祝福语或标题

                红包个数最小为10个

                红包金额最低5元

                当前余额3.43前往充值 >
                需支付:10.00
                成就一亿技术人!
                领取后你会自动成为博主和红包主的粉丝 规则
                hope_wisdom
                发出的红包
                实付
                使用余额支付
                点击重新获取
                扫码支付
                钱包余额 0

                抵扣说明:

                1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                余额充值