溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ava中怎么對PDF文件進(jìn)行電子簽章

發(fā)布時(shí)間:2021-07-13 10:26:37 來源:億速云 閱讀:209 作者:Leah 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)ava中怎么對PDF文件進(jìn)行電子簽章,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

    一、 概述

    印章是我國特有的歷史文化產(chǎn)物,古代主要用作身份憑證和行駛職權(quán)的工具。它的起源是由于社會生活的實(shí)際需要。早在商周時(shí)代,印章就已經(jīng)產(chǎn)生。如今的印章已成為一種獨(dú)特的,融實(shí)用性和藝術(shù)性為一體的藝術(shù)瑰寶。傳統(tǒng)的印章容易被壞人、小人私刻;從而新聞鮮有報(bào)道某某私刻公章,侵吞國家財(cái)產(chǎn)。隨著計(jì)算機(jī)技術(shù)、加密技術(shù)及圖像處理技術(shù)的發(fā)展,出現(xiàn)了電子簽章。電子簽章是電子簽名的一種表現(xiàn)形式,利用圖像處理技術(shù)、數(shù)字加密技術(shù)將電子簽名操作轉(zhuǎn)化為與紙質(zhì)文件蓋章操作相同的可視效果,同時(shí)利用電子簽名技術(shù)保障電子信息的真實(shí)性和完整性以及簽名人的不可否認(rèn)性。

    電子簽章與數(shù)字證書一樣是身份驗(yàn)證的一種手段,泛指所有以電子形式存在,依附在電子文件并與其邏輯關(guān)聯(lián),可用以辨識電子文件簽署者身份,保證文件的完整性,并表示簽署者同意電子文件所陳述事實(shí)的內(nèi)容。一般來說對電子簽章的認(rèn)定都是從技術(shù)角度而言的。主要是指通過特定的技術(shù)方案來鑒別當(dāng)事人的身份及確保電子資料內(nèi)容不被篡改的安全保障措施。電子簽章常于發(fā)送安全電子郵件、訪問安全站點(diǎn)、網(wǎng)上招標(biāo)投標(biāo)、網(wǎng)上簽約、安全網(wǎng)上公文傳送、公司合同、電子處方箋等。

    電子簽章是一個(gè)很復(fù)雜的問題,大到有相關(guān)的電子簽章系統(tǒng);今天分享一下如何把電子簽章應(yīng)用到電子處方箋的PDF文件里。

    二、 技術(shù)選型

    目前主流處理PDF文件兩個(gè)jar包分別是:

    • 開源組織Apache的PDFBox,官網(wǎng)https://pdfbox.apache.org/

    • 大名鼎鼎adobe公司的iText,官網(wǎng)https://itextpdf.com/tags/adobe,其中iText又分為iText5和iText7

    如何在PDFBox、iText5和iText7選出合適自己項(xiàng)目的技術(shù)呢?

    對比PDFBox、iText5和iText7這三者:

    1. PDFBox的功能相對較弱,iText5和iText7的功能非常強(qiáng)悍;

    2. iText5的資料網(wǎng)上相對較多,如果出現(xiàn)問題容易找到解決方案;PDFBox和iText7的網(wǎng)上資料相對較少,如果出現(xiàn)問題不易找到相關(guān)解決方案;

    3. 通過閱讀PDFBox代碼目前PDFBox還沒提供自定義簽章的相關(guān)接口;iText5和iText7提供了處理自定義簽章的相關(guān)實(shí)現(xiàn);

    4. PDFBox只能實(shí)現(xiàn)把簽章圖片加簽到PDF文件;iText5和iText7除了可以把簽章圖片加簽到PDF文件,還可以實(shí)現(xiàn)直接對簽章進(jìn)行繪制,把文件繪制到簽章上。

    5. PDFBox和iText5/iText7使用的協(xié)議不一樣。PDFBox使用的是APACHE LICENSE VERSION 2.0(https://www.apache.org/licenses/);iText5/iText7使用的是AGPL(https://itextpdf.com/agpl)。PDFBox免費(fèi)使用,AGPL商用收費(fèi)

    本分享JAVA對PDF文件進(jìn)行電子簽章需要實(shí)現(xiàn)的功能:

    1. 生成證書。與PDFBox、iText5和iText7技術(shù)無關(guān)

    2. 按模板輸出PDF文件:PDFBox、iText5和iText7都可以完成,但是PDFBox會遇到中文亂碼比較棘手的問題

    3. 在PDF文件中實(shí)現(xiàn)把簽章圖片加簽到PDF文件:PDFBox、iText5和iText7都可以實(shí)現(xiàn),沒有很多的區(qū)別

    4. 在PDF文件中繪制簽章:iText5和iText7都可以實(shí)現(xiàn),PDFBox目前不支持

    5. 在PDF文件中生成高清簽章:iText5和iText7都可以實(shí)現(xiàn),PDFBox目前不支持

    6. 在PDF文件中進(jìn)行多次簽名::PDFBox、iText5和iText7都可以完成,沒有區(qū)別

    通過相關(guān)技術(shù)分析和要實(shí)現(xiàn)的功能分析,采用iText5進(jìn)行開發(fā),唯一遺憾的是iText商用收費(fèi);但是這不是做技術(shù)需要關(guān)心的?。∵x用iText5的理由:

    • 使用iText5能實(shí)現(xiàn)全部的功能

    • 如何在開發(fā)中遇到相關(guān)問題,容易找到相應(yīng)解決方案

    三、 生成一個(gè)圖片簽章

    1. 生成一個(gè)如下圖的簽章圖片

    ava中怎么對PDF文件進(jìn)行電子簽章

    2. 相關(guān)代碼

    import java.awt.Color;
        import java.awt.Font;
        import java.awt.FontMetrics;
        import java.awt.Graphics2D;
        import java.awt.RenderingHints;
        import java.awt.image.BufferedImage;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import sun.font.FontDesignMetrics;
     
        import com.sun.image.codec.jpeg.JPEGCodec;
        import com.sun.image.codec.jpeg.JPEGEncodeParam;
        import com.sun.image.codec.jpeg.JPEGImageEncoder;
     
        public class SignImage {
     
        /**
         * @param doctorName
         *            String 醫(yī)生名字
         * @param hospitalName
         *            String 醫(yī)生名稱
         * @param date
         *            String 簽名日期
         *            圖片高度
         * @param jpgname
         *            String jpg圖片名
         * @return
         */
        public static boolean createSignTextImg(
                String doctorName, //
                String hospitalName, //
                String date, 
                String jpgname) {
            int width = 255;
            int height = 100;
            FileOutputStream out = null;
            //背景色
            Color bgcolor = Color.WHITE;
            //字色
            Color fontcolor = Color.RED;
            Font doctorNameFont = new Font(null, Font.BOLD, 20);
            Font othorTextFont = new Font(null, Font.BOLD, 18);
            try { // 寬度 高度
                BufferedImage bimage = new BufferedImage(width, height,
                        BufferedImage.TYPE_INT_RGB);
                Graphics2D g = bimage.createGraphics();
                g.setColor(bgcolor); // 背景色
                g.fillRect(0, 0, width, height); // 畫一個(gè)矩形
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON); // 去除鋸齒(當(dāng)設(shè)置的字體過大的時(shí)候,會出現(xiàn)鋸齒)
     
                g.setColor(Color.RED);
                g.fillRect(0, 0, 8, height);
                g.fillRect(0, 0, width, 8);
                g.fillRect(0, height - 8, width, height);
                g.fillRect(width - 8, 0, width, height);
     
                g.setColor(fontcolor); // 字的顏色
                g.setFont(doctorNameFont); // 字體字形字號
                FontMetrics fm = FontDesignMetrics.getMetrics(doctorNameFont);
                int font1_Hight = fm.getHeight();
                int strWidth = fm.stringWidth(doctorName);
                int y = 35;
                int x = (width - strWidth) / 2;
                g.drawString(doctorName, x, y); // 在指定坐標(biāo)除添加文字
     
                g.setFont(othorTextFont); // 字體字形字號
     
                fm = FontDesignMetrics.getMetrics(othorTextFont);
                int font2_Hight = fm.getHeight();
                strWidth = fm.stringWidth(hospitalName);
                x = (width - strWidth) / 2;
                g.drawString(hospitalName, x, y + font1_Hight); // 在指定坐標(biāo)除添加文字
     
                strWidth = fm.stringWidth(date);
                x = (width - strWidth) / 2;
                g.drawString(date, x, y + font1_Hight + font2_Hight); // 在指定坐標(biāo)除添加文字
     
                g.dispose();
                out = new FileOutputStream(jpgname); // 指定輸出文件
                JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
                JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage);
                param.setQuality(50f, true);
                encoder.encode(bimage, param); // 存盤
                out.flush();
                return true;
            } catch (Exception e) {
                return false;
            }finally{
                if(out!=null){
                    try {
                        out.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
        public static void main(String[] args) {
            createSignTextImg("華佗", "在線醫(yī)院", "2018.01.01",   "sign.jpg");
        }
    }

    四、 如何按模板生成PDF文件

    1. 制作PDF模板

    目前PDF模板工具別無他物,只能使用偉大的Adobe公司提供的Adobe Acrobatpro DC軟件進(jìn)行制作。如何使用該軟件這里就不多說了,如果在使用中遇到什么可以另外咨詢。

    2. 制作一個(gè)如下圖的PDF模板,該模板是帶有PDF的表單域的

    ava中怎么對PDF文件進(jìn)行電子簽章

    五、 如何生成PKCS12證書

    1. PKCS的簡單介紹

    PKCS:The Public-Key Cryptography Standards (簡稱PKCS)是由美國RSA數(shù)據(jù)安全公司及其合作伙伴制定的一組公鑰密碼學(xué)標(biāo)準(zhǔn),其中包括證書申請、證書更新、證書作廢表發(fā)布、擴(kuò)展證書內(nèi)容以及數(shù)字簽名、數(shù)字信封的格式等方面的一系列相關(guān)協(xié)議。

    到1999年底,PKCS已經(jīng)公布了以下標(biāo)準(zhǔn):

    • PKCS#1:定義RSA公開密鑰算法加密和簽名機(jī)制,主要用于組織PKCS#7中所描述的數(shù)字簽名和數(shù)字信封[22]。

    • PKCS#3:定義Diffie-Hellman密鑰交換協(xié)議[23]。

    • PKCS#5:描述一種利用從口令派生出來的安全密鑰加密字符串的方法。使用MD2或MD5 從口令中派生密鑰,并采用DES-CBC模式加密。主要用于加密從一個(gè)計(jì)算機(jī)傳送到另一個(gè)計(jì)算機(jī)的私人密鑰,不能用于加密消息[24]。

    • PKCS#6:描述了公鑰證書的標(biāo)準(zhǔn)語法,主要描述X.509證書的擴(kuò)展格式[25]。

    • PKCS#7:定義一種通用的消息語法,包括數(shù)字簽名和加密等用于增強(qiáng)的加密機(jī)制,PKCS#7與PEM兼容,所以不需其他密碼操作,就可以將加密的消息轉(zhuǎn)換成PEM消息[26]。

    • PKCS#8:描述私有密鑰信息格式,該信息包括公開密鑰算法的私有密鑰以及可選的屬性集等[27]。

    • PKCS#9:定義一些用于PKCS#6證書擴(kuò)展、PKCS#7數(shù)字簽名和PKCS#8私鑰加密信息的屬性類型[28]。

    • PKCS#10:描述證書請求語法[29]。

    • PKCS#11:稱為Cyptoki,定義了一套獨(dú)立于技術(shù)的程序設(shè)計(jì)接口,用于智能卡和PCMCIA卡之類的加密設(shè)備[30]。

    • PKCS#12:描述個(gè)人信息交換語法標(biāo)準(zhǔn)。描述了將用戶公鑰、私鑰、證書和其他相關(guān)信息打包的語法[31]。

    • PKCS#13:橢圓曲線密碼體制標(biāo)準(zhǔn)[32]。

    • PKCS#14:偽隨機(jī)數(shù)生成標(biāo)準(zhǔn)。

    • PKCS#15:密碼令牌信息格式標(biāo)準(zhǔn)[33]。

    PKCS12也就是以上標(biāo)準(zhǔn)的PKCS#12,主要用來描述個(gè)人身份信息;本次分享中要進(jìn)行簽章操作的是醫(yī)生和藥師,他們就是一個(gè)個(gè)人主體,給他們分配一個(gè)PKCS12的證書,就等于給他們分配了一個(gè)用于蓋章的印章。

    2. 使用JAVA生成一個(gè)PKCS12證書并進(jìn)行存貯,相關(guān)分析見代碼注解

    public class Extension {
     
            private String oid;
     
            private boolean critical;
     
            private byte[] value;
     
            public String getOid() {
                return oid;
            }
     
            public byte[] getValue() {
                return value;
            }
            public boolean isCritical() {
                return critical;
            }
        }
     
     
        import java.io.ByteArrayInputStream;
        import java.io.ByteArrayOutputStream;
        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.math.BigInteger;
        import java.security.KeyPair;
        import java.security.KeyPairGenerator;
        import java.security.KeyStore;
        import java.security.NoSuchAlgorithmException;
        import java.security.PrivateKey;
        import java.security.PublicKey;
        import java.security.SecureRandom;
        import java.security.cert.Certificate;
        import java.security.cert.CertificateFactory;
        import java.security.cert.X509Certificate;
        import java.text.SimpleDateFormat;
        import java.util.Calendar;
        import java.util.Date;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
        import java.util.Random;
     
        import org.bouncycastle.asn1.ASN1ObjectIdentifier;
        import org.bouncycastle.asn1.ASN1Primitive;
        import org.bouncycastle.asn1.x500.X500Name;
        import org.bouncycastle.asn1.x509.BasicConstraints;
        import org.bouncycastle.asn1.x509.CRLDistPoint;
        import org.bouncycastle.asn1.x509.DistributionPoint;
        import org.bouncycastle.asn1.x509.DistributionPointName;
        import org.bouncycastle.asn1.x509.GeneralName;
        import org.bouncycastle.asn1.x509.GeneralNames;
        import org.bouncycastle.asn1.x509.KeyUsage;
        import org.bouncycastle.cert.X509CertificateHolder;
        import org.bouncycastle.cert.X509v3CertificateBuilder;
        import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
        import org.bouncycastle.jce.provider.BouncyCastleProvider;
        import org.bouncycastle.operator.ContentSigner;
        import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
     
        public class Pkcs {
     
        private static KeyPair getKey() throws NoSuchAlgorithmException {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA",
                    new BouncyCastleProvider());
            generator.initialize(1024);
            // 證書中的密鑰 公鑰和私鑰
            KeyPair keyPair = generator.generateKeyPair();
            return keyPair;
        }
     
        /**
         * @param password
         *            密碼
         * @param issuerStr 頒發(fā)機(jī)構(gòu)信息
         * 
         * @param subjectStr 使用者信息
         * 
        * @param certificateCRL 頒發(fā)地址
         * 
         * @return
         */
        public static Map<String, byte[]> createCert(String password,
                String issuerStr, String subjectStr, String certificateCRL) {
            Map<String, byte[]> result = new HashMap<String, byte[]>();
            ByteArrayOutputStream out = null;
            try {
                // 生成JKS證書
                // KeyStore keyStore = KeyStore.getInstance("JKS");
                // 標(biāo)志生成PKCS12證書
                KeyStore keyStore = KeyStore.getInstance("PKCS12",
                        new BouncyCastleProvider());
                keyStore.load(null, null);
                KeyPair keyPair = getKey();
                // issuer與 subject相同的證書就是CA證書
                Certificate cert = generateCertificateV3(issuerStr, subjectStr,
                        keyPair, result, certificateCRL, null);
                // cretkey隨便寫,標(biāo)識別名
                keyStore.setKeyEntry("cretkey", keyPair.getPrivate(),
                        password.toCharArray(), new Certificate[] { cert });
                out = new ByteArrayOutputStream();
                cert.verify(keyPair.getPublic());
                keyStore.store(out, password.toCharArray());
                byte[] keyStoreData = out.toByteArray();
                result.put("keyStoreData", keyStoreData);
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                    }
                }
            }
            return result;
        }
     
        /**
         * @param issuerStr
         * @param subjectStr
         * @param keyPair
         * @param result
         * @param certificateCRL
         * @param extensions
         * @return
         */
        public static Certificate generateCertificateV3(String issuerStr,
                String subjectStr, KeyPair keyPair, Map<String, byte[]> result,
                String certificateCRL, List<Extension> extensions) {
            ByteArrayInputStream bout = null;
            X509Certificate cert = null;
            try {
                PublicKey publicKey = keyPair.getPublic();
                PrivateKey privateKey = keyPair.getPrivate();
                Date notBefore = new Date();
                Calendar rightNow = Calendar.getInstance();
                rightNow.setTime(notBefore);
                // 日期加1年
                rightNow.add(Calendar.YEAR, 1);
                Date notAfter = rightNow.getTime();
                // 證書序列號
                BigInteger serial = BigInteger.probablePrime(256, new Random());
                X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
                        new X500Name(issuerStr), serial, notBefore, notAfter,
                        new X500Name(subjectStr), publicKey);
                JcaContentSignerBuilder jBuilder = new JcaContentSignerBuilder(
                        "SHA1withRSA");
                SecureRandom secureRandom = new SecureRandom();
                jBuilder.setSecureRandom(secureRandom);
                ContentSigner singer = jBuilder.setProvider(
                        new BouncyCastleProvider()).build(privateKey);
                // 分發(fā)點(diǎn)
                ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier(
                        "2.5.29.31");
                GeneralName generalName = new GeneralName(
                        GeneralName.uniformResourceIdentifier, certificateCRL);
                GeneralNames seneralNames = new GeneralNames(generalName);
                DistributionPointName distributionPoint = new DistributionPointName(
                        seneralNames);
                DistributionPoint[] points = new DistributionPoint[1];
                points[0] = new DistributionPoint(distributionPoint, null, null);
                CRLDistPoint cRLDistPoint = new CRLDistPoint(points);
                builder.addExtension(cRLDistributionPoints, true, cRLDistPoint);
                // 用途
                ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier(
                        "2.5.29.15");
                // | KeyUsage.nonRepudiation | KeyUsage.keyCertSign
                builder.addExtension(keyUsage, true, new KeyUsage(
                        KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
                // 基本限制 X509Extension.java
                ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier(
                        "2.5.29.19");
                builder.addExtension(basicConstraints, true, new BasicConstraints(
                        true));
                // privKey:使用自己的私鑰進(jìn)行簽名,CA證書
                if (extensions != null)
                    for (Extension ext : extensions) {
                        builder.addExtension(
                                new ASN1ObjectIdentifier(ext.getOid()),
                                ext.isCritical(),
                                ASN1Primitive.fromByteArray(ext.getValue()));
                    }
                X509CertificateHolder holder = builder.build(singer);
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                bout = new ByteArrayInputStream(holder.toASN1Structure()
                        .getEncoded());
                cert = (X509Certificate) cf.generateCertificate(bout);
                byte[] certBuf = holder.getEncoded();
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                // 證書數(shù)據(jù)
                result.put("certificateData", certBuf);
                //公鑰
                result.put("publicKey", publicKey.getEncoded());
                //私鑰
                result.put("privateKey", privateKey.getEncoded());
                //證書有效開始時(shí)間
                result.put("notBefore", format.format(notBefore).getBytes("utf-8"));
                //證書有效結(jié)束時(shí)間
                result.put("notAfter", format.format(notAfter).getBytes("utf-8"));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (bout != null) {
                    try {
                        bout.close();
                    } catch (IOException e) {
                    }
                }
            }
            return cert;
        }
     
        public static void main(String[] args) throws Exception{
            // CN: 名字與姓氏    OU : 組織單位名稱
            // O :組織名稱  L : 城市或區(qū)域名稱  E : 電子郵件
            // ST: 州或省份名稱  C: 單位的兩字母國家代碼 
            String issuerStr = "CN=在線醫(yī)院,OU=gitbook研發(fā)部,O=gitbook有限公司,C=CN,E=gitbook@sina.com,L=北京,ST=北京";
            String subjectStr = "CN=huangjinjin,OU=gitbook研發(fā)部,O=gitbook有限公司,C=CN,E=huangjinjin@sina.com,L=北京,ST=北京";
            String certificateCRL  = "https://gitbook.cn";
            Map<String, byte[]> result = createCert("123456", issuerStr, subjectStr, certificateCRL);
     
            FileOutputStream outPutStream = new FileOutputStream("c:/keystore.p12"); // ca.jks
            outPutStream.write(result.get("keyStoreData"));
            outPutStream.close();
            FileOutputStream fos = new FileOutputStream(new File("c:/keystore.cer"));
            fos.write(result.get("certificateData"));
            fos.flush();
            fos.close();
        }
        }

    六、 如何生成一個(gè)高清晰的簽章

    1. 由PDF模板生成一個(gè)PDF文件,見代碼注解

    import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.OutputStream;
        import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.Iterator;
        import java.util.List;
        import java.util.Map;
        import com.itextpdf.text.DocumentException;
        import com.itextpdf.text.pdf.AcroFields;
        import com.itextpdf.text.pdf.AcroFields.Item;
        import com.itextpdf.text.pdf.BaseFont;
        import com.itextpdf.text.pdf.PdfReader;
        import com.itextpdf.text.pdf.PdfStamper;
     
        public class PDFUtils {
     
     
        /**
         * @param fields
         * @param data
         * @throws IOException
         * @throws DocumentException
         */
        private static void fillData(AcroFields fields, Map<String, String> data) throws IOException, DocumentException {
            List<String> keys = new ArrayList<String>();
            Map<String, Item> formFields = fields.getFields();
            for (String key : data.keySet()) {
                if(formFields.containsKey(key)){
                    String value = data.get(key);
                    fields.setField(key, value); // 為字段賦值,注意字段名稱是區(qū)分大小寫的
                    keys.add(key);
                }
            }
            Iterator<String> itemsKey = formFields.keySet().iterator();
            while(itemsKey.hasNext()){
                String itemKey = itemsKey.next();
                if(!keys.contains(itemKey)){
                    fields.setField(itemKey, " ");
                }
            }
        }
     
        /**
         * @param templatePdfPath
         *            模板pdf路徑
         * @param generatePdfPath
         *            生成pdf路徑
         * @param data
         *            數(shù)據(jù)
         */
        public static String generatePDF(String templatePdfPath, String generatePdfPath, Map<String, String> data) {
            OutputStream fos = null;
            ByteArrayOutputStream bos = null;
            try {
                PdfReader reader = new PdfReader(templatePdfPath);
                bos = new ByteArrayOutputStream();
                /* 將要生成的目標(biāo)PDF文件名稱 */
                PdfStamper ps = new PdfStamper(reader, bos);
                /* 使用中文字體 */
                BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
                ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
                fontList.add(bf);
                /* 取出報(bào)表模板中的所有字段 */
                AcroFields fields = ps.getAcroFields();
                fields.setSubstitutionFonts(fontList);
                fillData(fields, data);
                /* 必須要調(diào)用這個(gè),否則文檔不會生成的  如果為false那么生成的PDF文件還能編輯,一定要設(shè)為true*/
                ps.setFormFlattening(true);
                ps.close();
                fos = new FileOutputStream(generatePdfPath);
                fos.write(bos.toByteArray());
                fos.flush();
                return generatePdfPath;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }
     
        public static void main(String[] args) {
            Map<String, String> data = new HashMap<String, String>();
            //key為pdf模板的form表單的名字,value為需要填充的值
            data.put("title", "在線醫(yī)院");
            data.put("case", "123456789");
            data.put("date", "2018.12.07");
            data.put("name", "gitbook");
            data.put("sex", "男");
            data.put("age", "29");
            data.put("phone", "13711645814");
            data.put("office", "內(nèi)科");
            data.put("cert", "身癢找打");
            data.put("drug", "1、奧美拉唑腸溶膠囊             0.25g10?!?板 ");
            data.put("dose", "×2盒");
            data.put("cons", "用法用量:口服 一日兩次 一次2粒");
            data.put("tips", "溫馨提示");
            data.put("desc", "盡量呆在通風(fēng)較好的地方,保持空氣流通,有利于病情康復(fù)。盡量呆在通風(fēng)較好的地方");
            generatePDF("C:\\Users\\zhilin\\Desktop\\chat\\tpl.pdf", 
                    "C:\\Users\\zhilin\\Desktop\\chat\\filled.pdf", data );
        }
        }

    ava中怎么對PDF文件進(jìn)行電子簽章

    2. 對PDF文件進(jìn)行簽章

    經(jīng)過過上面的代碼可以生成一個(gè)名為sign.jpg的簽章圖片,生成一個(gè)keystore.p12的證書文件,還有一個(gè)已經(jīng)通過模板填充了表單的名為filled.pdf的pdf文件。下面就可通過以上材料生成一個(gè)簽名的PDF文件。

    import java.io.File;
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.security.KeyStore;
        import java.security.PrivateKey;
        import java.security.Security;
        import java.security.cert.Certificate;
        import java.util.UUID;
     
        import org.bouncycastle.jce.provider.BouncyCastleProvider;
     
        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;
     
     
        public class SignPdf {
        /**
         * @param password
         *            秘鑰密碼
         * @param keyStorePath
         *            秘鑰文件路徑
         * @param signPdfSrc
         *            簽名的PDF文件
         * @param signImage
         *            簽名圖片文件
         * @param x
         *            x坐標(biāo)
         * @param y
         *            y坐標(biāo)
         * @return
         */
        public static byte[] sign(String password, String keyStorePath, String signPdfSrc, String signImage,
                float x, float y) {
            File signPdfSrcFile = new File(signPdfSrc);
            PdfReader reader = null;
            ByteArrayOutputStream signPDFData = null;
            PdfStamper stp = null;
            FileInputStream fos = null;
            try {
                BouncyCastleProvider provider = new BouncyCastleProvider();
                Security.addProvider(provider);
                KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
                fos = new FileInputStream(keyStorePath);
                // 私鑰密碼 為Pkcs生成證書是的私鑰密碼 123456
                ks.load(fos, password.toCharArray()); 
                String alias = (String) ks.aliases().nextElement();
                PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
                Certificate[] chain = ks.getCertificateChain(alias);
                reader = new PdfReader(signPdfSrc);
                signPDFData = new ByteArrayOutputStream();
                // 臨時(shí)pdf文件
                File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
                stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
                 stp.setFullCompression();
                PdfSignatureAppearance sap = stp.getSignatureAppearance();
                sap.setReason("數(shù)字簽名,不可改變");
                // 使用png格式透明圖片
                Image image = Image.getInstance(signImage);
                sap.setImageScale(0);
                sap.setSignatureGraphic(image);
                sap.setRenderingMode(RenderingMode.GRAPHIC);
                // 是對應(yīng)x軸和y軸坐標(biāo)
                sap.setVisibleSignature(new Rectangle(x, y, x + 185, y + 68), 1,
                        UUID.randomUUID().toString().replaceAll("-", ""));
                stp.getWriter().setCompressionLevel(5);
                ExternalDigest digest = new BouncyCastleDigest();
                ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
                MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
                stp.close();
                reader.close();
                return signPDFData.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
     
                if (signPDFData != null) {
                    try {
                        signPDFData.close();
                    } catch (IOException e) {
                    }
                }
     
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                    }
                }
            }
            return null;
        }
     
        public static void main(String[] args) throws Exception {
            byte[] fileData = sign("123456", "C:\\Users\\zhilin\\Desktop\\chat\\keystore.p12", //
                    "C:\\Users\\zhilin\\Desktop\\chat\\filled.pdf",//
                    "C:\\Users\\zhilin\\Desktop\\chat\\sign.jpg", 100, 290);
            FileOutputStream f = new FileOutputStream(new File("C:\\Users\\zhilin\\Desktop\\chat\\signed.pdf"));
            f.write(fileData);
            f.close();
        }
        }

    ava中怎么對PDF文件進(jìn)行電子簽章

    3. 高清簽章

    高清簽章是通過iText的繪制功能來完成。主要直接在PDF文件中繪制簽章,代碼實(shí)現(xiàn)如下:

    import java.io.File;
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.security.KeyStore;
        import java.security.PrivateKey;
        import java.security.Security;
        import java.security.cert.Certificate;
        import org.bouncycastle.jce.provider.BouncyCastleProvider;
     
        import com.itextpdf.awt.AsianFontMapper;
        import com.itextpdf.text.BaseColor;
        import com.itextpdf.text.Element;
        import com.itextpdf.text.Font;
        import com.itextpdf.text.Paragraph;
        import com.itextpdf.text.Rectangle;
        import com.itextpdf.text.pdf.BaseFont;
        import com.itextpdf.text.pdf.ColumnText;
        import com.itextpdf.text.pdf.PdfReader;
        import com.itextpdf.text.pdf.PdfSignatureAppearance;
        import com.itextpdf.text.pdf.PdfStamper;
        import com.itextpdf.text.pdf.PdfStream;
        import com.itextpdf.text.pdf.PdfTemplate;
        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;
     
     
        public class SignHighPdf {
     
        /**
         * @param password
         *            秘鑰密碼
         * @param keyStorePath
         *            秘鑰文件路徑
         * @param signPdfSrc
         *            簽名的PDF文件
         * @param x
         * 
         * @param y
         * @return
         */
        public static byte[] sign(String password, String keyStorePath, String signPdfSrc, 
                float x, float y,
                String signText) {
            File signPdfSrcFile = new File(signPdfSrc);
            PdfReader reader = null;
            ByteArrayOutputStream signPDFData = null;
            PdfStamper stp = null;
            FileInputStream fos = null;
            try {
                BouncyCastleProvider provider = new BouncyCastleProvider();
                Security.addProvider(provider);
                KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
                fos = new FileInputStream(keyStorePath);
                ks.load(fos, password.toCharArray()); // 私鑰密碼
                String alias = (String) ks.aliases().nextElement();
                PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
                Certificate[] chain = ks.getCertificateChain(alias);
                reader = new PdfReader(signPdfSrc);
                signPDFData = new ByteArrayOutputStream();
                // 臨時(shí)pdf文件
                File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
                stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
                PdfSignatureAppearance sap = stp.getSignatureAppearance();
                sap.setReason("數(shù)字簽名,不可改變");
                // 是對應(yīng)x軸和y軸坐標(biāo)
                sap.setVisibleSignature(new Rectangle(x, y, x + 150, y + 65), 1,
                        "sr"+String.valueOf(System.nanoTime()));
                /layer 0 Creating the appearance for layer 0
                PdfTemplate n0 = sap.getLayer(0);
                n0.reset();
                float lx = n0.getBoundingBox().getLeft();
                float by = n0.getBoundingBox().getBottom();
                float width = n0.getBoundingBox().getWidth();
                float height = n0.getBoundingBox().getHeight();
                n0.setRGBColorFill(255, 0, 0);
                n0.rectangle(lx, by, 5, height);
                n0.rectangle(lx, by, width, 5);
                n0.rectangle(lx, by+height-5, width, 5);
                n0.rectangle(lx+width-5, by, 5, height);
                n0.fill();
                ///layer 2
                PdfTemplate n2 = sap.getLayer(2);
                n2.setCharacterSpacing(0.0f);
                ColumnText ct = new ColumnText(n2);
                ct.setSimpleColumn(n2.getBoundingBox());
                n2.setRGBColorFill(255, 0, 0);
                //做一個(gè)占位的動作
                Paragraph p1 = new Paragraph(" ");
                BaseFont bf = BaseFont.createFont(AsianFontMapper.ChineseSimplifiedFont, AsianFontMapper.ChineseSimplifiedEncoding_H, 
                        BaseFont.NOT_EMBEDDED);
                Font font1 = new Font(bf, 5, Font.BOLD, BaseColor.RED);
                Font font2 = new Font(bf, 13, Font.BOLD, BaseColor.RED);
                p1.setFont(font1);
                ct.addElement(p1);
                Paragraph p = new Paragraph(signText);
                p.setAlignment(Element.ALIGN_CENTER);
                p.setFont(font2);
                ct.addElement(p);
                ct.go();
                stp.getWriter().setCompressionLevel(PdfStream.BEST_COMPRESSION);
                ExternalDigest digest = new BouncyCastleDigest();
                ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
                MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
                stp.close();
                reader.close();
                return signPDFData.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (signPDFData != null) {
                    try {
                        signPDFData.close();
                    } catch (IOException e) {
                    }
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                    }
                }
            }
            return null;
        }
     
        public static void main(String[] args) throws Exception {
            //對已經(jīng)簽章的signed.pdf文件再次簽章,這次是高清簽章
            byte[] fileData = sign("123456", "C:\\Users\\zhilin\\Desktop\\chat\\keystore.p12",//
                    "C:\\Users\\zhilin\\Desktop\\chat\\signed.pdf", 350, 290, "華佗\n2017-12-20");
            FileOutputStream f = new FileOutputStream(new File("C:\\Users\\zhilin\\Desktop\\chat\\signed2.pdf"));
            f.write(fileData);
            f.close();
        }
     
        }

    可以分析下下面這兩個(gè)簽章的區(qū)別,發(fā)現(xiàn)左邊的簽章很模糊,右邊的特別清晰。

    ava中怎么對PDF文件進(jìn)行電子簽章

    七、 如何進(jìn)行多次PDF簽名

    生成多個(gè)簽章重點(diǎn)代碼,已在SignPdf.java類進(jìn)行標(biāo)注說明;如果想進(jìn)行多次簽名,就只需對已經(jīng)進(jìn)行過簽名的PDF文件再次調(diào)用sign方法進(jìn)行再次簽名即可(第六點(diǎn)有張圖片就有兩個(gè)簽章,這就是多次簽名的結(jié)果)。

    PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);

    關(guān)于ava中怎么對PDF文件進(jìn)行電子簽章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI