开放签电子签章系统-开源版这周(2023-12-15)就要上线发布了,为了让开源版能够更好的服务广大研发工程师,接下来会详细的说说开源版从代码层面的具体实现,以便大家在工作过程中更好使用开放签电子签章系统。本文主要讲开放签开源电子签章系统Java后端代码中关于数字证书生成的Java代码。
bouncycastle
java.security
(1)生成数字证书主题信息
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;
public class CertificateUtils {
/**
* 生成证书主题信息
*
* @param C Country Name (国家代号),eg: CN
* @param ST State or Province Name (洲或者省份),eg: Beijing
* @param L Locality Name (城市名),eg: Beijing
* @param O Organization Name (可以是公司名称),eg: xxxxxx网络技术有限公司
* @param OU Organizational Unit Name (可以是单位部门名称)
* @param CN Common Name (服务器ip或者域名或者公司名称),eg: 192.168.30.71 or www.baidu.com or xxxxxx网络技术有限公司
* @return X500Name Subject
*/
public static String buildSubject(String C, String ST, String L,
String O, String OU, String CN) {
X500NameBuilder x500NameBuilder = new X500NameBuilder();
x500NameBuilder.addRDN(BCStyle.C, C);
x500NameBuilder.addRDN(BCStyle.ST, ST);
x500NameBuilder.addRDN(BCStyle.L, L);
if(O != null)
x500NameBuilder.addRDN(BCStyle.O, O);
if(OU != null)
x500NameBuilder.addRDN(BCStyle.OU, OU);
x500NameBuilder.addRDN(BCStyle.CN, CN);
System.out.println(x500NameBuilder.build().toASN1Primitive().toString());
return x500NameBuilder.build().toString();
}
public static byte [] coverToPfx(byte [] jks, String password) {
try {
KeyStore inputKeyStore = KeyStore.getInstance("JKS");
ByteArrayInputStream inputStream = new ByteArrayInputStream(jks);
inputKeyStore.load(inputStream, password.toCharArray());
KeyStore outputKeyStore = KeyStore.getInstance("PKCS12");
outputKeyStore.load(null, password.toCharArray());
Enumeration enums = inputKeyStore.aliases();
while (enums.hasMoreElements()) { // we are readin just one certificate.
String keyAlias = (String) enums.nextElement();
if (inputKeyStore.isKeyEntry(keyAlias)) {
Key key = inputKeyStore.getKey(keyAlias, password.toCharArray());
Certificate[] certChain = inputKeyStore
.getCertificateChain(keyAlias);
outputKeyStore.setKeyEntry("", key, password
.toCharArray(), certChain);
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
outputKeyStore.store(out, password.toCharArray());
out.close();
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
(2)数字证书生成过程信息
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenerateCertificateInfo {
/**
* 证书密码
*/
private String password;
/**
* 证书类型 JKS PFX
*/
private String certFileType;
/**
* 证书库
*/
private byte [] jks;
/**
* 签名证书
*/
private byte [] pfx;
/**
* 证书序列号
*/
private String serial;
/**
* 证书签名算法 SHA1withRSA SHA256withRSA
*/
private String algorithmSignature;
/**
* 证书算法类型: RSA、SM2
*/
private String algorithm;
/**
* 证书有效期起始时间
*/
private Date termOfValidityStartTime;
/**
* 证书有效期结束时间
*/
private Date termOfValidityEndTime;
}
(3)根证书类
import java.security.KeyStore;
public class BaseCertificateInfo {
/**
* 根证书
*/
private KeyStore cert;
/**
* 证书别名
*/
private String alias;
/**
* 证书密码
*/
private String password;
public KeyStore getCert() {
return cert;
}
public void setCert(KeyStore cert) {
this.cert = cert;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
(4)数字证书类型枚举
public enum CertificateType {
RSA,SM2
}
(5)生成数字证书
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
public class GenerateRootCertificate {
//SHA256WITHRSA
private String subject;
private String algorithmSignature = "SHA256withRSA";
private CertificateType certificateType;
private GenerateRootCertificate(String subject,CertificateType certificateType){
this.subject = subject;
this.certificateType = certificateType;
}
/**
* @param subject 证书主题
* 可通过 CertificateUtils.buildSubject进行生成
*/
public static GenerateRootCertificate instance(String subject,CertificateType certificateType){
return new GenerateRootCertificate(subject,certificateType);
}
/**
* 设置证书签名算法 SHA1withRSA SHA256withRSA
* @param algorithmSignature
*/
public void setAlgorithmSignature(String algorithmSignature){
this.algorithmSignature = algorithmSignature;
}
/**
* 证书类型
* @param password
* @param lifespan 单位年
*
* @return
*/
public GenerateCertificateInfo generateCertificate(BaseCertificateInfo root, String password, int lifespan) throws Exception {
X500Name issuer = new X500Name(subject);
KeyStore.PrivateKeyEntry rootPK = null;
PrivateKey signPrivateKey = null;
List chains = new ArrayList<>(5);
if(root != null){
rootPK = (KeyStore.PrivateKeyEntry) root.getCert().getEntry(root.getAlias(),
new KeyStore.PasswordProtection(root.getPassword().toCharArray()));
X509Certificate rootCert = (X509Certificate) rootPK.getCertificate();
issuer = X500Name.getInstance(rootCert.getSubjectX500Principal().getEncoded());
signPrivateKey = rootPK.getPrivateKey();
Certificate [] rootChains = rootPK.getCertificateChain();
for (int i=0;i extensions) throws IOException, CertificateException, OperatorCreationException {
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
issuer, serial, startDate, expireDate,
subject, publicKey);
ContentSigner sigGen = new JcaContentSignerBuilder(algorithmSignature).build(privKey);
//privKey是CA的私钥,publicKey是待签名的公钥,那么生成的证书就是被CA签名的证书。
if (extensions != null){
for (Extension ext : extensions) {
builder.addExtension(ext.getExtnId(), ext.isCritical(),ext.getExtnValue());
}
}
X509CertificateHolder holder = builder.build(sigGen);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is1 = new ByteArrayInputStream(holder.toASN1Structure()
.getEncoded());
X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
return theCert;
}
public static void main(String[] args) throws Exception {
//
KeyStore store = loadJKS("Ting111");
BaseCertificateInfo baseCertificateInfo = new BaseCertificateInfo();
baseCertificateInfo.setAlias("root");
baseCertificateInfo.setCert(store);
baseCertificateInfo.setPassword("123456");
String x500Name = "CN=Ting222,OU=研发部,O=资源律动,L=BeiJing,ST=BeiJing,C=CN";
GenerateCertificateInfo certificateInfo = GenerateRootCertificate.instance(x500Name,CertificateType.RSA).generateCertificate(baseCertificateInfo,"123456",10);
FileUtils.writeByteArrayToFile(new File("C:\\Users\\Administrator\\Desktop\\tem\\cert\\Ting222.jks"), certificateInfo.getJks());
}
private static BigInteger str2BigInteger(String str){
StringBuffer sb = new StringBuffer();
//将字符串转换为字符数组
char ch[] = str.toCharArray();
for(int i = 0; i < ch.length; i++) {
String hexString = Integer.toHexString(ch[i]);
sb.append(hexString);
}
return new BigInteger(sb.toString());
}
private static String big2String(String str){
String result = new String();
char[] charArray = str.toString().toCharArray();
for(int i = 0; i < charArray.length; i=i+2) {
String st = ""+charArray[i]+""+charArray[i+1];
char ch1 = (char)Integer.parseInt(st, 16);
result = result + ch1;
}
return result;
}
public static KeyStore loadJKS(String name) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
KeyStore store = KeyStore.getInstance("JKS");
File file = new File(""+name+".jks");
store.load(new FileInputStream(file), "123456".toCharArray());
return store;
}
}