完美解决pdf手写签名(纯手写水印图片)
首先你需要生成证书公私钥,后面pdf盖章时需要用到。
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.security.KeyPair;
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 颁发机构信息
*
* @param subjectStr 使用者信息
*
* @param certificateCRL 颁发地址
*
* @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");
// 标志生成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随便写,标识别名
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);
// 分发点
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:使用自己的私钥进行签名,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");
// 证书数据
result.put("certificateData", certBuf);
//公钥
result.put("publicKey", publicKey.getEncoded());
//私钥
result.put("privateKey", privateKey.getEncoded());
//证书有效开始时间
result.put("notBefore", format.format(notBefore).getBytes("utf-8"));
//证书有效结束时间
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 : 城市或区域名称 E : 电子邮件
// ST: 州或省份名称 C: 单位的两字母国家代码
String issuerStr = "CN=在线医院,OU=gitbook研发部,O=gitbook有限公司,C=CN,E=gitbook@sina.com,L=北京,ST=北京";
String subjectStr = "CN=huangjinjin,OU=gitbook研发部,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("d:/keystore.p12"); // ca.jks
outPutStream.write(result.get("keyStoreData"));
outPutStream.close();
FileOutputStream fos = new FileOutputStream(new File("d:/keystore.cer"));
fos.write(result.get("certificateData"));
fos.flush();
fos.close();
}
}
运行上面的代码生成两个文件,主要是.p12结尾的文件后面主要会用到
以下是一个死的图片水印
import java.io.ByteArrayOutputStream;
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();
// 临时pdf文件
File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setReason("数字签名,不可改变");
// 是对应x轴和y轴坐标
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);
//做一个占位的动作
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 {
//高清签章
byte[] fileData = sign("123456", "d:/keystore.p12",
"d:/xxx.pdf", 350, 290, "华佗\n2017-12-20");
FileOutputStream f = new FileOutputStream(new File("d:/new.pdf"));
f.write(fileData);
System.out.println("签名成功");
f.close();
}
}
需要更完美的实现代码,请到我的主页资源中查看(纯手写签名哦)
利用图片签名技术,保存并水印到pdf指定位置
下载链接:https://download.csdn.net/download/weixin_42786641/11214592