亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

瀏覽:21日期:2022-08-09 09:25:25
目錄一、 概述二、 技術(shù)選型三、 生成一個(gè)圖片簽章1. 生成一個(gè)如下圖的簽章圖片2. 相關(guān)代碼四、 如何按模板生成PDF文件1. 制作PDF模板2. 制作一個(gè)如下圖的PDF模板,該模板是帶有PDF的表單域的五、 如何生成PKCS12證書(shū)1. PKCS的簡(jiǎn)單介紹2. 使用JAVA生成一個(gè)PKCS12證書(shū)并進(jìn)行存貯,相關(guān)分析見(jiàn)代碼注解六、 如何生成一個(gè)高清晰的簽章1. 由PDF模板生成一個(gè)PDF文件,見(jiàn)代碼注解2. 對(duì)PDF文件進(jìn)行簽章3. 高清簽章七、 如何進(jìn)行多次PDF簽名八、 總結(jié)一、 概述

印章是我國(guó)特有的歷史文化產(chǎn)物,古代主要用作身份憑證和行駛職權(quán)的工具。它的起源是由于社會(huì)生活的實(shí)際需要。早在商周時(shí)代,印章就已經(jīng)產(chǎn)生。如今的印章已成為一種獨(dú)特的,融實(shí)用性和藝術(shù)性為一體的藝術(shù)瑰寶。傳統(tǒng)的印章容易被壞人、小人私刻;從而新聞鮮有報(bào)道某某私刻公章,侵吞國(guó)家財(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ù)字證書(shū)一樣是身份驗(yàn)證的一種手段,泛指所有以電子形式存在,依附在電子文件并與其邏輯關(guān)聯(lián),可用以辨識(shí)電子文件簽署者身份,保證文件的完整性,并表示簽署者同意電子文件所陳述事實(shí)的內(nèi)容。一般來(lái)說(shuō)對(duì)電子簽章的認(rèn)定都是從技術(shù)角度而言的。主要是指通過(guò)特定的技術(shù)方案來(lái)鑒別當(dāng)事人的身份及確保電子資料內(nèi)容不被篡改的安全保障措施。電子簽章常于發(fā)送安全電子郵件、訪問(wèn)安全站點(diǎn)、網(wǎng)上招標(biāo)投標(biāo)、網(wǎng)上簽約、安全網(wǎng)上公文傳送、公司合同、電子處方箋等。

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

二、 技術(shù)選型

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

開(kāi)源組織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ù)呢?

對(duì)比PDFBox、iText5和iText7這三者:

PDFBox的功能相對(duì)較弱,iText5和iText7的功能非常強(qiáng)悍; iText5的資料網(wǎng)上相對(duì)較多,如果出現(xiàn)問(wèn)題容易找到解決方案;PDFBox和iText7的網(wǎng)上資料相對(duì)較少,如果出現(xiàn)問(wèn)題不易找到相關(guān)解決方案; 通過(guò)閱讀PDFBox代碼目前PDFBox還沒(méi)提供自定義簽章的相關(guān)接口;iText5和iText7提供了處理自定義簽章的相關(guān)實(shí)現(xiàn); PDFBox只能實(shí)現(xiàn)把簽章圖片加簽到PDF文件;iText5和iText7除了可以把簽章圖片加簽到PDF文件,還可以實(shí)現(xiàn)直接對(duì)簽章進(jìn)行繪制,把文件繪制到簽章上。 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對(duì)PDF文件進(jìn)行電子簽章需要實(shí)現(xiàn)的功能:

生成證書(shū)。與PDFBox、iText5和iText7技術(shù)無(wú)關(guān) 按模板輸出PDF文件:PDFBox、iText5和iText7都可以完成,但是PDFBox會(huì)遇到中文亂碼比較棘手的問(wèn)題 在PDF文件中實(shí)現(xiàn)把簽章圖片加簽到PDF文件:PDFBox、iText5和iText7都可以實(shí)現(xiàn),沒(méi)有很多的區(qū)別 在PDF文件中繪制簽章:iText5和iText7都可以實(shí)現(xiàn),PDFBox目前不支持 在PDF文件中生成高清簽章:iText5和iText7都可以實(shí)現(xiàn),PDFBox目前不支持 在PDF文件中進(jìn)行多次簽名::PDFBox、iText5和iText7都可以完成,沒(méi)有區(qū)別

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

使用iText5能實(shí)現(xiàn)全部的功能 如何在開(kāi)發(fā)中遇到相關(guān)問(wèn)題,容易找到相應(yīng)解決方案 三、 生成一個(gè)圖片簽章1. 生成一個(gè)如下圖的簽章圖片

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

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ī)生名稱(chēng) * @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); // 畫(huà)一個(gè)矩形 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 去除鋸齒(當(dāng)設(shè)置的字體過(guò)大的時(shí)候,會(huì)出現(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); // 字體字形字號(hào) 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); // 字體字形字號(hào) 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); // 存盤(pán) 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模板工具別無(wú)他物,只能使用偉大的Adobe公司提供的Adobe Acrobatpro DC軟件進(jìn)行制作。如何使用該軟件這里就不多說(shuō)了,如果在使用中遇到什么可以另外咨詢(xún)。

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

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

