开放签电子签章系统-开源版这周(2023-12-15)就要上线发布了,为了让开源版能够更好的服务广大研发工程师,接下来会详细的说说开源版从代码层面的具体实现,以便大家在工作过程中更好使用开放签电子签章系统。本文主要讲开放签开源电子签章系统Java后端代码中关于数字证书生成的Java代码。

一、需要引用的jar包:

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<Certificate> 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<rootChains.length;i++){
chains.add(rootChains[i]);
}
} KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair(); KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, null); Date startDate = new Date(System.currentTimeMillis());
Date expireDate = new Date(System.currentTimeMillis() + (86400000L * 365 * lifespan)); String serial = UUID.randomUUID().toString().replaceAll("-","");
BigInteger serialBig = str2BigInteger(serial); if(signPrivateKey == null){
signPrivateKey = keyPair.getPrivate();
}
Certificate cert = generateV3(issuer, new X500Name(subject),serialBig, startDate,expireDate, keyPair.getPublic(),
signPrivateKey
, null); //用户证书要放在证书链的最前面
chains.add(0,cert); keyStore.setKeyEntry("root", keyPair.getPrivate(), password.toCharArray(), chains.toArray(new Certificate[chains.size()])); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
keyStore.store(outputStream,password.toCharArray()); GenerateCertificateInfo certificateInfo = new GenerateCertificateInfo();
certificateInfo.setAlgorithm(certificateType.name());
certificateInfo.setAlgorithmSignature(algorithmSignature);
certificateInfo.setPassword(password);
certificateInfo.setCertFileType("jks");
certificateInfo.setSerial(serial);
certificateInfo.setJks(outputStream.toByteArray());
certificateInfo.setPfx(CertificateUtils.coverToPfx(certificateInfo.getJks(),certificateInfo.getPassword())); certificateInfo.setTermOfValidityStartTime(startDate);
certificateInfo.setTermOfValidityEndTime(expireDate); return certificateInfo;
} public Certificate generateV3(X500Name issuer, X500Name subject,
BigInteger serial, Date startDate, Date expireDate,
PublicKey publicKey, PrivateKey privKey, List<Extension> 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;
} }

java后端工具类之从0到1生成数字证书(RSA)的更多相关文章

  1. [转]Java常用工具类集合

    转自:http://blog.csdn.net/justdb/article/details/8653166 数据库连接工具类——仅仅获得连接对象 ConnDB.java package com.ut ...

  2. Java Properties工具类详解

    1.Java Properties工具类位于java.util.Properties,该工具类的使用极其简单方便.首先该类是继承自 Hashtable<Object,Object> 这就奠 ...

  3. 项目经验分享——Java常用工具类集合 转

    http://blog.csdn.net/xyw591238/article/details/51678525 写在前面     本文涉及的工具类部分是自己编写,另一部分是在项目里收集的.工具类涉及数 ...

  4. Java日期工具类,Java时间工具类,Java时间格式化

    Java日期工具类,Java时间工具类,Java时间格式化 >>>>>>>>>>>>>>>>>&g ...

  5. Java并发工具类 - CountDownLatch

    Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http: ...

  6. MinerUtil.java 爬虫工具类

    MinerUtil.java 爬虫工具类 package com.iteye.injavawetrust.miner; import java.io.File; import java.io.File ...

  7. MinerDB.java 数据库工具类

    MinerDB.java 数据库工具类 package com.iteye.injavawetrust.miner; import java.sql.Connection; import java.s ...

  8. 小记Java时间工具类

    小记Java时间工具类 废话不多说,这里主要记录以下几个工具 两个时间只差(Data) 获取时间的格式 格式化时间 返回String 两个时间只差(String) 获取两个时间之间的日期.月份.年份 ...

  9. Java Cookie工具类,Java CookieUtils 工具类,Java如何增加Cookie

    Java Cookie工具类,Java CookieUtils 工具类,Java如何增加Cookie >>>>>>>>>>>>& ...

  10. UrlUtils工具类,Java URL工具类,Java URL链接工具类

    UrlUtils工具类,Java URL工具类,Java URL链接工具类 >>>>>>>>>>>>>>>&g ...

随机推荐

  1. MySQL实战实战系列 07 行锁功过:怎么减少行锁对性能的影响?

    在上一篇文章中,我跟你介绍了 MySQL 的全局锁和表级锁,今天我们就来讲讲 MySQL 的行锁. MySQL 的行锁是在引擎层由各个引擎自己实现的.但并不是所有的引擎都支持行锁,比如 MyISAM ...

  2. 模块化打包工具-初识Webpack

    1. 为什么需要模块化打包工具 在上一篇文章中提到的ES Module可以帮助开发者更好地组织代码,完成js文件的模块化,基本解决了模块化的问题,但是实际开发中仅仅完成js文件的模块化是不够的,尤其是 ...

  3. 痞子衡嵌入式:MCUBootUtility v5.3发布,利用XMCD轻松使能外部RAM

    -- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v5.0.0)发布过去4个多月了,期间痞子衡也做过三个小版本更新,但不足以单独介绍.这一次痞子衡为大家带来了全新重要版本 ...

  4. Django框架——中间件、Auth模块、ContentType

    文章目录 一 什么是中间件 二 中间件有什么用 三 自定义中间件 process_request和process_response process_view process_exception pro ...

  5. 超星读书下载的pdz文件如何转为pdf文件详细教程(亲测有效)

    前言: 你还在为超星读书下载的pdz格式书籍而烦恼吗?还在为不知道怎么将pdz格式转为pdf格式而气愤吗?请看以下教程. 流程: 使用超星阅读器将pdz文件转换为.xps或.oxps文件 利用第三方软 ...

  6. umich cv-3-1

    UMICH CV Neural Network 对于传统的线性分类器,分类效果并不好,所以这节引入了一个两层的神经网络,来帮助我们进行图像分类 可以看出它的结构十分简单,x作为输入层,经过max(0, ...

  7. 一个Unity富文本插件的实现思路

    项目中原来的富文本组件不太好用,做了一些修改,记述主要思路.缺陷很多. 仅适用于没用TextMeshPro,且不打算用的项目,否则请直接用TextMeshPro 原组件特点: 使用占位符模式,创建新的 ...

  8. openwrt使用tailscale实现内网穿透

    问题 之前一直有电信公网ip,最近发现电信公网ip被撤下来了,打电话再去要发现给的是10开头的ip,电信客服还跟我说10开头就是公网ip,= =,根本就不是,无奈使用zerotier进行打洞,把zer ...

  9. IIS和PHP相关权限问题及解决方案_500错误_500.19 - Internal Server Error与401未授权错误_401.3 - Unauthorized

    在IIS添加网站(假设站点为xxx.yyy.com,本例假设IIS版本为7.5或以上),如果采用IIS默认配置,会在创建站点同时创建相应同名的"应用程序池"(也是xxx.yyy.c ...

  10. 微前端、single-spa初探

    微前端 微前端,前端这次词就不用多做解释了,这个概念的重点在于这个"微"字, 从字面意义上看,微是小的意思,小是相对于大的一个用于比较的形容词,所以通常是在项目庞大的情况下,才会考 ...