学习整理了一个Java OpenSsl X509证书相关的工具类
环境:
JDK:11
加密解密算法:SM2
package com.lighting.iap.cert;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
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.openssl.PEMParser;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.encoders.Hex;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.spec.*;
import java.util.*;
public class X509CertUtil {
//定义使用的常量信息
//要生成的EC的标准名称
private static final String PRIME256V1 = "prime256v1";
//密钥算法的名称 密钥基于椭圆曲线
private static final String KEY_PAIR_ALGORITHM = "EC";
//签名算法是带有 ECDSA 的 SHA256
private static final String SIG_ALG = "SHA256withECDSA";
//证书类型
public static final String X509 = "X.509";
//密钥存储库的类型
public static final String KEY_STORE_TYPE = "PKCS12";
static {
//系统统添加BC加密算法 以后系统中调用的算法都是BC的算法,其实不注册也行
Security.addProvider(new BouncyCastleProvider());
}
public static KeyPair getKeyPair() throws Exception {
//指定用于的参数集生成椭圆曲线(EC)域参数。入参为:要生成的EC的标准名称
//prime256v1 带有NISTP-256算法的椭圆曲线
ECGenParameterSpec ecSpec = new ECGenParameterSpec(PRIME256V1);
//返回一个生成public/private的KeyPairGenerator对象。入参为算法的标准字符串名称。
KeyPairGenerator kf = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
//初始化密钥对生成器随机性的设置和来源。
kf.initialize(ecSpec, new SecureRandom());
//kf.initialize(ecSpec); 也可使用系统的随机数来源
//生成密钥对
KeyPair keyPair = kf.generateKeyPair();
return keyPair;
}
public static PrivateKey toPrivateKey(String privateKey) throws Exception {
//将Base64编码的String解码为新分配的字节数组
byte[] keyBytes =Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec pkcs8 = new PKCS8EncodedKeySpec(keyBytes);
//键工厂用于转换键。入参:请求的密钥算法的名称。 提供程序的名称。
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGORITHM);
//根据提供的密钥规范生成一个私钥对象
return keyFactory.generatePrivate(pkcs8);
}
public static PublicKey toPublicKey(String publicKey) throws Exception {
//将Base64编码的String解码为新分配的字节数组
byte[] bytes = Base64.getDecoder().decode(publicKey);
return getPublicKey(bytes);
}
public static PublicKey getPublicKey(byte[] bytes) throws Exception {
//这个类表示公钥的ASN.1编码
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
//键工厂用于转换键。入参:请求的密钥算法的名称。 提供程序的名称。
KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGORITHM);
//根据提供的密钥规范生成一个公钥对象
return keyFactory.generatePublic(keySpec);
}
public static String encrypt(PublicKey publicKey, String data) {
//加密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
//通过公钥对象获取公钥的基本域参数。
//椭圆曲线(EC)公钥的接口
BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
//椭圆曲线加密(ECC)一起使用的域参数集
ECParameterSpec spec = bcecPublicKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(spec.getCurve(),spec.getG(), spec.getN());
//通过公钥值和公钥基本参数创建公钥参数对象
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
//根据加密模式实例化SM2公钥加密引擎
SM2Engine sm2Engine = new SM2Engine(mode);
//初始化加密引擎
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
//将明文字符串转换为指定编码的字节串
byte[] in = data.getBytes("utf-8");
//通过加密引擎对字节数串行加密
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
} catch (Exception e) {
System.out.println("SM2加密时出现异常:" + e.getMessage());
e.printStackTrace();
}
//将加密后的字节串转换为十六进制字符串
return Hex.toHexString(arrayOfBytes);
}
public static String decrypt(PrivateKey privateKey, String data) {
//解密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
//通过私钥对象获取私钥的基本域参数。
ECParameterSpec spec = bcecPrivateKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(spec.getCurve(),spec.getG(), spec.getN());
//通过私钥值和私钥钥基本参数创建私钥参数对象
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(),ecDomainParameters);
//通过解密模式创建解密引擎并初始化
SM2Engine sm2Engine = new SM2Engine(mode);
sm2Engine.init(false, ecPrivateKeyParameters);
String result = null;
try {
//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)
byte[] cipherDataByte = Hex.decode(data);
//通过解密引擎对密文字节串进行解密
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)
result = new String(arrayOfBytes, "utf-8");
} catch (Exception e) {
System.out.println("SM2解密时出现异常" + e.getMessage());
}
return result;
}
public static String signECDSA(PrivateKey privateKey, String data) {
String resultSign = "";
try {
//Signature类用于为应用程序提供该功能的数字签名算法。入参:算法
Signature signal = Signature.getInstance(SIG_ALG);
//初始化此对象以进行签名
signal.initSign(privateKey);
//更新要签名或验证的数据
signal.update(data.getBytes(StandardCharsets.UTF_8));
//返回所有已更新数据的签名字节。
byte[] sign = signal.sign();
return Base64.getEncoder().encodeToString(sign);
}catch (NoSuchAlgorithmException ne){
System.out.println("算法异常:");
ne.printStackTrace();
}catch (InvalidKeyException ke){
System.out.println("秘钥:");
ke.printStackTrace();
}catch (SignatureException se){
System.out.println("签名异常:");
se.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
return resultSign;
}
public static boolean verifyECDSA(PublicKey publicKey, String signedData, String data) {
try {
//Signature类用于为应用程序提供该功能的数字签名算法。入参:算法
Signature signature = Signature.getInstance(SIG_ALG);
//初始化该对象以供验证。
signature.initVerify(publicKey);
//更新要签名或验证的数据
signature.update(data.getBytes(StandardCharsets.UTF_8));
//将Base64编码的String解码为新分配的字节数组
byte[] signed = Base64.getDecoder().decode(signedData);
//验证传入的签名。如果签名被验证,则为True,否则为false。
boolean bool = signature.verify(signed);
return bool;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
return false;
}
public static X509Certificate readX509CertificateByPath(String crtPath)throws CertificateException, IOException {
//根据路径创建输入流
InputStream inStream = new FileInputStream(crtPath);
//定义证书工厂类。入参:请求的证书类型的名称。
CertificateFactory cf = CertificateFactory.getInstance(X509);
//生成一个证书对象,并从输入流读取的数据。入参:包含证书数据的输入流。
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
return cert;
}
public static void showX509Certificate(X509Certificate x509) {
// JAVA程序中显示证书指定信息
System.out.println("输出证书信息:"+x509.toString());
System.out.println("版本号:"+x509.getVersion());
System.out.println("序列号:"+x509.getSerialNumber().toString(16));
System.out.println("主体名:"+x509.getSubjectDN());
System.out.println("签发者:"+x509.getIssuerDN());
System.out.println("有效起始日期:"+x509.getNotBefore());
System.out.println("有效终止日期:"+x509.getNotAfter());
System.out.println("签名算法:"+x509.getSigAlgName());
byte [] sig=x509.getSignature();//签名值
PublicKey pk=x509.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公钥");
for (int i = 0; i < pkenc.length; i++) {
System.out.print(pkenc[i] + ",");
}
}
private static byte[] readFile(String path) throws Exception {
ByteArrayOutputStream baos;
try (FileInputStream fis = new FileInputStream(path)) {
baos = new ByteArrayOutputStream();
int b = -1;
while ((b = fis.read()) != -1) {
baos.write(b);
}
fis.close();
}
byte[] contents = baos.toByteArray();
baos.close();
return contents;
}
private static String readFileToString(String filePath) throws IOException {
String res ="";
BufferedReader br = new BufferedReader(new FileReader(filePath));
String line;
while ((line = br.readLine()) != null) {
res += line;
}
br.close();
return res;
}
public static PrivateKey getPrivateKeyByPath(String path) throws Exception {
String strKeyPEM = readFileToString(path);
System.out.println(strKeyPEM);
strKeyPEM = strKeyPEM.replace("-----BEGIN EC PRIVATE KEY-----","");
strKeyPEM = strKeyPEM.replace("-----END EC PRIVATE KEY-----","");
System.out.println(strKeyPEM);
return toPrivateKey(strKeyPEM);
}
public static PublicKey getPublicKeyByPath(String path) throws Exception {
byte[] b = readFile(path);
String strKeyPEM = new String(b);
strKeyPEM = strKeyPEM.replace("-----BEGIN PUBLIC KEY-----","");
strKeyPEM = strKeyPEM.replace("-----END PUBLIC KEY-----","");
strKeyPEM = strKeyPEM.replaceAll("[tnr]", "");
return toPublicKey(strKeyPEM);
}
public static PublicKey readX509CertificatePublicKey(String crtPath)throws Exception{
X509Certificate x509Certificate = readX509CertificateByPath(crtPath);
PublicKey publicKey = x509Certificate.getPublicKey();
return publicKey;
}
private static void writeFile(String path, byte[] content) throws Exception {
//如果存在则删除逻辑不考虑了,自行扩展
FileOutputStream fos = new FileOutputStream(path);
fos.write(content);
fos.close();
}
private static void writeObject(String path, Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(object);
oos.close();
}
public static boolean verifyCertificate(String certificatePath) {
return verifyCertificate(new Date(), certificatePath);
}
public static boolean verifyCertificate(Date date, String certificatePath) {
boolean status = true;
try {
// 取得证书
X509Certificate x509Certificate = readX509CertificateByPath(certificatePath);
// 验证证书是否过期或无效
status = verifyCertificate(date, x509Certificate);
} catch (Exception e) {
status = false;
}
return status;
}
private static boolean verifyCertificate(Date date, X509Certificate x509Certificate) {
boolean status = true;
try {
//检查给定的日期是否在证书的日期内有效期
x509Certificate.checkValidity(date);
} catch (Exception e) {
status = false;
}
return status;
}
private static X500NameBuilder userCreateStdBuilder() {
X500NameBuilder x500Nb = new X500NameBuilder(BCStyle.INSTANCE);
x500Nb.addRDN(BCStyle.SERIALNUMBER, "证书序列号");
x500Nb.addRDN(BCStyle.EmailAddress, "邮箱");
x500Nb.addRDN(BCStyle.E, "电子邮箱");
x500Nb.addRDN(BCStyle.UID, "UID");
x500Nb.addRDN(BCStyle.CN, "名字");
x500Nb.addRDN(BCStyle.T, "标题");
x500Nb.addRDN(BCStyle.OU, "组织单位名称");
x500Nb.addRDN(BCStyle.DC, "领域(机构)");
x500Nb.addRDN(BCStyle.O, "组织名称");
x500Nb.addRDN(BCStyle.STREET, "街道");
x500Nb.addRDN(BCStyle.L, "地方名");
x500Nb.addRDN(BCStyle.ST, "省份");
x500Nb.addRDN(BCStyle.C, "国家");
x500Nb.addRDN(BCStyle.UnstructuredName, "主机名称");
x500Nb.addRDN(BCStyle.UnstructuredAddress, "IP");
//BCStyle 详见
return x500Nb;
}
private static X500NameBuilder rootCreateStdBuilder() {
X500NameBuilder x500Nb = new X500NameBuilder(BCStyle.INSTANCE);
x500Nb.addRDN(BCStyle.SERIALNUMBER, "证书序列号");
x500Nb.addRDN(BCStyle.EmailAddress, "邮箱");
x500Nb.addRDN(BCStyle.E, "电子邮箱");
x500Nb.addRDN(BCStyle.UID, "UID");
x500Nb.addRDN(BCStyle.CN, "名字");
x500Nb.addRDN(BCStyle.T, "标题");
x500Nb.addRDN(BCStyle.OU, "组织单位名称");
x500Nb.addRDN(BCStyle.DC, "领域(机构)");
x500Nb.addRDN(BCStyle.O, "组织名称");
x500Nb.addRDN(BCStyle.STREET, "街道");
x500Nb.addRDN(BCStyle.L, "地方名");
x500Nb.addRDN(BCStyle.ST, "省份");
x500Nb.addRDN(BCStyle.C, "国家");
x500Nb.addRDN(BCStyle.UnstructuredName, "主机名称");
x500Nb.addRDN(BCStyle.UnstructuredAddress, "IP");
//BCStyle 详见
return x500Nb;
}
public static PKCS10CertificationRequest createScr(PublicKey publicKeyA,PrivateKey privateKeyA) throws OperatorCreationException {
//申请者DN信息构造
// X500Name 通过传递通用名称,组织单位,组织,位置,州和国家/地区来创建X500Name对象
//X500Name x500Name = userCreateStdBuilder().build();
X500Name x500Name = new X500Name("CN=zhangsan,L=shanghai,ST=Shang Hai,O=个人,OU=个人专用,C=China,STREET=街道");
//使用Dn和PublicKey初始化PKCS10。(组装信息)
JcaPKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(x500Name, publicKeyA);
//使用标准算法获取签名实例。(私钥加密)
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(SIG_ALG);
//使用私钥对CertificationRequestInfo进行数字签名得到签名值;
ContentSigner signer = csBuilder.build(privateKeyA);
//组合CertificationRequestInfo信息和签名得到最终的CSR文件
PKCS10CertificationRequest csr = p10Builder.build(signer);// PKCS10的请求
return csr;
}
public static byte[] createScrToByte(PublicKey publicKeyA,PrivateKey privateKeyA) throws OperatorCreationException, IOException {
PKCS10CertificationRequest csr = createScr(publicKeyA, privateKeyA);
String csrStr = "-----BEGIN CERTIFICATE REQUEST-----n" +
org.apache.commons.codec.binary.Base64.encodeBase64String(csr.getEncoded())
+ "n-----END CERTIFICATE REQUEST-----n";
return csrStr.getBytes();
}
public static PKCS10CertificationRequest readCsr(InputStream in) throws Exception {
//readAllBytes 适合读取小文件,大文件可能会出现内存不足,导致堆溢出
byte[] bytes = in.readAllBytes();
in.close();
String contents = new String(bytes, StandardCharsets.UTF_8);
//一个内部缓冲区 流的来源或目的地并不一定是文件,也可以是内存中的一块空间,将字节数组当作流输入来源、输出目的地的类
ByteArrayInputStream pemStream = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
//转换字符串是为了防止乱码
//ByteArrayInputStream pemStream2 = new ByteArrayInputStream(bytes);
//用于读取字符流
Reader pemReader = new BufferedReader(new InputStreamReader(pemStream));
PEMParser pemParser = new PEMParser(pemReader);
Object parsedObj = pemParser.readObject();
pemStream.close();
pemReader.close();
pemParser.close();
//使用PKCS10CertificationRequestBuilder来构造CSR
if (parsedObj instanceof PKCS10CertificationRequest) {
return (PKCS10CertificationRequest) parsedObj;
}else{
throw new Exception("无法读取csr");
}
}
public static X509Certificate createRootCert(PublicKey publicKeyCA,PrivateKey privateKeyCA) throws IOException, OperatorCreationException, CertificateException {
//定义颁布者DN(根证书是自己颁布给自己的)
//X500Name issuerDn = rootCreateStdBuilder().build();
X500Name issuerDn = new X500Name("CN=CA,L=shanghai,ST=Shang Hai,O=CA组织,OU=ca单位,C=China,STREET=街道");
//serial 证书序列号
BigInteger serial=BigInteger.valueOf(System.currentTimeMillis());
//组装公钥信息
SubjectPublicKeyInfo subjectPublicKeyInfo =
SubjectPublicKeyInfo.getInstance(new ASN1InputStream(publicKeyCA.getEncoded()).readObject());
//定义证书有效期
long year = 360 * 24 * 60 * 60 * 1000;
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + year);
//构造X.509 第3版的证书构建者 设置证书的基本参数。X509V3是数字证书的一种通用规范标准
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuerDn, //颁发者信息
serial, //证书序列号
notBefore, //证书生效日期
notAfter, //证书失效日期
issuerDn, //使用者信息(PS:由于是自签证书,所以颁发者和使用者DN都相同)
subjectPublicKeyInfo);//公钥
//基础约束,标识是否是CA证书,这里true标识为CA证书
builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
// 设置密钥用法
//证书的签名数据 用于为应用程序提供该功能的数字签名算法。使用数字签名数字数据的认证和完整性保证
//使用标准算法获取签名实例。
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(SIG_ALG);
//进行数字签名得到签名值;(私钥用于签名)
ContentSigner signer = csBuilder.build(privateKeyCA);
//绑定算法,给证书签名
X509CertificateHolder holder = builder.build(signer);
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(holder);;
return certificate;
}
public static void createRootCertSaveToFile(PublicKey publicKeyCA,PrivateKey privateKeyCA, String path){
try {
X509Certificate rootCert = createRootCert(publicKeyCA, privateKeyCA);
writeFile(path, rootCert.getEncoded());
}catch (Exception e){
e.printStackTrace();
}
}
public static X509Certificate createIssueCert(PKCS10CertificationRequest pkc10,X509Certificate rootCert,PrivateKey privateKeyCA)
throws Exception {
//获取证书请求中信息
//X500Name subjectDn = pkc10.getSubject();
//证书公钥相关信息
//获取公钥(如果获取不到或异常使用上面方法试试)
byte[] bytes = pkc10.getSubjectPublicKeyInfo().toASN1Primitive().getEncoded("UTF-8");
PublicKey publicKeyA = getPublicKey(bytes);
//定义证书有效期
long year = 360 * 24 * 60 * 60 * 1000;
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + year);
//serial 证书序列号
BigInteger serial=BigInteger.valueOf(System.currentTimeMillis());
//X509Certificate var1, BigInteger var2, Date var3, Date var4, X500Name var5, PublicKey var6
//构造X.509 第3版的证书构建者 设置证书的基本参数。X509V3是数字证书的一种通用规范标准
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(rootCert, //颁发者
serial, //证书序列号
notBefore, //证书生效日期
notAfter, //证书失效日期
pkc10.getSubject(), //使用者信息
publicKeyA);//使用者公钥(构造信息)
//基础约束,标识是否是CA证书,这里false标识为实体证书
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
//使用标准算法获取签名实例。
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(SIG_ALG);
//进行数字签名得到签名值;(CA私钥。私钥用于签名)
ContentSigner signer = csBuilder.build(privateKeyCA);
//绑定算法,给证书签名
X509CertificateHolder holder = builder.build(signer);
X509Certificate issueCert = new JcaX509CertificateConverter().getCertificate(holder);;
return issueCert;
}
public static void createIssueCertSaveToFile(PKCS10CertificationRequest pkc10,X509Certificate rootCert,PrivateKey privateKeyCA, String path){
try {
X509Certificate issueCert = createIssueCert(pkc10, rootCert, privateKeyCA);
writeFile(path, issueCert.getEncoded());
}catch (Exception e){
e.printStackTrace();
}
}
public static boolean verifyRootCertSign(X509Certificate rootCert)
throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException, SignatureException {
//Signature类用于为应用程序提供该功能的数字签名算法。入参:算法
Signature signature = Signature.getInstance(rootCert.getSigAlgName());
//初始化该对象以供验证。入参:—要验证其签名的身份的证书。
signature.initVerify(rootCert);
//更新或验证数据
signature.update(rootCert.getTBSCertificate()); //从该证书获取der编码的证书信息,即tbcertificate。这可以用来独立地验证签名。
//验证传入的签名。入参:需要验证的签名字节数。
boolean verifySign = signature.verify(rootCert.getSignature());
return verifySign;
}
public static boolean verifyIssueCertSign(X509Certificate issueCert,PublicKey publicKeyCA)
throws NoSuchAlgorithmException, InvalidKeyException, CertificateEncodingException, SignatureException {
//Signature类用于为应用程序提供该功能的数字签名算法。入参:算法
Signature signature = Signature.getInstance(issueCert.getSigAlgName());
//初始化该对象以供验证。入参:—待验证身份的公钥。
signature.initVerify(publicKeyCA);
//更新或验证数据
signature.update(issueCert.getTBSCertificate());//从该证书获取der编码的证书信息,即tbcertificate。这可以用来独立地验证签名。
//验证传入的签名。入参:需要验证的签名字节数。
boolean verifySign = signature.verify(issueCert.getSignature());
return verifySign;
}
public static boolean verify(X509Certificate X509certificateRoot,
Collection collectionX509CertificateChain,
X509CRL X509crl,
String stringTarget) {
//获取证书链长度
int nSize = collectionX509CertificateChain.size();
//将证书链转化为数组
X509Certificate[] arX509certificate = new X509Certificate[nSize];
collectionX509CertificateChain.toArray(arX509certificate);
//声明list,存储证书链中证书主体信息
ArrayList list = new ArrayList();
//沿证书链自上而下,验证证书的所有者是下一个证书的颁布者
Principal principalLast = null;
for (int i = 0; i < nSize; i++) {//遍历arX509certificate
X509Certificate x509Certificate = arX509certificate[i];
//获取发布者标识
Principal principalIssuer = x509Certificate.getIssuerDN();
//获取证书的主体标识
Principal principalSubject = x509Certificate.getSubjectDN();
//保存证书的序列号
list.add(x509Certificate.getSerialNumber());
if (principalLast != null) {
//验证证书的颁布者是上一个证书的所有者
if (principalIssuer.equals(principalLast)) {
try {
//获取上个证书的公钥
PublicKey publickey = arX509certificate[i - 1].getPublicKey();
//验证是否已使用与指定公钥相应的私钥签署了此证书
arX509certificate[i].verify(publickey);
} catch (Exception e) {
return false;
}
} else {
return false;
}
}
principalLast = principalSubject;
}
//验证根证书是否在撤销列表中
try {
if (!X509crl.getIssuerDN().equals(X509certificateRoot.getSubjectDN())) return false;
X509crl.verify(X509certificateRoot.getPublicKey());
} catch (Exception e) {
return false;
}
//在当前时间下,验证证书链中每个证书是否存在撤销列表中
if (X509crl != null) {
try {
//获取CRL中所有的项
Set setEntries = X509crl.getRevokedCertificates();
if (setEntries == null && setEntries.isEmpty() == false) {
Iterator iterator = setEntries.iterator();
while (iterator.hasNext()) {
X509CRLEntry X509crlentry = (X509CRLEntry) iterator.next();
if (list.contains(X509crlentry.getSerialNumber())) return false;
}
}
} catch (Exception e) {
return false;
}
}
//证明证书链中的第一个证书由用户所信任的CA颁布
try {
PublicKey publickey = X509certificateRoot.getPublicKey();
arX509certificate[0].verify(publickey);
} catch (Exception e) {
return false;
}
//证明证书链中的最后一个证书的所有者正是现在通信对象
Principal principalSubject = arX509certificate[nSize - 1].getSubjectDN();
if (!stringTarget.equals(principalSubject.getName())) return false;
//验证证书链里每个证书是否在有效期里
Date date = new Date();
for (int i = 0; i < nSize; i++) {
try {
arX509certificate[i].checkValidity(date);
} catch (Exception e) {
return false;
}
}
return true;
}
//------------------------------KeyStore---------------------------------------------
public static KeyStore createKeyStore(String storePass)throws Exception {
//Keytool是一个Java数据证书的管理工具 单理解为一个存放应用签名的文件 入参:密钥存储库的类型。PKCS12/JKS
KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
//从给定的输入流加载这个KeyStore。
//入参1:从其中加载密钥存储库的输入流。如果新建,则为NULL
//入参2:用于解锁密钥存储库的密码
ks.load(null, storePass.toCharArray());
return ks;
}
public static void saveKeyStoreByPath(KeyStore ks, String path,String pwd)throws Exception{
//定义文件输出对象,将修改后的keyStore存储
FileOutputStream output = new FileOutputStream(path);
//将此密钥存储到给定的输出流,并用密码保护
//入参1:将此密钥存储库写入其中的输出流。
//入参2:生成密钥存储库完整性检查的密码
ks.store(output,pwd.toCharArray());
output.close();
}
public static KeyStore loadKeyStore(String path, String storePass) throws Exception{
//读取文件
FileInputStream fin = new FileInputStream(path);
//Keytool是一个Java数据证书的管理工具 单理解为一个存放应用签名的文件 入参:密钥存储库的类型。PKCS12/JKS
KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
//从给定的输入流加载这个KeyStore。
//入参1:从其中加载密钥存储库的输入流。如果新建,则为NULL
//入参2:用于解锁密钥存储库的密码
ks.load(fin, storePass.toCharArray());
fin.close();
return ks;
}
public static X509Certificate getCertificateByStore(String keyStorePath,String alias, String stroePwd) throws Exception {
//获得keyStroe
KeyStore ks = loadKeyStore(keyStorePath, stroePwd);
//返回与给定别名关联的证书。
X509Certificate certificate = (X509Certificate) ks.getCertificate(alias);
return certificate;
}
public static PrivateKey readPrivateKey(String alias, String pfxPath,String storePwd,String certPwd) throws Exception {
KeyStore keyStore = loadKeyStore(pfxPath, storePwd);
//返回与给定别名关联的键,使用给定的密码恢复它。
//创建证书时,放入证书的私钥,对应别名(ca的私钥)
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, certPwd.toCharArray());
return privateKey;
}
public static void listKeyAndCertificate(KeyStore ks, String storePass,String priKeyPass) throws Exception {
//列出此密钥存储库的所有别名。
Enumeration enum1 = ks.aliases();
//测试此枚举是否包含更多元素。
while (enum1.hasMoreElements()) {
//获取别名
String alias = (String) enum1.nextElement();
//返回与给定别名关联的证书。
java.security.cert.Certificate c = ks.getCertificate(alias);
//读取并打印证书
showX509Certificate((X509Certificate) c);
//列出store中私钥和证书链信息
readPriKey(ks, alias, priKeyPass);
}
}
public static void readPriKey(KeyStore ks, String alias, String pass)throws Exception {
//返回与给定别名关联的键,使用给定的密码恢复它。
Key key;
key = ks.getKey(alias, pass.toCharArray());
if (null == key) {
return;
}
System.out.println("key :" + key);
System.out.println("Key的算法=" + key.getAlgorithm());
System.out.println("Key的编码格式=" + key.getFormat());
//列出store中证书链信息
readCertChain(ks, alias);
}
public static void readCertChain(KeyStore ks, String alias) throws Exception {
//返回与给定别名关联的证书链
Certificate[] certChain = ks.getCertificateChain(alias);
if (null == certChain) {
return;
}
for (Certificate c : certChain) {
//读取并打印证书信息
showX509Certificate((X509Certificate) c);
}
}
public static void changePassword(String storePath,String oldPwd,String newPwd) throws Exception{
//获得keyStore
KeyStore ks = loadKeyStore(storePath, oldPwd);
saveKeyStoreByPath(ks, storePath, newPwd);
}
public static void deleteAlias(String certPath,String password,String alias,String entry) throws Exception{
//获得keyStore
KeyStore ks = loadKeyStore(certPath, password);
//验证是否存在此别名
if(ks.containsAlias(alias)){
//从这个密钥存储库中删除由给定别名标识的条目。 入参:别名
ks.deleteEntry(entry);
//删除后重新存储
saveKeyStoreByPath(ks, certPath, password);
}else{
throw new Exception("该证书未包含别名--->"+alias);
}
}
public static void testKeyStore(){
try {
String keyStroeFielPath = "E:/CertTest/testKeyStore.pfx";
String newKsPwd = "666666";
//存储根证书的别名
String caAlias = "caCert";
//存储根证书的密码
String caPwd = "111222";
//创建一个KeyStore
String keyStroePwd = "112233";
KeyStore keyStore = createKeyStore(keyStroePwd);
System.out.println("创建KeyStore:" + keyStore.toString());
//将KeyStore存储到指定路径
saveKeyStoreByPath(keyStore, keyStroeFielPath, keyStroePwd);
System.out.println("存储到本地完成");
//修改密码
changePassword(keyStroeFielPath, keyStroePwd, newKsPwd);
System.out.println("修改密码完成,新密码为:" + newKsPwd);
//加载本地KeyStore
KeyStore loadKeyStore = loadKeyStore(keyStroeFielPath, newKsPwd);
System.out.println("读取本地KeyStore:n" + loadKeyStore);
//创建根证书并存储到KeyStore中
KeyPair caKeypair = getKeyPair();
PublicKey caPublicKey = caKeypair.getPublic();
PrivateKey caPrivateKey = caKeypair.getPrivate();
X509Certificate rootCert = createRootCert(caPublicKey, caPrivateKey);
//存储
loadKeyStore.setKeyEntry(caAlias,//证书别名
caPrivateKey,//私钥
caPwd.toCharArray(), //密码
new Certificate[]{rootCert} //证书链
);
//切记在存储一次,不然keyStore没存进去
saveKeyStoreByPath(loadKeyStore, keyStroeFielPath, newKsPwd);
//读取证书并打印
X509Certificate caRootCert = getCertificateByStore(keyStroeFielPath, caAlias, newKsPwd);
System.out.println(caRootCert);
showX509Certificate(caRootCert);
System.out.println("n");
PrivateKey privateKey = readPrivateKey(caAlias, keyStroeFielPath, newKsPwd, caPwd);
String caPk = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println("读取的Base64公钥:" + caPk);
}catch (Exception e){
e.printStackTrace();
System.out.println("KeyStore测试中出现问题");
}
}
public static void main(String[] args) {
try {
//初始化获取公钥和私钥
//生成csr(证书请求文件)
//testKeyStore();
}catch (Exception e){
e.printStackTrace();
System.out.println("出现异常");
}
}
}