五、 如何生成PKCS12證書(shū)1. PKCS的簡(jiǎn)單介紹

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

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

PKCS#1:定義RSA公開(kāi)密鑰算法加密和簽名機(jī)制,主要用于組織PKCS#7中所描述的數(shù)字簽名和數(shù)字信封[22]。 PKCS#3:定義Diffie-Hellman密鑰交換協(xié)議[23]。 PKCS#5:描述一種利用從口令派生出來(lái)的安全密鑰加密字符串的方法。使用MD2或MD5 從口令中派生密鑰,并采用DES-CBC模式加密。主要用于加密從一個(gè)計(jì)算機(jī)傳送到另一個(gè)計(jì)算機(jī)的私人密鑰,不能用于加密消息[24]。 PKCS#6:描述了公鑰證書(shū)的標(biāo)準(zhǔn)語(yǔ)法,主要描述X.509證書(shū)的擴(kuò)展格式[25]。 PKCS#7:定義一種通用的消息語(yǔ)法,包括數(shù)字簽名和加密等用于增強(qiáng)的加密機(jī)制,PKCS#7與PEM兼容,所以不需其他密碼操作,就可以將加密的消息轉(zhuǎn)換成PEM消息[26]。 PKCS#8:描述私有密鑰信息格式,該信息包括公開(kāi)密鑰算法的私有密鑰以及可選的屬性集等[27]。 PKCS#9:定義一些用于PKCS#6證書(shū)擴(kuò)展、PKCS#7數(shù)字簽名和PKCS#8私鑰加密信息的屬性類(lèi)型[28]。 PKCS#10:描述證書(shū)請(qǐng)求語(yǔ)法[29]。 PKCS#11:稱(chēng)為Cyptoki,定義了一套獨(dú)立于技術(shù)的程序設(shè)計(jì)接口,用于智能卡和PCMCIA卡之類(lèi)的加密設(shè)備[30]。 PKCS#12:描述個(gè)人信息交換語(yǔ)法標(biāo)準(zhǔn)。描述了將用戶(hù)公鑰、私鑰、證書(shū)和其他相關(guān)信息打包的語(yǔ)法[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,主要用來(lái)描述個(gè)人身份信息;本次分享中要進(jìn)行簽章操作的是醫(yī)生和藥師,他們就是一個(gè)個(gè)人主體,給他們分配一個(gè)PKCS12的證書(shū),就等于給他們分配了一個(gè)用于蓋章的印章。

2. 使用JAVA生成一個(gè)PKCS12證書(shū)并進(jìn)行存貯,相關(guān)分析見(jià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);// 證書(shū)中的密鑰 公鑰和私鑰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證書(shū) // KeyStore keyStore = KeyStore.getInstance('JKS'); // 標(biāo)志生成PKCS12證書(shū) KeyStore keyStore = KeyStore.getInstance('PKCS12', new BouncyCastleProvider()); keyStore.load(null, null); KeyPair keyPair = getKey(); // issuer與 subject相同的證書(shū)就是CA證書(shū) Certificate cert = generateCertificateV3(issuerStr, subjectStr, keyPair, result, certificateCRL, null); // cretkey隨便寫(xiě),標(biāo)識(shí)別名 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(); // 證書(shū)序列號(hào) 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證書(shū) 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ū)數(shù)據(jù) result.put('certificateData', certBuf); //公鑰 result.put('publicKey', publicKey.getEncoded()); //私鑰 result.put('privateKey', privateKey.getEncoded()); //證書(shū)有效開(kāi)始時(shí)間 result.put('notBefore', format.format(notBefore).getBytes('utf-8')); //證書(shū)有效結(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 : 組織單位名稱(chēng)// O :組織名稱(chēng) L : 城市或區(qū)域名稱(chēng) E : 電子郵件// ST: 州或省份名稱(chēng) C: 單位的兩字母國(guó)家代碼 String issuerStr = 'CN=在線醫(yī)院,OU=gitbook研發(fā)部,O=gitbook有限公司,C=CN,[email protected],L=北京,ST=北京';String subjectStr = 'CN=huangjinjin,OU=gitbook研發(fā)部,O=gitbook有限公司,C=CN,[email protected],L=北京,ST=北京';String certificateCRL = 'https://gitbook.cn';Map<String, byte[]> result = createCert('123456', issuerStr, subjectStr, certificateCRL); FileOutputStream outPutStream = new FileOutputStream('c:/keystore.p12'); // ca.jksoutPutStream.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文件,見(jiàn)代碼注解

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); // 為字段賦值,注意字段名稱(chēng)是區(qū)分大小寫(xiě)的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文件名稱(chēng) */ 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è),否則文檔不會(huì)生成的 如果為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粒×2板 ');data.put('dose', '×2盒');data.put('cons', '用法用量:口服 一日兩次 一次2粒');data.put('tips', '溫馨提示');data.put('desc', '盡量呆在通風(fēng)較好的地方,保持空氣流通,有利于病情康復(fù)。盡量呆在通風(fēng)較好的地方');generatePDF('C:UserszhilinDesktopchattpl.pdf', 'C:UserszhilinDesktopchatfilled.pdf', data ); } }

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

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

