证书生成
基于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来获取源代码哦!