整理了X.509格式证书相关的工具类,便于自己学习和使用。
使用的JDK为11.0.2。
加密算法采用SM2(椭圆曲线公钥密码算法,也叫国密算法),加密算法可以根据不同的JDK进行调整。加密类Cipher可以参考:Class Cipher
具体代码,参考最下面,包含:秘钥生成相关方法(KeyPaif),公钥加密(SM2),私钥解密,私钥对信息签名,公钥验证签名,X.509证书的读取、生成、查看、验证,DN信息,CSR文件的生成、读取,KeyStore相关操作等。
san(subjectAlternativeName)这些证书扩展自行搜索。
名词理解:
- TLS:传输层安全协议。
- SSL:安全套接字层。
- KEY:通常指私钥。
- CSR:即证书签名请求(发送方)。
- CRT:即CA生成的证书。
- X.509:是一种证书格式。对X.509证书来说,认证者总是CA或由CA指定的人。X.509的证书文件,一般以.crt结尾,根据该文件的内容编码格式,可以分为以下二种格式:
PEM - 打开看文本格式,以"-----BEGIN..."开头, "-----END..."结尾,内容是BASE64编码。
DER - 打开看是二进制格式,不可读。 - 私钥和公钥:每一方都可以拥有自己的公钥私钥,公钥用于公开,私钥用于加密。(公钥私钥可以说没多大区别,只是根据使用功能进行区分) 私钥通常用来生成签名,公钥用来验证签名。
- 数字签名:摘要经过私钥加密后,便有了一个新的名字--数字签名。
签名 是在发送方,这是一个加密(私钥加密)的过程。
验签 是在接收方,这是一个解密(公钥解密)的过程。 - 数字证书: 就是一个.crt文件。由CA颁发。发送者私钥、信息、域名等 以csr文件发送给CA。CA生成私钥公钥。使用私钥加密,生成签名。返回证书。
流程理解:
证书体系中存在三种角色:
A:发送方
B:接受方
CA:第三方认证机构或下属
流程:
- A生成公钥私钥,A信息 + 域名等 + PublicKeyA(A公钥),整合一起生成.csr。将这个.csr文件发给 CA。
- CA收到.csr信息后,会验证申请信息,然后使用算法对.csr里的明文信息先做一个HASH,得到一个信息摘要,再用CA自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的数字签名。 签名 + .csr明文信息 = 证书。
- 签名对象包括需要在证书中说明的内容,比如A的公钥、时间戳、序列号等。
假设证书中只有:A的公钥PublicKeyA、时间戳TIME1、序列号IDA。
那么CA发送给A的简单证书凭证可表达 为:CertA = Eca[TIME1, IDA, PublicKeyA];
A得到自己的证书CertA和 CA的 PublicKeyCA - B同样把自己的公钥PublicKeyB送到CA;
B得到CA发布的证书CertB和CA的PublicKeyCA(步骤3); - A告知B证书CertA; CertA=Eca[TIME1,IDA,PublicKeyA];
- B告知A证书CertB。 CertA=Eca[TIME1,IDA,PublicKeyB];
- A、B各自得到对方证书后,利用从CA得到的公钥(在CA的自签证书中)验证彼此对方的证书是否有效,如果有效,那么就得到了彼此的公钥。
利用对方的公钥,可以加密数据,也可以用来验证对方的数字签名。 - 单向验证情况,CA将CertA(A的证书)+ PublicKeyCA(CA公钥)发送给B。
B根据PublicKeyCA获得A的PublicKeyA,可以加密数据,也可以用来验证对方的数字签名。保证A可以正常访问B。
发送方:
将消息进行散列运算,得到消息摘要。
使用自己的私钥对消息摘要加密(认证模式:确保了接收方能够确认自己)。
使用接收方的公钥对消息进行加密(加密模式:确保了消息只能由期望的接收方解密)。
发送消息和消息摘要。 A用B公钥加密
接收方:
使用发送方的公钥对消息摘要进行解密(确认了消息是由谁发送的)。
使用自己的私钥对消息进行解密(安全地获得了实际应获得的信息)。
将消息进行散列运算,获得消息摘要。
将上一步获得的消息摘要 和 第一步解密的消息摘要进行对比(确认了消息是否被篡改)。
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 reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
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.*;
/**
* X509证书工具类
*/
public class X509CertUtil {
//定义使用的常量信息
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());
}
/**
* 生成密钥对
*
* @return 密钥对
*/
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;
}
/**
* 从string转private key
* @param privateKey 私钥(BASE64编码)字符串
*
* @return 私钥对象
*/
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);
}
/**
* 从string转publicKey
*
* @param publicKey 公钥(BASE64编码)的字符串
* @return 公钥对象
*/
public static PublicKey toPublicKey(String publicKey) throws Exception {
//将Base64编码的String解码为新分配的字节数组
byte[] bytes = Base64.getDecoder().decode(publicKey);
return getPublicKey(bytes);
}
/**
* 从byte[]转publicKey
*
* @param bytes 公钥(BASE64编码)的字节数组
* @return 公钥对象
*/
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);
}
/**
* 公钥加密(算法SM2加密解密)
*
* @param publicKey SM2公钥
* @param data 明文数据
* @return 密文
*/
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);
}
/**
* 私钥解密(算法SM2加密解密)
*
* @param privateKey SM私钥
* @param data 密文数据
*
* @return 解密后数据
*/
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;
}
/**
* 用私钥对信息生成数字签名
*
* @param privateKey 私钥对象
* @param data 需要签名的数据
*
* @return 签名后数据
*/
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;
}
/**
* 使用公钥验证签名
*
* @param publicKey 公钥
* @param signedData 签名后数据
* @param data 原数据
*
* @return boolean
*/
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;
}
/**
* 读取X.509证书
*
* @param crtPath 证书路径
* @return X.509证书对象
*/
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;
}
/**
* 列出x509Certificate的基本信息
* 测试及查看使用
*
* @param x509 证书对象
*/
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] + ",");
}
}
/**
* 按照指定路径读取文件,并返回字节流
* 读取Key文件时,此方法会将文件中的回车、空行等读取。
*
* @param path 文件地址
* @return 文件字节流
*/
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;
}
/**
* 按照指定路径读取文件,返回字符串
* 读取Key文件时,此方法不会读取到回车信息。
*
* @param filePath
* @return 文件内容
*/
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;
}
/**
* 根据路径读取私钥文件
* 测试及查看使用
*
* @param path 路径
* @return 私钥对象
*/
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);
}
/**
* 根据路径读取公钥文件
* 测试及查看使用(读取OpenSSL生成的PEM公钥文件)
*
* @param path 路径
*
* @return 公钥对象
*/
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("[\t\n\r]", "");
return toPublicKey(strKeyPEM);
}
/**
* 读取证书中的公钥
*
* @param crtPath 证书路径
*
* @return 公钥对象
*/
public static PublicKey readX509CertificatePublicKey(String crtPath)throws Exception{
X509Certificate x509Certificate = readX509CertificateByPath(crtPath);
PublicKey publicKey = x509Certificate.getPublicKey();
return publicKey;
}
/**
* 按照指定路径写入文件
*
* @param path 文件地址
* @param content 写入字节
*/
private static void writeFile(String path, byte[] content) throws Exception {
//如果存在则删除逻辑不考虑了,自行扩展
FileOutputStream fos = new FileOutputStream(path);
fos.write(content);
fos.close();
}
/**
* 将对象写入指定路径中
*
* @param path 路径
* @param object 写入对象
*/
private static void writeObject(String path, Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(object);
oos.close();
}
/**
* 验证Certificate
*
* @param certificatePath 证书路径
*
* @return boolean
*/
public static boolean verifyCertificate(String certificatePath) {
return verifyCertificate(new Date(), certificatePath);
}
/**
* 验证Certificate是否过期或无效
*
* @param date 日期
* @param certificatePath 证书路径
*
* @return boolean
*/
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;
}
/**
* 验证证书是否过期或无效
*
* @param date 日期
* @param x509Certificate 证书路径
*
* @return boolean
*/
private static boolean verifyCertificate(Date date, X509Certificate x509Certificate) {
boolean status = true;
try {
//检查给定的日期是否在证书的日期内有效期
x509Certificate.checkValidity(date);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* user DN信息构造。示例方法,不测试
*/
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;
}
/**
* ca DN信息构造。示例方法,不测试
*/
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;
}
/**
* 创建CSR(生成证书请求文件),返回CSR文件对象(CSR也叫做认证申请,是一个发送到CA的请求认证信息。)
*
* CSR文件生成步骤如下:
* 根据Dn、 Public Key组装请求证书信息;
* 用Private Key加密证书请求信息(对申请信息签名);
* 根据请求信息、签名算法和签名生成CSR文件;
* 注意:私钥不包含在CSR文件中,但是应用于数字签名。
*
* @param publicKeyA 申请证书请求文件者的公钥,用于封装
* @param privateKeyA 申请证书请求文件者的私钥,用于根据私钥加密
*
* @return PKCS10(参介绍)
*/
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;
}
/**
* 创建CSR(生成证书请求文件),返回CSR文件字节流
*
* @param publicKeyA 申请证书请求文件者的公钥,用于封装
* @param privateKeyA 申请证书请求文件者的私钥,用于根据私钥加密
*
* @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();
}
/**
* 读取CSR文件
*
* @param in csr文件流对象
*
* @return PKCS10(参介绍)
*/
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");
}
}
/**
* 根据证书请求文件(CSR)提取信息
* Tuple (参阅java元组相关介绍)
*
* @param inputStream csr文件流对象
*
* @return Tuple T1 csr中的DN信息,T2 csr中公钥信息
* */
public static Tuple2<X500Name, byte[]> readCsrResultTuple(InputStream inputStream) throws Exception {
PKCS10CertificationRequest pkcs10 = readCsr(inputStream);
X500Name x500Name = pkcs10.getSubject();
byte[] publicKeyBytes = pkcs10.getSubjectPublicKeyInfo().toASN1Primitive().getEncoded("UTF-8");
return Tuples.of(x500Name, publicKeyBytes);
}
/**
* CA 创建根证书
* 根证书是认证中心与用户建立信任关系的基础。
* 根证书是对主要是对网站的鉴权,个人证书是对个人的鉴权。
* 个人证书要在根证书基础上,根证书不被认可,你就不要想操作了。
* 根证书就是自己给自己颁布证书
*
* @param publicKeyCA CA公钥,用于组装信息
* @param privateKeyCA CA私钥,用于签名
*
* @return 证书对象
*/
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));
// 设置密钥用法
/* ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
builder.addExtension(keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));*/
/*// 添加扩展信息 Extension
// 设置密钥用法
builder .addExtension(Extension.keyUsage, false, new X509KeyUsage(X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation))
// 设置扩展密钥用法:客户端身份认证、安全电子邮件
builder.addExtension(Extension.extendedKeyUsage, false, extendedKeyUsage())
// 基础约束,标识是否是CA证书,这里false标识为实体证书
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false))
// Netscape Cert Type SSL客户端身份认证
builder.addExtension(MiscObjectIdentifiers.netscapeCertType, false, new NetscapeCertType(NetscapeCertType.sslClient));
//SAN subjectAlternativeName
//支持多域名(备选使用者名称)
DERSequence subjectAlternativeNames = new DERSequence(new ASN1Encodable[]{
new GeneralName(GeneralName.dNSName,"localhost"),
new GeneralName(GeneralName.rfc822Name,"demo@test.test"),
new GeneralName(GeneralName.iPAddress,"127.0.0.1"),
new GeneralName(GeneralName.otherName,""),
new GeneralName(GeneralName.x400Address,"null"),
new GeneralName(GeneralName. directoryName,"null"),
new GeneralName(GeneralName.ediPartyName,"null"),
new GeneralName(GeneralName.uniformResourceIdentifier,"null"),
new GeneralName(GeneralName.registeredID,"null")
});
builder.addExtension(X509Extensions.SubjectAlternativeName,false,subjectAlternativeNames);
builder.addExtension(X509Extensions.SubjectAlternativeName,false,new GeneralName(GeneralName.dNSName,"localhost"));
//X509Extensions 不推荐使用的话,可使用Extension
builder.addExtension(Extension.subjectAlternativeName,false,new GeneralName(GeneralName.dNSName,san));
*/
//使用标准算法获取签名实例。
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(SIG_ALG);
//进行数字签名得到签名值;(私钥用于签名)
ContentSigner signer = csBuilder.build(privateKeyCA);
//绑定算法,给证书签名
X509CertificateHolder holder = builder.build(signer);
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(holder);;
return certificate;
}
/**
* 生成根证书并存储到指定路径
* 测试使用
*
* @param publicKeyCA CA公钥,用于组装信息
* @param privateKeyCA CA私钥,用于签名
* @param path 根证书存储路径
*/
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();
}
}
/**
* 颁布证书(根据证书请求文件生成用户证书)
* 其实主要是使用根证书私钥为其签名
*
* @param pkc10 证书请求文件对象
* @param rootCert 根证书
* @param privateKeyCA CA私钥
*
* @return 证书对象
*/
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());
//构造X.509 第3版的证书构建者 设置证书的基本参数。X509V3是数字证书的一种通用规范标准
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
rootCert, //颁发者
serial, //证书序列号
notBefore, //证书生效日期
notAfter, //证书失效日期
pkc10.getSubject(), //使用者信息
publicKeyA);//使用者公钥(构造信息)
/*
//直接根据根证书创建证书
X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(
rootCertificate,
serialNumber,
from, to,
new X500Principal(subject.toSubject()),
keyPair.getPublic());*/
//基础约束,标识是否是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;
}
/**
* 生成用户证书并存储到指定路径
* 测试使用
*
* @param pkc10 证书请求文件对象
* @param rootCert 根证书
* @param privateKeyCA CA私钥
* @param path 根证书存储路径
*/
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();
}
}
/**
* 验证根证书签名
* (可以使用公钥验签替代,这里只是提供更多的选择)
*
* @param rootCert 根证书对象
*/
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;
}
/**
* 验证用户证书签名
* (原理:其实就是使用CA的公钥,验证用户申请证书时提交的签名是否合理)
* (可以使用公钥验签替代,这里只是提供更多的选择)
*
* @param issueCert 用户证书
* @param publicKeyCA CA公钥
*/
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;
}
/**
* 证书校验(供参考)
* (本次不使用不测试)
*
* @param X509certificateRoot 根证书
* @param collectionX509CertificateChain
* @param X509crl X.509证书撤销列表(CRL)的抽象类。
* CRL是一个带有时间戳的列表,用于标识已撤销的证书。它由证书颁发机构(CA)签名,并在公共存储库中免费提供。
* @param stringTarget 证明证书链中的最后一个证书的所有者名称
*/
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---------------------------------------------
* PKCS12,一种标准的密钥库类型,可以在Java和其他语言中使用。可以参考sun.security.pkcs12.PKCS12KeyStore类。
* 它通常具有p12或pfx的扩展名。可以在此类型上存储私钥,密钥和证书。
* 与JKS不同,PKCS12密钥库上的私钥可以用Java提取。此类型是可以与其他语言(如C,C++或C#)编写的其他库一起使用
* 目前,Java中的默认密钥库类型是JKS
*
* 无法使用JDK将没有关联证书链的私钥存储到密钥库中
* */
/**
* 创建Keytool对象
* @param storePass 用于解锁密钥存储库的密码
*
* @return 证书管理工具对象
*/
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;
}
/**
* 将KeyStore存储到指定路径
* @param ks keyStore
* @param path 路径
* @param pwd 密码
*/
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();
}
/**
* 根据路径加载keyStore
*
* @param path keyStore文件的路径(例:F:\test.pfx)
* @param storePass 密钥存储库,用于解锁密钥存储库的密码
*
* @return 证书管理工具对象
*/
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;
}
/**
* 获得keyStore的证书对象
*
* @param keyStorePath
* @param alias
* @param stroePwd
*
* @return 证书对象
*/
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;
}
/**
* 读取(keyStroe文件)PFX文件中的私钥
*
* @param alias 别名
* @param pfxPath PFX文件路径
* @param storePwd KeyStore密码
* @param certPwd 对应别名的密码
*
* @return 私钥对象
*/
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;
}
/**
* 列出store中所有的私钥和公钥 以及签名信息。
* 测试及查看使用
*
* @param ks KeyStore对象
* @param storePass KeyStore密码
* @param priKeyPass 恢复秘钥的密码
*/
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);
}
}
/**
* 列出store中私钥和证书链信息
* 测试及查看使用
*
* @param ks KeyStore对象
* @param alias 别名
* @param pass 恢复秘钥的密码
*/
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);
}
/**
* 列出store中证书链信息
* 测试及查看使用
*
* @param ks KeyStore对象
* @param 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);
}
}
/**
* 修改密码(在设置一次密码将原来的覆盖)
* @param storePath store地址
* @param oldPwd 原始密码
* @param newPwd 新密码
*/
public static void changePassword(String storePath,String oldPwd,String newPwd) throws Exception{
//获得keyStore
KeyStore ks = loadKeyStore(storePath, oldPwd);
saveKeyStoreByPath(ks, storePath, newPwd);
}
/** 删除证书(本次不使用不测试)
* @param certPath 证书地址
* @param password 密码
* @param alias 别名
* @param entry 条目
*/
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 main(String[] args) {
try {
//初始化获取公钥和私钥
KeyPair keypair = getKeyPair();
PublicKey publicKey = keypair.getPublic();
PrivateKey privateKey = keypair.getPrivate();
/* System.out.println("生成的公钥:" + publicKey);
System.out.println("生成的私钥:" + privateKey);
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println("Base64公钥:" + publicKeyBase64);
System.out.println("Base64私钥:" + privateKeyBase64);
System.out.println("---------------");
PublicKey pu1 = toPublicKey(publicKeyBase64);
PrivateKey pv1 = toPrivateKey(privateKeyBase64);
String publicKeyBase642 = Base64.getEncoder().encodeToString(pu1.getEncoded());
String privateKeyBase642 = Base64.getEncoder().encodeToString(pv1.getEncoded());
System.out.println("公钥转回去:" + publicKeyBase642);
System.out.println("私钥转回去:" + privateKeyBase642);
if (publicKeyBase64.equals(publicKeyBase642) && privateKeyBase64.equals(privateKeyBase642)){
System.out.println("转换完成");
}*/
String con = "测试加密数据";
System.out.println("加密之前:" + con);
//加密
String content = encrypt(publicKey, con);
System.out.println("加密之后:" + content);
//解密
String contentDe = decrypt(privateKey, content);
//解密之后
System.out.println("解密之后:" + contentDe);
//私钥签名
String signVString = "签名数据";
String signVerStr = signECDSA(privateKey, signVString);
System.out.println("签名后:" + signVerStr);
//公钥验签
boolean veBl = verifyECDSA(publicKey, signVerStr, signVString);
if (veBl){
System.out.println("验签通过");
}else{
System.out.println("验签没通过");
}
//读取证书并打印
/*String crtFilePath = "E:/CertTest/pra-rest-if/Local-BM-Service.crt";
X509Certificate x509Certificate = readX509CertificateByPath(crtFilePath);
showX509Certificate(x509Certificate);*/
//公钥私钥写入本地文件
/*String publucKeyPemText = "-----BEGIN PUBLIC KEY-----\n" +
Base64.getEncoder().encodeToString(publicKey.getEncoded()) +
"\n-----BEGIN PUBLIC KEY-----\n";
writeFile("E:/CertTest/testPublicKey.pem", publucKeyPemText.getBytes(StandardCharsets.UTF_8));
String privateKeyPemText = "-----BEGIN EC PRIVATE KEY-----\n" +
Base64.getEncoder().encodeToString(privateKey.getEncoded()) +
"\n-----END EC PRIVATE KEY-----\n";
writeFile("E:/CertTest/testPrivateKey.pem", privateKeyPemText.getBytes(StandardCharsets.UTF_8));*/
/*//根据路径读取公钥私钥
PublicKey loadPublicKey = getPublicKeyByPath("E:/CertTest/testPublicKey.pem");
System.out.println("生成的公钥:" + loadPublicKey);
String laodPkBase64 = Base64.getEncoder().encodeToString(loadPublicKey.getEncoded());
System.out.println("Base64公钥:" + laodPkBase64);
PrivateKey loadPv = getPrivateKeyByPath("E:/CertTest/testPrivateKey.pem");
System.out.println("生成的私钥:" + loadPv);
String loadPvBase64 = Base64.getEncoder().encodeToString(loadPv.getEncoded());
System.out.println("Base64私钥:" + loadPvBase64);*/
/*//读取证书中的公钥
PublicKey loadCertpublicKey = readX509CertificatePublicKey("E:/CertTest/pra-rest-if/Local-BM-Service.crt");
System.out.println("读取的公钥:" + loadCertpublicKey);
String laodPkBase64 = Base64.getEncoder().encodeToString(loadCertpublicKey.getEncoded());
System.out.println("读取的Base64公钥:" + laodPkBase64);*/
/*//验证证书
boolean vcBl = verifyCertificate("E:/CertTest/pra-rest-if/Local-BM-Service.crt");
if (vcBl){
System.out.println(1111111);
}*/
//生成csr(证书请求文件)
/* KeyPair keypair = getKeyPair();
PublicKey userApublicKey = keypair.getPublic();
PrivateKey userAprivateKey = keypair.getPrivate();
byte[] pkcs10Byte = createScrToByte(userApublicKey, userAprivateKey);
System.out.println("生成CSR:\n"+pkcs10);
writeFile("E:/CertTest/testPkcs10Csr.csr",pkcs10Byte);*/
/*//读取csr(证书请求文件)
PKCS10CertificationRequest pkcs10 = readCsr(new FileInputStream("E:/CertTest/pra-rest-if/Local-BM-Service.csr"));
System.out.println("读取CSR:\n"+pkcs10);
System.out.println("Base64读取CSR:\n"+Base64.getEncoder().encodeToString(pkcs10.getEncoded()));
System.out.println("读取CSR基础信息:\n"+pkcs10.getSubject().toString());*/
/*//CA生成根证书
KeyPair caKeypair = getKeyPair();
PublicKey caPublicKey = caKeypair.getPublic();
PrivateKey caPrivateKey = caKeypair.getPrivate();
String caPk = Base64.getEncoder().encodeToString(caPublicKey.getEncoded());
System.out.println("生成的Base64公钥:" + caPk);
//创建根证书
createRootCertSaveToFile(caPublicKey, caPrivateKey, "E:/CertTest/caRootCert.crt");*/
/*
//读取根证书
PublicKey loadCertpublicKey = readX509CertificatePublicKey("E:/CertTest/caRootCert.crt");
String laodPkBase64 = Base64.getEncoder().encodeToString(loadCertpublicKey.getEncoded());
System.out.println("读取的Base64公钥:" + laodPkBase64);
*/
/*
//读取根证书并测试验证方法
X509Certificate rootCert = readX509CertificateByPath("E:/CertTest/caRootCert.crt");
boolean vRcBl = verifyRootCertSign(rootCert);
if (vRcBl){
System.out.println("验证根证书签名通过");
}else {
System.out.println("根证书验证失败");
}*/
/*
//颁布证书(生成用户证书),并存储到指定位置
String issueCertFilePath = "E:/CertTest/issueTestCert.crt";
//读取CSr
PKCS10CertificationRequest pkcs10 = readCsr(new FileInputStream("E:/CertTest/testPkcs10Csr.csr"));
//生成根证书时候保存CA私钥的话,这里可以读取,忘记了,重新创建吧
KeyPair caKeypair = getKeyPair();
PublicKey caPublicKey = caKeypair.getPublic();
PrivateKey caPrivateKey = caKeypair.getPrivate();
//生成根证书
X509Certificate caRootCert = createRootCert(caPublicKey, caPrivateKey);
//颁布
createIssueCertSaveToFile(pkcs10, caRootCert, caPrivateKey, issueCertFilePath);
System.out.println("证书颁布完成!");
//读取
X509Certificate issC = readX509CertificateByPath("E:/CertTest/issueTestCert.crt");
//打印
showX509Certificate(issC);
//验证用户证书签名
if(verifyIssueCertSign(issC,caPublicKey)){
System.out.println("用户证书签名验证通过");
}else{
System.out.println("用户证书签名验证失败");
}*/
//KeyStore相关测试
//testKeyStore();
}catch (Exception e){
e.printStackTrace();
System.out.println("出现异常");
}
}
/**
* KeyStore相关测试
* */
public static void testKeyStore(){
try {
/**
* KeyStore 相关测试
* KeyStroe存储工具。上面相关可以存储到到文件,存储到库(秘钥对字节流,证书字节流),也可以存储到KeyStroe
* 到文件见上面,入库存储不示例。这里示例KeyStore相关
* */
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测试中出现问题");
}
}
}
本文详细介绍了如何在Java中使用X.509证书进行公钥加密、私钥解密、签名验证等操作,以及如何生成、读取和验证证书。此外,还涵盖了SM2椭圆曲线加密算法的实现,包括公钥加密和私钥解密。文中提供了完整的工具类代码,展示了如何生成密钥对、CSR文件、根证书和用户证书,以及KeyStore的管理。
1378