經(jīng)過(guò)過(guò)上面的代碼可以生成一個(gè)名為sign.jpg的簽章圖片,生成一個(gè)keystore.p12的證書(shū)文件,還有一個(gè)已經(jīng)通過(guò)模板填充了表單的名為filled.pdf的pdf文件。下面就可通過(guò)以上材料生成一個(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生成證書(shū)是的私鑰密碼 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); // 是對(duì)應(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:UserszhilinDesktopchatkeystore.p12', //'C:UserszhilinDesktopchatfilled.pdf',//'C:UserszhilinDesktopchatsign.jpg', 100, 290);FileOutputStream f = new FileOutputStream(new File('C:UserszhilinDesktopchatsigned.pdf'));f.write(fileData);f.close(); } }

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

3. 高清簽章

高清簽章是通過(guò)iText的繪制功能來(lái)完成。主要直接在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ù)字簽名,不可改變'); // 是對(duì)應(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è)占位的動(dòng)作 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 {//對(duì)已經(jīng)簽章的signed.pdf文件再次簽章,這次是高清簽章byte[] fileData = sign('123456', 'C:UserszhilinDesktopchatkeystore.p12',//'C:UserszhilinDesktopchatsigned.pdf', 350, 290, '華佗n2017-12-20');FileOutputStream f = new FileOutputStream(new File('C:UserszhilinDesktopchatsigned2.pdf'));f.write(fileData);f.close(); } }

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

利用Java對(duì)PDF文件進(jìn)行電子簽章的實(shí)戰(zhàn)過(guò)程

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

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

PdfStamper.createSignature(reader, signPDFData, ’0’, temp, true);八、 總結(jié)

分享中sign.jpg文件的白色背景需要做透明化處理才能達(dá)到正確電子簽章的效果(不覆蓋PDF文件中已有的內(nèi)容,真實(shí)的電子簽章也是這樣做的),大家回去可以思考下怎么把一個(gè)jpg文件白色背景透明化(高清簽章就已經(jīng)實(shí)現(xiàn)透明化,可以試著把SignPdf.java和SignHighPdf.java簽章到有文字的PDF上面看看效果)。

大家見(jiàn)到的公司公章都是圓形的;這個(gè)也是可以做到的大家想想怎樣生成一個(gè)圓形的圖片簽章;然后進(jìn)行電子簽名。這里主要是講解代碼實(shí)現(xiàn),所有代碼非常多。大家回去好好研讀代碼。真正的電子簽名需要通過(guò)CA認(rèn)證公司來(lái)完成,我這里只是提供參考方案讓大家學(xué)習(xí)。

到此這篇關(guān)于利用Java對(duì)PDF文件進(jìn)行電子簽章的文章就介紹到這了,更多相關(guān)Java對(duì)PDF文件電子簽章內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 被公侵犯肉体中文字幕一区二区 | 免费视频网站在线观看黄 | 一级韩国aa毛片免费观看 | 高清国产一级毛片国语 | 高清在线亚洲精品国产二区 | 亚洲不卡av不卡一区二区 | 欧美日韩综合 | 黑人性较大a| japanese日本护士xx亚洲 | 求黄色网| 国产亚洲欧美一区二区三区 | 亚洲网在线观看 | 久久久久久免费播放一级毛片 | 国产精品揄拍100视频最近 | 亚洲黄色免费网址 | 亚洲久久久久 | 成年人啪啪网站 | 任你躁在线精品视频m3u8 | 色天天久久| 成年性羞羞视频免费观看无限 | 一级一级女人18毛片 | 国产欧美日韩视频 | 亚洲日本高清成人aⅴ片 | 91在线老王精品免费播放 | 一区二区在线欧美日韩中文 | 黄色一级a毛片 | freesex双人videos人妖 | 亚洲精品色播一区二区 | 91精品国产综合久久婷婷 | 黄色小视频免费网站 | 丰满女人毛片免费播放 | 久久综合久久综合久久 | 中国a毛片 | 成人资源在线观看 | 国产网址在线观看 | 五月婷婷六月天 | 色播亚洲视频在线观看 | 九九九九在线精品免费视频 | 欧美二级在线观看免费 | 爱爱永久免费视频网站 | 日韩特级片 |