Java OpenSsl 证书

        整理了X.509格式证书相关的工具类,便于自己学习和使用。

        使用的JDK为11.0.2。

        加密算法采用SM2(椭圆曲线公钥密码算法,也叫国密算法),加密算法可以根据不同的JDK进行调整。加密类Cipher可以参考:Class Cipher

        具体代码,参考最下面,包含:秘钥生成相关方法(KeyPaif),公钥加密(SM2),私钥解密,私钥对信息签名,公钥验证签名,X.509证书的读取、生成、查看、验证,DN信息,CSR文件的生成、读取,KeyStore相关操作等。

        san(subjectAlternativeName)这些证书扩展自行搜索。


名词理解:

  1. TLS:传输层安全协议。
  2. SSL:安全套接字层。
  3. KEY:通常指私钥。
  4. CSR:即证书签名请求(发送方)。
  5. CRT:即CA生成的证书。
  6. X.509:是一种证书格式。对X.509证书来说,认证者总是CA或由CA指定的人。X.509的证书文件,一般以.crt结尾,根据该文件的内容编码格式,可以分为以下二种格式: 
        PEM - 打开看文本格式,以"-----BEGIN..."开头, "-----END..."结尾,内容是BASE64编码。 
        DER - 打开看是二进制格式,不可读。
  7. 私钥和公钥:每一方都可以拥有自己的公钥私钥,公钥用于公开,私钥用于加密。(公钥私钥可以说没多大区别,只是根据使用功能进行区分) 私钥通常用来生成签名,公钥用来验证签名。
  8. 数字签名:摘要经过私钥加密后,便有了一个新的名字--数字签名。
    签名 是在发送方,这是一个加密(私钥加密)的过程。
    验签 是在接收方,这是一个解密(公钥解密)的过程。
  9. 数字证书: 就是一个.crt文件。由CA颁发。发送者私钥、信息、域名等 以csr文件发送给CA。CA生成私钥公钥。使用私钥加密,生成签名。返回证书。

流程理解:

证书体系中存在三种角色:
            A:发送方
            B:接受方
            CA:第三方认证机构或下属

 流程:

  1.  A生成公钥私钥,A信息 + 域名等 + PublicKeyA(A公钥),整合一起生成.csr。将这个.csr文件发给 CA。
  2.  CA收到.csr信息后,会验证申请信息,然后使用算法对.csr里的明文信息先做一个HASH,得到一个信息摘要,再用CA自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的数字签名。 签名 + .csr明文信息 = 证书。
  3.  签名对象包括需要在证书中说明的内容,比如A的公钥、时间戳、序列号等。
    假设证书中只有:A的公钥PublicKeyA、时间戳TIME1、序列号IDA。
    那么CA发送给A的简单证书凭证可表达 为:CertA = Eca[TIME1, IDA, PublicKeyA];
    A得到自己的证书CertA和 CA的 PublicKeyCA
  4. B同样把自己的公钥PublicKeyB送到CA;
    B得到CA发布的证书CertB和CA的PublicKeyCA(步骤3);
  5. A告知B证书CertA; CertA=Eca[TIME1,IDA,PublicKeyA];
  6. B告知A证书CertB。 CertA=Eca[TIME1,IDA,PublicKeyB];
  7. A、B各自得到对方证书后,利用从CA得到的公钥(在CA的自签证书中)验证彼此对方的证书是否有效,如果有效,那么就得到了彼此的公钥。
    利用对方的公钥,可以加密数据,也可以用来验证对方的数字签名。
  8. 单向验证情况,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测试中出现问题");
        }
    }
}

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十年梦归尘

愿意支持一下不(*^▽^*)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值