Java签章操作的设计与实现
1 说明
本⽂主要讲解使⽤Java和SpringBoot框架设计实现对PDF的签章操作。实现对PDF的签章操作不是简单的找个图⽚贴到PDF上即可,⽽是需要申请数字证书才能对PDF签章,否则⽆法验证签章的⾝份。具体实现分两步进⾏,第⼀步⽣成PKCS12证书,第⼆步添加签章。
2 ⽣成PKCS12证书
⽣成PKCS12证书是通过使⽤bouncycastle包实现的,具体实现如下所⽰,⾸先引⼊依赖。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.49</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.49</version>
</dependency>
步骤1: 编写⽣成PKCS12证书⼯具类
import ASN1ObjectIdentifier;
import ASN1Primitive;
import X500Name;
import*;
import X509CertificateHolder;
import X509v3CertificateBuilder;
import JcaX509v3CertificateBuilder;
import BouncyCastleProvider;
import ContentSigner;
import JcaContentSignerBuilder;
import ByteArrayInputStream;
import ByteArrayOutputStream;
import IOException;
import BigInteger;
import StandardCharts;
import*;
import Certificate;
import CertificateFactory;
import X509Certificate;
import SimpleDateFormat;
import*;
import Extension;
/**
* 证书⽣成⼯具类
*
* @author ⼀朝风⽉
* @date 2021/8/6 14:20
*/
public class GenerateCertificateUtil {
private static KeyPair getKey()throws NoSuchAlgorithmException {
// 密钥对⽣成器,RSA算法⽣成的提供者是 BouncyCastle
KeyPairGenerator generator = Instance("RSA",new BouncyCastleProvider());
// 密钥长度 1024
generator.initialize(1024);
// 证书中的密钥公钥和私钥
ateKeyPair();
}
/**
* 创建证书
*
* @param password 密码
* @param issuerStr 颁发机构信息
* @param subjectStr 使⽤者信息
* @param certificateCrl 颁发地址
* @return 证书
*/
public static Map<String,byte[]>createCert(String password, String issuerStr, String subjectStr,
String certificateCrl, List<Extension> extensions){
Map<String,byte[]> result =new HashMap<>();
ByteArrayOutputStream out =null;
try{
// ⽣成证书
// KeyStore keyStore = Instance("JKS");
KeyStore keyStore = Instance("PKCS12",new BouncyCastleProvider());李庆明
keyStore.load(null,null);
KeyPair keyPair =getKey();
// issuer与 subject相同的证书就是CA证书
Certificate cert =generateCertificate(issuerStr, subjectStr, keyPair, result, certificateCrl, extensions);
// cretkey随便写,标识别名
keyStore.tKeyEntry("cretkey", Private(), CharArray(),new Certificate[]{cert}); out =new ByteArrayOutputStream();
cert.Public());
keyStore.store(out, CharArray());
byte[] keyStoreData = ByteArray();
result.put("keyStoreData", keyStoreData);
return result;
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(out !=null){
out.clo();
提刑司
}
}catch(Exception e){
e.printStackTrace();
}
}
return result;
}
/**
* ⽣成证书
*
* @param issuerStr 事项字符串
* @param subjectStr 主题字符串
* @param keyPair 密钥对(公钥,私钥)
* @param result 结果
* @param certificateCrl CRL分发点
* @param extensions 扩展字段
* @return ⽣成证书
*/
private static Certificate generateCertificate(String issuerStr, String subjectStr, KeyPair keyPair,
Map<String,byte[]> result,
String certificateCrl, List<Extension> extensions){
ByteArrayInputStream bout =null;
X509Certificate cert =null;
try{
PublicKey publicKey = Public();
PrivateKey privateKey = Private();
Date notBefore =new Date();
Calendar rightNow = Instance();
rightNow.tTime(notBefore);
// ⽇期加1年
rightNow.add(Calendar.YEAR,1);
rightNow.add(Calendar.YEAR,1);
Date notAfter = Time();
/
/ 证书序列号
BigInteger rial = BigInteger.probablePrime(256,new Random());
X509v3CertificateBuilder builder =new JcaX509v3CertificateBuilder(
new X500Name(issuerStr), rial, notBefore, notAfter,new X500Name(subjectStr), publicKey);
JcaContentSignerBuilder jBuilder =new JcaContentSignerBuilder("SHA1withRSA");
SecureRandom cureRandom =new SecureRandom();
jBuilder.tSecureRandom(cureRandom);
ContentSigner singer = jBuilder.tProvider(new BouncyCastleProvider()).build(privateKey);
// 分发点
ASN1ObjectIdentifier identifier =new ASN1ObjectIdentifier("2.5.29.31");
GeneralName generalName =new GeneralName(GeneralName.uniformResourceIdentifier, certificateCrl);
GeneralNames neralNames =new GeneralNames(generalName);
DistributionPointName distributionPoint =new DistributionPointName(neralNames);
DistributionPoint[] points =new DistributionPoint[1];
points[0]=new DistributionPoint(distributionPoint,null,null);
CRLDistPoint crlDistPoint =new CRLDistPoint(points);
builder.addExtension(identifier,true, crlDistPoint);
// ⽤途
ASN1ObjectIdentifier keyUsage =new ASN1ObjectIdentifier("2.5.29.15");
// | Repudiation | 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));
// privateKey:使⽤⾃⼰的私钥进⾏签名,CA证书
if(extensions !=null){
for(Extension ext : extensions){
builder.addExtension(
new Oid()),
ext.isCritical(),
ASN1Primitive.Value()));
}
}
X509CertificateHolder holder = builder.build(singer);
CertificateFactory cf = Instance("X.509");
打印机怎么添加
bout =new ASN1Structure().getEncoded());
cert =(X509Certificate) cf.generateCertificate(bout);
byte[] certBuf = Encoded();
SimpleDateFormat format =new SimpleDateFormat("yyyy-MM-dd");
// 证书数据
nutural
result.put("certificateData", certBuf);
//公钥
result.put("publicKey", Encoded());
//私钥
学生会主席竞选演讲稿
result.put("privateKey", Encoded());
//证书有效开始时间
result.put("notBefore", format.format(notBefore).getBytes(StandardCharts.UTF_8));
//证书有效结束时间
result.put("notAfter", format.format(notAfter).getBytes(StandardCharts.UTF_8));
}catch(Exception e){
e.printStackTrace();
}finally{
if(bout !=null){
try{
bout.clo();
}catch(IOException e){
e.printStackTrace();
}
}
}
return cert;
}
}
步骤2: 证书参数类
import Data;
/**
秋天的风景作文
* 证书参数
*
* @author ⼀朝风⽉
* @date 2021/8/6 16:06
*/
@Data
public class CertificateParams {
/**
* 事项字符串
*/
private String issuerStr;
/
**
* 主题字符串
*/
private String subjectStr;
/**
* CRL分发点
*/
private String certificateCrl;
/**
* 证书密码
*/
private String password;
/**
* 额外的信息
*/
private String extensionsJson;
}
步骤3: 编写获取证书接⼝
/**
* 获取PKCS12证书
*
* @param params 证书参数
* @param respon HTTP响应
* @return PKCS12证书
* @throws IOException 异常
*/
@PostMapping("getCertificate")
public Result mGetCertificate(CertificateParams params, HttpServletRespon respon)throws IOException { /*
* 填写说明
*
* CN: 名字与姓⽒ OU : 组织单位名称
* O :组织名称 L : 城市或区域名称 E : 电⼦邮件
* ST: 州或省份名称 C: 单位的两字母国-家代码
*
* 例如:
* String issuerStr = "CN=jcb凭证,OU=研发部,O=jcb有限公司,C=CN,E=,L=北京,ST=北京";
* String subjectStr = "CN=jcb有限公司,OU=⽤户,O=test,C=CN,E=,L=北京,ST=北京";
* String certificateCrl = "jcb";
*/
Map<String,byte[]> result = Password(), IssuerStr(), SubjectStr(), CertificateCrl(),
JSONArray.ExtensionsJson(), Extension.class));
// 将证书放置在respon中
respon.tContentType("application/*");
respon.tHeader("Content-Disposition","attachment;filename=\""+"PKCS12证书"+"\"");
IOUtils.("keyStoreData"), OutputStream());
return null;
}
3 使⽤证书添加签章
使⽤刚刚⽣成的证书添加签章,依赖如下所⽰:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
3.1 签章参数类