依赖jar包

itextpdf-5.5.10

pdfbox-app-2.0.7.jar

 生成p12证书

 到java bin目录执行

keytool -genkey -v -alias root -keyalg RSA -storetype PKCS12 -keystore dlt.p12 -dname "CN=www.handtimes.com,OU=ipcc,O=云电同方,L=昆明,ST=云南,C=中国" -storepass ipcc@95598 -keypass ipcc@95598

测试Controller

@GetMapping("/preview/{xy}")
    public Boolean preview(@PathVariable("xy")String xy) throws Exception {
        String SRC="C:\\Users\\zfj\\Desktop\\awc.pdf" ;//原始pdf
        String DEST2="C:\\Users\\zfj\\Desktop\\demo_signed_itext4.pdf" ;//签名完成的pdf
        Integer x = Integer.valueOf(xy.split("-")[0]);
        Integer y = Integer.valueOf(xy.split("-")[1]);
        PdfSignatureUtil.genderPdfwatermark(SRC,DEST2,"曾凡杰",x,y);
        return true;
    }

  工具类

package com.gpg.utils;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.springframework.core.io.ClassPathResource;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;

/**
 * pdf 相关操作工具类
 * @author Administrator
 *
 */
public class PdfSignatureUtil {

    private static String watermarkText = "上海xx科技有限公司";

    private static String simsunttc = "";


    /**
     * 创建PDF
     * @param pdfPath PDF路径
     * @param dest 输入的路径
     * @param userName  签名的名字
     * @return
     * @throws Exception
     */
    public static boolean genderPdfwatermark(String pdfPath,String dest,String userName,int x,int y) throws Exception {

        // 加密文件
        ClassPathResource classPathResource = new ClassPathResource("static/dlt.p12");
        InputStream p12Stream =classPathResource.getInputStream();

        // 字体文件
        ClassPathResource simsunClassPathResource = new ClassPathResource("static/simsun.ttc");
        InputStream simsunInputstream = simsunClassPathResource.getInputStream();

        // 临时文件
        File simsunttcTempFile = File.createTempFile("simsunttc", ".ttc");
        inputStream2File(simsunInputstream,simsunttcTempFile);
        simsunttc = simsunttcTempFile.getAbsolutePath()+",1";

        // 密码
        char[] PASSWORD = "ipcc@95598".toCharArray();
        String reason="";
        String location="";

        // 文字转图片
        int width = 200;
        int heigth = 150;
        Font font = new Font("微软雅黑", Font.ROMAN_BASELINE, 33);//字体
        BufferedImage bi1 = ImageUtil.waterMarkByText(width, heigth, userName, Color.GRAY, font, 0d,
                1.0f);//给图片添加文字水印

        File nameImg = File.createTempFile("nameImg", ".jpg");
        ImageIO.write(bi1, "png", nameImg);//写入文件
        // pdf水印生成
        sign(new FileInputStream(pdfPath), new FileOutputStream(dest),
                p12Stream, PASSWORD,
                reason, location, nameImg.getAbsolutePath(),x, y, 250, 250);

        nameImg.deleteOnExit();
        simsunttcTempFile.deleteOnExit();
        return true;
    }

    /**
     * pdf 水纹印签章
     * 注意:签章的图片底色必须是透明色,否者签章出来会遮盖pdf中的部分
     * @param src	需要签章的pdf文件路径输入流
     * @param dest	签名完成后pdf存放路径输出流
     * @param p12Stream		p12路径
     * @param password		获取keystory私钥的密码(每个私钥根据创建时生成不同而不同)
     * @param reason		传入空字符串
     * @param location		传入空字符串
     * @param chapterPath   水纹印图片路径
     * @param x				在pdf中横坐标
     * @param y				在pdf中纵坐标
     * @param m				水纹印图片长
     * @param z				水纹印图片宽
     * @throws Exception
     */
    public static void sign(InputStream src, OutputStream dest, InputStream p12Stream, char[] password, String reason , String location,
                            String chapterPath ,int x,int y,int m,int z)throws Exception {
        KeyStore ks = KeyStore.getInstance("jks");
        ks.load(p12Stream, password);
        String alias = (String)ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
        Certificate[] chain = ks.getCertificateChain(alias);

        PdfReader reader = new PdfReader(src);


        //目标文件输出流
        //创建签章工具PdfStamper ,最后一个boolean参数
        //false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
        //true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
        PdfStamper stamper = PdfStamper.createSignature(reader, dest, '\0', null, false);


        Rectangle pageSize = reader.getPageSize(1);
        float height = pageSize.getHeight();
        float width = pageSize.getWidth();



        for(int i = 1;i<=reader.getNumberOfPages();i++){
            PdfContentByte overContent = stamper.getOverContent(i);
            overContent.setColorFill(BaseColor.RED);
            BaseFont Chinese = BaseFont.createFont(simsunttc, BaseFont.IDENTITY_H,
                    BaseFont.EMBEDDED);

            overContent.setFontAndSize(Chinese, 15F);
            PdfGState pdfGState = new PdfGState();
            pdfGState.setFillOpacity(0.2f);
            overContent.setGState(pdfGState);

            int ysize = 50;
            int xsize = 50;
            for(int j=0;j<100;j++){
                if(height <= ysize){
                    xsize += 150;
                    ysize = 50;
                }
                overContent.showTextAligned(Element.ALIGN_CENTER, watermarkText, xsize, ysize, 20);
                ysize += 100;

            }
        }



        // 获取数字签章属性对象,设定数字签章的属性
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason(reason);
        appearance.setLocation(location);
        //设置签名的位置,页码,签名域名称,多次追加签名的时候,签名预名称不能一样
        int pageCount = reader.getNumberOfPages();
        //签名的位置,是图章相对于pdf页面的位置坐标,原点为pdf页面左下角
        //四个参数的分别是,图章左下角x,图章左下角y,图章右上角x,图章右上角y
        appearance.setVisibleSignature(new Rectangle(x, y, m, z), pageCount, "sig1");
        //读取图章图片,这个image是itext包的image
        Image image = Image.getInstance(chapterPath);
        appearance.setSignatureGraphic(image);
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
        //设置图章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)
        appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

        // 这里的itext提供了2个用于签名的接口,可以自己实现,后边着重说这个实现
        // 摘要算法
        ExternalDigest digest = new BouncyCastleDigest();
        // 签名算法
        ExternalSignature signature = new PrivateKeySignature(pk, DigestAlgorithms.SHA1, null);
        // 调用itext签名方法完成pdf签章CryptoStandard.CMS 签名方式,建议采用这种
        MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
    }
    /**
     * 将inputStream转化为file
     * @param is
     * @param file 要输出的文件目录
     */
    public static void inputStream2File (InputStream is, File file) throws IOException {
        OutputStream os = null;
        try {
            os = new FileOutputStream(file);
            int len = 0;
            byte[] buffer = new byte[8192];

            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } finally {
            os.close();
            is.close();
        }
    }

}