最近需要添加EMQX的双向认证。通过服务器生成根证书的子证书及私钥发送给设备。
话不多说。直接上DEMO
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import lombok.SneakyThrows;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import sun.misc.BASE64Decoder;
/**
* @author TanJ
* @date 2021/11/16 17:20
*/
public class X509 {
//私钥
private static final String PRIVATE_KEY_S = "-----BEGIN RSA PRIVATE KEY-----\n";
private static final String PRIVATE_KEY_E = "\n-----END RSA PRIVATE KEY-----";
//证书
private static final String CERTIFICATE_S = "-----BEGIN CERTIFICATE-----\n";
private static final String CERTIFICATE_E = "\n-----END CERTIFICATE-----";
static{
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 加载私钥
* @param privateKey
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
// 解码由base64编码的私钥
BASE64Decoder base64Decoder= new BASE64Decoder();
byte[] keyBytes = base64Decoder.decodeBuffer(privateKey);
PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
@SneakyThrows
public static void main(String[] args) {
//加载根证书
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate rootCert = (X509Certificate) cf
.generateCertificate(new FileInputStream("XXXX/ca.pem"));
//提取根证书私钥 注意参数需要去除证书中的前缀及后缀!!
PrivateKey rootPrivateKey = getPrivateKey(
"MIIEvQIBADANBgkqhkiG9w0s...");
// 生成client私钥
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
//填充subject信息
X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
nameBuilder.addRDN(BCStyle.C, "testClient");
BigInteger serial = new BigInteger(Long.toString(System.currentTimeMillis()));
org.bouncycastle.asn1.x500.X500Name subjectName = nameBuilder.build();
// 发行者名称
final X500Name issuerName;
if (rootCert == null) {
// 如果没有根证书,则默认用当前证书
issuerName = subjectName;
} else {
//设置下发证书为根证书信息
issuerName = new X509CertificateHolder(rootCert.getEncoded()).getSubject();
}
//证书过期时间
Date notBefore = new Date(System.currentTimeMillis());
Date notAfter = DateUtil.offsetMonth(notBefore,12);
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(issuerName, serial, notBefore, notAfter, subjectName, keyPair.getPublic());
//-----------
// 添加信息
// final JcaX509ExtensionUtils u = new JcaX509ExtensionUtils();
// if (rootCert != null) {
// // Authority key identifier 2.5.29.35
// certBuilder.addExtension(Extension.authorityKeyIdentifier, false, //u.createAuthorityKeyIdentifier(rootCert));
// }
// Subject key identifier 2.5.29.14
// certBuilder.addExtension(Extension.subjectKeyIdentifier, false,
// u.createSubjectKeyIdentifier(keyPair.getPublic()));
// Key usage 2.5.29.15
// if (rootCert == null) {
// certBuilder.addExtension(Extension.keyUsage, true,
// new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | //KeyUsage.dataEncipherment
// | KeyUsage.keyAgreement | KeyUsage.nonRepudiation | KeyUsage.cRLSign
// | KeyUsage.keyCertSign));
// }
// Basic Constraints 2.5.29.19
// certBuilder.addExtension(Extension.basicConstraints, false, new //BasicConstraints(rootCert == null));
// Extended key usage 2.5.29.37
// certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(
// new KeyPurposeId[] { KeyPurposeId.id_kp_clientAuth, //KeyPurposeId.id_kp_serverAuth }));
// CRL distribution points 2.5.29.31
// String SERVER_BASE_REST_PKI_URL = "123";
// String CRL_URL = "456";
// DistributionPointName distributionPoint = new DistributionPointName(new //GeneralNames(new GeneralName(
// GeneralName.uniformResourceIdentifier, SERVER_BASE_REST_PKI_URL + issuerName + //CRL_URL)));
// DistributionPoint[] distPoints = new DistributionPoint[1];
// distPoints[0] = new DistributionPoint(distributionPoint, null, null);
// certBuilder.addExtension(Extension.cRLDistributionPoints, false, new //CRLDistPoint(distPoints));
//----
//设置使用根证书私钥进行签名。算法使用根证书相同算法
ContentSigner signer = new JcaContentSignerBuilder( rootCert.getSigAlgName()).build(rootPrivateKey);
X509CertificateHolder certHolder = certBuilder.build(signer);
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certHolder);
//拼接证书格式
String privateKeyStr =
PRIVATE_KEY_S + new String(Base64.getEncoder().encode(privateKey.getEncoded()))
+ PRIVATE_KEY_E;
String certificateStr =
CERTIFICATE_S + new String( Base64.getEncoder().encode((certificate.getEncoded())))
+ CERTIFICATE_E;
//以下自行发挥
//TODO
IoUtil.write(new FileOutputStream(".../client.key"), true,
privateKeyStr.getBytes(StandardCharsets.UTF_8));
IoUtil.write(new FileOutputStream(".../client.pem"), true,
certificateStr.getBytes(StandardCharsets.UTF_8));
}
}