证书生成
基于BouncyCastle开源库,可以轻松制作X509证书、CRL、pkcs10、pkcs12,支持国际通用的RSA、ECC算法。制作SM2证书可以通过扩展BouncyCastle库来实现,需加入SM2签名算法DerObjectIdentifier标识1.2.156.10197.1.501(基于SM3的SM2算法签名),密钥对的生成使用国密推荐曲线参数,然后如上所示自行实现SM2签名验证算法。X509证书由证书主体、证书签名算法标识、签名组成,和RSA证书主要不同的是SM2证书的签名算法标识和签名,及证书公钥使用ECKeyParameters。
一. SM2证书生成
1. 创建sm2秘钥对象:
package com.zhonglz.util.cert.sm2;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
public class SM2PublicKey extends BCECPublicKey {
//SM3withSM2 OID 为1.2.156.10197.1.501
//SM3的公钥参数OID为1.2.156.10197.1.401
public static final ASN1ObjectIdentifier ID_SM2_PUBKEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
private boolean withCompression;
public SM2PublicKey(BCECPublicKey key) {
super(key.getAlgorithm(), key);
this.withCompression = false;
}
public SM2PublicKey(String algorithm, BCECPublicKey key) {
super(algorithm, key);
this.withCompression = false;
}
@Override
public byte[] getEncoded() {
ASN1OctetString p = ASN1OctetString.getInstance(new X9ECPoint(getQ(), withCompression).toASN1Primitive());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ID_SM2_PUBKEY_PARAM), p.getOctets());
return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
}
@Override
public void setPointFormat(String style) {
withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
}
}
2. sm2创建秘钥对
package com.zhonglz.util.cert.sm2;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
public class Sm2Utils {
// 国密推荐曲线
public static BigInteger gx = new BigInteger(//
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
public static BigInteger gy = new BigInteger(//
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
// 密码参数对象
public static SM2P256V1Curve curve = new SM2P256V1Curve();
public static BigInteger n = curve.getOrder();
public static BigInteger h = curve.getCofactor();
// 通过国密推荐曲线计算出g点
public static ECPoint point = curve.createPoint(gx, gy);
public static KeyPair generateKeyPair() throws Exception {
// BigInteger a = curve.getA().toBigInteger();
// BigInteger b = curve.getB().toBigInteger();
ECPoint point = curve.createPoint(gx, gy);
// SM2椭圆曲线参数
ECDomainParameters domainParameters = new ECDomainParameters(curve, point, n, h);
System.out.println(domainParameters);
// 随机数
SecureRandom random = new SecureRandom();
Security.addProvider(new BouncyCastleProvider());
// 密钥生成
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", //
BouncyCastleProvider.PROVIDER_NAME);
ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), //
domainParameters.getG(), domainParameters.getN(), domainParameters.getH());
kpg.initialize(parameterSpec, random);
// 密钥对生成
KeyPair keyPair = kpg.generateKeyPair();
System.out.println(keyPair);
return keyPair;
}
}
3. 创建证书:
/***
* sm2证书生成
* @param c C项 国家
* @param cn CN项 公用名/CN
* @param ou OU项 部门/OU
* @param o O项 公用信息/O 单位统一社会信用代码、个人身份证
* @param st ST项 省份
* @param l L项 城市
* @param certExpire 证书有效期 单位天
* @param password 证书密码
* @return
*/
public static Map<String, byte[]> createSM2CertToOne(String c,String cn,String ou,String o,String st,String l,long certExpire,String password){
Map<String, byte[]> resultMap = new HashMap<>();
byte[] pfxDER = null ;
try {
KeyPair keyPair = Sm2Utils.generateKeyPair();
// 证书对象生成
X500NameBuilder x500NameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
x500NameBuilder.addRDN(BCStyle.C, c);
x500NameBuilder.addRDN(BCStyle.CN, cn);
x500NameBuilder.addRDN(BCStyle.OU, ou);
x500NameBuilder.addRDN(BCStyle.ST, st);
x500NameBuilder.addRDN(BCStyle.L, l);
x500NameBuilder.addRDN(BCStyle.O, o);
X500Name x500Name = x500NameBuilder.build();
SM2PublicKey sm2PublicKey = new SM2PublicKey(keyPair.getPublic().getAlgorithm(),(BCECPublicKey) keyPair.getPublic());
// 使用使用者证书公钥生成证书签发请求
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(x500Name, sm2PublicKey);
ContentSigner signerBuilder = new JcaContentSignerBuilder("SM3withSM2").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(keyPair.getPrivate());
byte[] csr = csrBuilder.build(signerBuilder).getEncoded();
// 生成证书
X509Certificate x509Certificate = CertificateUtils.makeCertificate(csr,certExpire);
System.out.println("证书信息:/n"+x509Certificate);
PKCS10CertificationRequest issuerPkcs10CertificationRequest = new PKCS10CertificationRequest(csr);
PublicKey bcecPublicKey = CertificateUtils.convertX509ToECPublicKey(issuerPkcs10CertificationRequest.getSubjectPublicKeyInfo());
// 制作证书 证书私钥,签发证
PKCS12PfxPdu pkcs12PfxPdu = CertificateUtils.makePfx(keyPair.getPrivate(), bcecPublicKey, x509Certificate,password);
// 证书序列化
pfxDER= pkcs12PfxPdu.getEncoded(ASN1Encoding.DER);
// 证书数据
resultMap.put("certData", pfxDER);
//公钥
resultMap.put("publicKey", keyPair.getPublic().getEncoded());
//私钥
resultMap.put("privateKey", keyPair.getPrivate().getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
return resultMap;
}
二. RSA证书生成
1. oid工具类
package com.zhonglz.util.cert.rsa;
public class Extension {
private String oid;
private boolean critical;
private byte[] value;
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
public boolean isCritical() {
return critical;
}
public void setCritical(boolean critical) {
this.critical = critical;
}
public byte[] getValue() {
return value;
}
public void setValue(byte[] value) {
this.value = value;
}
}
2. 创建秘钥对
/***
* 密钥对 生成器
* @param certNum 证书位数
* @return
* @throws NoSuchAlgorithmException
*/
private static KeyPair getKey(int certNum) throws NoSuchAlgorithmException {
// 密钥对 生成器,RSA算法 生成的 提供者是 BouncyCastle
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
// 密钥长度 1024
generator.initialize(certNum);
// 证书中的密钥 公钥和私钥
KeyPair keyPair = generator.generateKeyPair();
return keyPair;
}
3. 生成证书
/**
* RSA证书生成
* @param password 密码
* @param issuerStr 颁发机构信息
* @param subjectStr 使用者信息
* @param certificateCRL 颁发地址
* @param certExpire 证书有效期 (单位天)
* @param certNum 证书位数:一般1024或2048
* @return
*/
public static Map<String, byte[]> createCert(String password, String issuerStr, String subjectStr, String certificateCRL,long certExpire,int certNum) {
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(certNum);
// issuer与 subject相同的证书就是CA证书
Certificate cert = generateCertificateV3(issuerStr, subjectStr, keyPair, result, certificateCRL, null,certExpire);
// cretkey随便写,标识别名
keyStore.setKeyEntry("知录API信息分享中心", 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,long certExpire) {
ByteArrayInputStream bout = null;
X509Certificate cert = null;
try {
System.out.println("公钥:"+ keyPair.getPublic());
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Date notBefore = new Date();
certExpire = 1L * certExpire * 24 * 60 * 60 * 1000;
// Calendar rightNow = Calendar.getInstance();
// rightNow.setTime(notBefore);
// // 日期加1年
// rightNow.add(Calendar.YEAR, 1);
// Date notAfter = rightNow.getTime();
Date notAfter = new Date(System.currentTimeMillis() + certExpire);
// 证书序列号
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");//SHA256withRSA
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( "1.3.14.3.2.26");
// | 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;
}
以上就是生成证书的全部代码了。如果还有不懂的地方可以添加右下角的微信,欢迎骚扰。
也可以通过点击我的gitee来获取源代码哦!
版权声明:本文为weixin_44669878原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。