Bootstrap

Java环境下用SM2或者RSA格式生成P10

背景:
需求,生成P10时用的私钥不能暴露出来,为了安全起见,将需要用户自己用私钥进行签名后,将私钥签名当参数传入生成P10的接口中。
1.生成P10的工具类
①该方法是需要使用者传入用户公钥,签名算法,私钥签名,才能生成P10

   /**
     * 生成 P10
     * @param pubKey  用户公钥
     * @param signAlg   签名算法
     * @param signer   私钥签名 对象
     * @return  p10
     */
    public static String makeCertificate(String pubKey, String signAlg,ContentSigner signer) {
        try {
            byte[] publicKey = Base64.decodeBase64(pubKey);
          //  byte[] privateKey = Base64.decodeBase64(signedPriKey);
            PublicKey pubkey = null;
          //  PrivateKey prikey = null;
            if(signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SM3WITHSM2)){
                pubkey = BCECUtil.convertX509ToECPublicKey(publicKey);
              //  prikey = BCECUtil.convertPKCS8ToECPrivateKey(privateKey);
            }
            if(signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA1WITHRSA) || signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA256WITHRSA) || signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA384WITHRSA)|| signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA512WITHRSA)){
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
                //转公钥
                pubkey = keyFactory.generatePublic(x509EncodedKeySpec);
                //转私钥
               // PKCS8EncodedKeySpec x509EncodedKeySpec1 = new PKCS8EncodedKeySpec(privateKey);
               // prikey = keyFactory.generatePrivate(x509EncodedKeySpec1);
            }
            X500NameBuilder localX500NameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
            localX500NameBuilder.addRDN(BCStyle.CN, "userName or CompName"); //个人,企业用户名
            localX500NameBuilder.addRDN(BCStyle.C, "CN");  //国家
            localX500NameBuilder.addRDN(BCStyle.O, "39dian blog"); //机构名称
            localX500NameBuilder.addRDN(BCStyle.ST, "shanghai");  //省
            localX500NameBuilder.addRDN(BCStyle.L, "shanghai");  //市
            localX500NameBuilder.addRDN(BCStyle.E, "[email protected]"); //邮箱
            org.bouncycastle.asn1.x500.X500Name localX500Name = localX500NameBuilder.build();
            JcaPKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(localX500Name, pubkey);
            JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(signAlg);// 签名算法
          /*  ContentSigner signer = null;
            if(signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SM3WITHSM2)){
                signer = csBuilder.setProvider("BC").build(prikey);
            }*/
           // signer = csBuilder.build(prikey);    //  私钥签名    localKeyPair.getPrivate()
            PKCS10CertificationRequest csr = p10Builder.build(signer);// PKCS10的请求
            return Base64.encodeBase64String(csr.getEncoded());  //P10 Base64 CSR
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

②该方法需要使用者传入签名算法就可以生成P10,因为里面需要的公私钥都用随机数进行了计算生成,不需要再手动收入

/**
     * 生成证书请求和公私玥。
     * @param subject  基本信息
     * @param signAlg  签名算法
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws OperatorCreationException
     * @throws IOException
     * @throws NoSuchProviderException
     * @throws InvalidAlgorithmParameterException
     */
    public static byte[] genUserReq(CertSubject subject, String signAlg) throws NoSuchAlgorithmException, InvalidKeyException, OperatorCreationException, IOException, NoSuchProviderException, InvalidAlgorithmParameterException {
        //主题信息
        org.bouncycastle.asn1.x500.X500Name userDN = subject.buildDN();
        if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SM3WITHSM2)) {
            //公私钥对
            KeyPair userKP = SM2Util.generateKeyPair();
            SM2PublicKey userPub = new SM2PublicKey(userKP.getPublic().getAlgorithm(),
                    (BCECPublicKey) userKP.getPublic());

            //生成证书请求
            byte[] userCSR = CommonUtil.createCSR(userDN, userPub, userKP.getPrivate(),
                    signAlg).getEncoded();
            //保存
            byte[] x = ((BCECPublicKey) userKP.getPublic()).getQ().getAffineXCoord().getEncoded();
            byte[] y = ((BCECPublicKey) userKP.getPublic()).getQ().getAffineYCoord().getEncoded();
            byte[] sk = ((BCECPrivateKey) userKP.getPrivate()).getD().toByteArray();

            GenReqResult genReqResult = new GenReqResult(userCSR, x, y, sk);
            return userCSR;
        } else if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA256WITHRSA)) {
            CertAndKeyGen gen = new CertAndKeyGen("RSA", SignAlg.SIGN_ALGO_SHA256WITHRSA);

            gen.generate(1024);//生成1024位密钥

            //生成证书请求
            byte[] userCSR = CommonUtil.createCSR(userDN, gen.getPublicKey(), gen.getPrivateKey(),
                    signAlg).getEncoded();
            return userCSR;
        } else if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA384WITHRSA)) {
            CertAndKeyGen gen = new CertAndKeyGen("RSA", SignAlg.SIGN_ALGO_SHA384WITHRSA);

            gen.generate(1024);//生成1024位密钥

            //生成证书请求
            byte[] userCSR = CommonUtil.createCSR(userDN, gen.getPublicKey(), gen.getPrivateKey(),
                    signAlg).getEncoded();
            return userCSR;
        } else if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA512WITHRSA)) {
            CertAndKeyGen gen = new CertAndKeyGen("RSA", SignAlg.SIGN_ALGO_SHA512WITHRSA);
            gen.generate(1024);//生成1024位密钥
            //生成证书请求
            byte[] userCSR = CommonUtil.createCSR(userDN, gen.getPublicKey(), gen.getPrivateKey(),
                    signAlg).getEncoded();
            return userCSR;
        } else if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA1WITHRSA)) {
            CertAndKeyGen gen = new CertAndKeyGen("RSA", SignAlg.SIGN_ALGO_SHA1WITHRSA);

            gen.generate(1024);//生成1024位密钥

            //生成证书请求
            byte[] userCSR = CommonUtil.createCSR(userDN, gen.getPublicKey(), gen.getPrivateKey(),
                    signAlg).getEncoded();
            return userCSR;
        }
        return null;
    }

注:补充②中用到的CertSubject实体类

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;

public class CertSubject {

    public  String country;
    public  String province;
    public String locality;
    public String orgName;
    public String orgUnit;
    public String commonName;
    public String email;


    public X500Name buildDN( ){

        X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
        if (country !=null) {
            builder.addRDN(BCStyle.C, country);
        }
        if (province !=null) {
            builder.addRDN(BCStyle.ST, province);
        }
        if (locality !=null) {
            builder.addRDN(BCStyle.L, locality);
        }
        if (orgName !=null) {
            builder.addRDN(BCStyle.O, orgName);
        }
        if (orgUnit !=null) {
            builder.addRDN(BCStyle.OU, orgUnit);
        }
        if (commonName !=null) {
            builder.addRDN(BCStyle.CN, commonName);
        }
        if (email !=null) {
            builder.addRDN(BCStyle.E, email);
        }

        return builder.build();

    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getLocality() {
        return locality;
    }

    public void setLocality(String locality) {
        this.locality = locality;
    }

    public String getOrgName() {
        return orgName;
    }

    public void setOrgName(String orgName) {
        this.orgName = orgName;
    }

    public String getCommonName() {
        return commonName;
    }

    public void setCommonName(String commonName) {
        this.commonName = commonName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getOrgUnit() {
        return orgUnit;
    }

    public void setOrgUnit(String orgUnit) {
        this.orgUnit = orgUnit;
    }


    @Override
    public String toString() {
        return "CertSubject{" +
                "country='" + country + '\'' +
                ", province='" + province + '\'' +
                ", locality='" + locality + '\'' +
                ", orgName='" + orgName + '\'' +
                ", orgUnit='" + orgUnit + '\'' +
                ", commonName='" + commonName + '\'' +
                ", email='" + email + '\'' +
                '}';
    }

2.上面工具类已成,下面开始调用,以第一种为例
直接上干货:

public void testP10(){  //生成P10
        String signAlg = "SHA256WithRSA";
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        KeyPairGenerator localKeyPairGenerator = null;
        String pubKey  = null;
        ContentSigner signer = null;
        PrivateKey prikey1 = null;
        KeyPair localKeyPair = null;
        try {
            if(signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA1WITHRSA) || signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA256WITHRSA) || signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA384WITHRSA)|| signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SHA512WITHRSA)){

                localKeyPairGenerator = KeyPairGenerator.getInstance("RSA",new BouncyCastleProvider());
                localKeyPairGenerator.initialize(1024);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                localKeyPair = localKeyPairGenerator.genKeyPair();
                // X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(localKeyPair.getPublic().getEncoded());
                //转公钥
                // pubkey1 = keyFactory.generatePublic(x509EncodedKeySpec);
                //转私钥
                PKCS8EncodedKeySpec x509EncodedKeySpec1 = new PKCS8EncodedKeySpec(localKeyPair.getPrivate().getEncoded());
                // prikey1 = keyFactory.generatePrivate(x509EncodedKeySpec1);
            } else if (signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SM3WITHSM2)) {
                localKeyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
                localKeyPairGenerator.initialize(new ECGenParameterSpec("sm2p256v1"));
                localKeyPair = localKeyPairGenerator.genKeyPair();
                //   pubkey1 = BCECUtil.convertX509ToECPublicKey(localKeyPair.getPublic().getEncoded());
                //  prikey1 = BCECUtil.convertPKCS8ToECPrivateKey(localKeyPair.getPrivate().getEncoded());
            }
            pubKey = Base64.encodeBase64String(localKeyPair.getPublic().getEncoded()); //转Base64
            //为了私钥的安全性,私钥签名过程需要用户自己来完成
            JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(signAlg);// 签名算法
            if(signAlg.equalsIgnoreCase(SignAlg.SIGN_ALGO_SM3WITHSM2)){
                signer = csBuilder.setProvider("BC").build(localKeyPair.getPrivate());
            }
            signer = csBuilder.build(localKeyPair.getPrivate());    //  私钥签名    localKeyPair.getPrivate()
        } catch (Exception ex) {
            ex.printStackTrace();
            Assert.fail();
        }
        String P10 = makeCertificate(pubKey,signAlg,signer);
        Log4jBean.logger.info("生成的P10为:"+P10);

    }

结果图示:
在这里插入图片描述
补上:BCECUtil .java

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 这个工具类的方法,也适用于其他基于BC库的ECC算法
 */
public class BCECUtil {
    private static final String ALGO_NAME_EC = "EC";
    private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
    private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";

    /**
     * 生成ECC密钥对
     *
     * @return ECC密钥对
     */
    public static AsymmetricCipherKeyPair generateKeyPairParameter(
            ECDomainParameters domainParameters, SecureRandom random) {
        ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters,
                random);
        ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
        keyGen.init(keyGenerationParams);
        return keyGen.generateKeyPair();
    }

    public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random)
            throws NoSuchProviderException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(),
                domainParameters.getN(), domainParameters.getH());
        kpg.initialize(parameterSpec, random);
        return kpg.generateKeyPair();
    }

    public static int getCurveLength(ECKeyParameters ecKey) {
        return getCurveLength(ecKey.getParameters());
    }

    public static int getCurveLength(ECDomainParameters domainParams) {
        return (domainParams.getCurve().getFieldSize() + 7) / 8;
    }

    public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) {
        if (src.length == curveLength) {
            return src;
        }

        byte[] result = new byte[curveLength];
        if (src.length > curveLength) {
            System.arraycopy(src, src.length - result.length, result, 0, result.length);
        } else {
            System.arraycopy(src, 0, result, result.length - src.length, src.length);
        }
        return result;
    }

    /**
     * @param dHex             十六进制字符串形式的私钥d值,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            String dHex, ECDomainParameters domainParameters) {
        return createECPrivateKeyParameters(ByteUtils.fromHexString(dHex), domainParameters);
    }

    /**
     * @param dBytes           字节数组形式的私钥d值,如果是SM2算法,应该是32字节
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            byte[] dBytes, ECDomainParameters domainParameters) {
        return createECPrivateKeyParameters(new BigInteger(1, dBytes), domainParameters);
    }

    /**
     * @param d                大数形式的私钥d值
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            BigInteger d, ECDomainParameters domainParameters) {
        return new ECPrivateKeyParameters(d, domainParameters);
    }

    /**
     * 根据EC私钥构造EC公钥
     *
     * @param priKey ECC私钥参数对象
     * @return
     */
    public static ECPublicKeyParameters buildECPublicKeyByPrivateKey(ECPrivateKeyParameters priKey) {
        ECDomainParameters domainParameters = priKey.getParameters();
        ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), priKey.getD());
        return new ECPublicKeyParameters(q, domainParameters);
    }

    /**
     * @param x                大数形式的公钥x分量
     * @param y                大数形式的公钥y分量
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            BigInteger x, BigInteger y, ECCurve curve, ECDomainParameters domainParameters) {
        return createECPublicKeyParameters(x.toByteArray(), y.toByteArray(), curve, domainParameters);
    }

    /**
     * @param xHex             十六进制形式的公钥x分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param yHex             十六进制形式的公钥y分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            String xHex, String yHex, ECCurve curve, ECDomainParameters domainParameters) {
        return createECPublicKeyParameters(ByteUtils.fromHexString(xHex), ByteUtils.fromHexString(yHex),
                curve, domainParameters);
    }

    /**
     * @param xBytes           十六进制形式的公钥x分量,如果是SM2算法,应该是32字节
     * @param yBytes           十六进制形式的公钥y分量,如果是SM2算法,应该是32字节
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) {
        final byte uncompressedFlag = 0x04;
        int curveLength = getCurveLength(domainParameters);
        xBytes = fixToCurveLengthBytes(curveLength, xBytes);
        yBytes = fixToCurveLengthBytes(curveLength, yBytes);
        byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length];
        encodedPubKey[0] = uncompressedFlag;
        System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length);
        System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length);
        return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters);
    }

    public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) {
        ECParameterSpec parameterSpec = ecPriKey.getParameters();
        ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
                parameterSpec.getN(), parameterSpec.getH());
        return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
    }

    public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) {
        ECParameterSpec parameterSpec = ecPubKey.getParameters();
        ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
                parameterSpec.getN(), parameterSpec.getH());
        return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters);
    }

    public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(SubjectPublicKeyInfo subPubInfo)
            throws NoSuchProviderException,
            NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        return BCECUtil.convertX509ToECPublicKey(subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER));
    }

    /**
     * 将ECC私钥转换为PKCS8标准的字节流
     *
     * @param priKey
     * @param pubKey 可以为空,但是如果为空的话得到的结果OpenSSL可能解析不了
     * @return
     */
    public static byte[] convertECPrivateKeyToPKCS8(
            ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) {
        ECDomainParameters domainParams = priKey.getParameters();
        ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
                domainParams.getN(), domainParams.getH());
        BCECPublicKey publicKey = null;
        if (pubKey != null) {
            publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
                    BouncyCastleProvider.CONFIGURATION);
        }
        BCECPrivateKey privateKey = new BCECPrivateKey(ALGO_NAME_EC, priKey, publicKey,
                spec, BouncyCastleProvider.CONFIGURATION);
        return privateKey.getEncoded();
    }

    /**
     * 将PKCS8标准的私钥字节流转换为私钥对象
     *
     * @param pkcs8Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     */
    public static BCECPrivateKey convertPKCS8ToECPrivateKey(byte[] pkcs8Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key);
        KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPrivateKey) kf.generatePrivate(peks);
    }

    /**
     * 将PKCS8标准的私钥字节流转换为PEM
     *
     * @param encodedKey
     * @return
     * @throws IOException
     */
    public static String convertECPrivateKeyPKCS8ToPEM(byte[] encodedKey) throws IOException {
        return convertEncodedDataToPEM(PEM_STRING_ECPRIVATEKEY, encodedKey);
    }

    /**
     * 将PEM格式的私钥转换为PKCS8标准字节流
     *
     * @param pemString
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeyPEMToPKCS8(String pemString) throws IOException {
        return convertPEMToEncodedData(pemString);
    }

    /**
     * 将ECC私钥转换为SEC1标准的字节流
     * openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的,
     * 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥.
     * 相对new NetworkConnPub().getProtocolHostPort()私钥的PKCS1标准,ECC私钥的标准为SEC1
     *
     * @param priKey
     * @param pubKey
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeyToSEC1(
            ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) throws IOException {
        byte[] pkcs8Bytes = convertECPrivateKeyToPKCS8(priKey, pubKey);
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes);
        ASN1Encodable encodable = pki.parsePrivateKey();
        ASN1Primitive primitive = encodable.toASN1Primitive();
        byte[] sec1Bytes = primitive.getEncoded();
        return sec1Bytes;
    }

    /**
     * 将SEC1标准的私钥字节流恢复为PKCS8标准的字节流
     *
     * @param sec1Key
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeySEC1ToPKCS8(byte[] sec1Key) throws IOException {
        /**
         * 参考org.bouncycastle.asn1.pkcs.PrivateKeyInfo和
         * org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey,逆向拼装
         */
        X962Parameters params = getDomainParametersFromName(SM2Util.JDK_EC_SPEC, false);
        ASN1OctetString privKey = new DEROctetString(sec1Key);
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(0)); //版本号
        v.add(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params)); //算法标识
        v.add(privKey);
        DERSequence ds = new DERSequence(v);
        return ds.getEncoded(ASN1Encoding.DER);
    }

    /**
     * 将SEC1标准的私钥字节流转为BCECPrivateKey对象
     *
     * @param sec1Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     * @throws IOException
     */
    public static BCECPrivateKey convertSEC1ToBCECPrivateKey(byte[] sec1Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(convertECPrivateKeySEC1ToPKCS8(sec1Key));
        KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPrivateKey) kf.generatePrivate(peks);
    }

    /**
     * 将SEC1标准的私钥字节流转为ECPrivateKeyParameters对象
     * openssl i2d_ECPrivateKey函数生成的DER编码的ecc私钥是:SEC1标准的、带有EC_GROUP、带有公钥的,
     * 这个工具函数的主要目的就是为了使Java程序能够“识别”openssl生成的ECC私钥
     *
     * @param sec1Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     */
    public static ECPrivateKeyParameters convertSEC1ToECPrivateKey(byte[] sec1Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        BCECPrivateKey privateKey = convertSEC1ToBCECPrivateKey(sec1Key);
        return convertPrivateKeyToParameters(privateKey);
    }

    /**
     * 将ECC公钥对象转换为X509标准的字节流
     *
     * @param pubKey
     * @return
     */
    public static byte[] convertECPublicKeyToX509(ECPublicKeyParameters pubKey) {
        ECDomainParameters domainParams = pubKey.getParameters();
        ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
                domainParams.getN(), domainParams.getH());
        BCECPublicKey publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
                BouncyCastleProvider.CONFIGURATION);
        return publicKey.getEncoded();
    }

    /**
     * 将X509标准的公钥字节流转为公钥对象
     *
     * @param x509Bytes
     * @return
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static BCECPublicKey convertX509ToECPublicKey(byte[] x509Bytes) throws NoSuchProviderException,
            NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes);
        KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPublicKey) kf.generatePublic(eks);
    }

    /**
     * 将X509标准的公钥字节流转为PEM
     *
     * @param encodedKey
     * @return
     * @throws IOException
     */
    public static String convertECPublicKeyX509ToPEM(byte[] encodedKey) throws IOException {
        return convertEncodedDataToPEM(PEM_STRING_PUBLIC, encodedKey);
    }

    /**
     * 将PEM格式的公钥转为X509标准的字节流
     *
     * @param pemString
     * @return
     * @throws IOException
     */
    public static byte[] convertECPublicKeyPEMToX509(String pemString) throws IOException {
        return convertPEMToEncodedData(pemString);
    }

    /**
     * copy from BC
     *
     * @param genSpec
     * @return
     */
    public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) {
        return getDomainParametersFromName(genSpec.getName());
    }

    /**
     * copy from BC
     *
     * @param curveName
     * @return
     */
    public static X9ECParameters getDomainParametersFromName(String curveName) {
        X9ECParameters domainParameters;
        try {
            if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') {
                ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
                domainParameters = ECUtil.getNamedCurveByOid(oidID);
            } else {
                if (curveName.indexOf(' ') > 0) {
                    curveName = curveName.substring(curveName.indexOf(' ') + 1);
                    domainParameters = ECUtil.getNamedCurveByName(curveName);
                } else {
                    domainParameters = ECUtil.getNamedCurveByName(curveName);
                }
            }
        } catch (IllegalArgumentException ex) {
            domainParameters = ECUtil.getNamedCurveByName(curveName);
        }
        return domainParameters;
    }

    /**
     * copy from BC
     *
     * @param ecSpec
     * @param withCompression
     * @return
     */
    public static X962Parameters getDomainParametersFromName(
            java.security.spec.ECParameterSpec ecSpec, boolean withCompression) {
        X962Parameters params;

        if (ecSpec instanceof ECNamedCurveSpec) {
            ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName());
            if (curveOid == null) {
                curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName());
            }
            params = new X962Parameters(curveOid);
        } else if (ecSpec == null) {
            params = new X962Parameters(DERNull.INSTANCE);
        } else {
            ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());

            X9ECParameters ecP = new X9ECParameters(
                    curve,
                    new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression),
                    ecSpec.getOrder(),
                    BigInteger.valueOf(ecSpec.getCofactor()),
                    ecSpec.getCurve().getSeed());

             如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码,因为高版本的EC5Util.convertPoint没有向下兼容
            /*
            X9ECParameters ecP = new X9ECParameters(
                curve,
                EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
                ecSpec.getOrder(),
                BigInteger.valueOf(ecSpec.getCofactor()),
                ecSpec.getCurve().getSeed());
            */

            params = new X962Parameters(ecP);
        }

        return params;
    }

    private static String convertEncodedDataToPEM(String type, byte[] encodedData) throws IOException {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
        try {
            PemObject pemObj = new PemObject(type, encodedData);
            pWrt.writeObject(pemObj);
        } finally {
            pWrt.close();
        }
        return new String(bOut.toByteArray());
    }

    private static byte[] convertPEMToEncodedData(String pemString) throws IOException {
        ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes());
        PemReader pRdr = new PemReader(new InputStreamReader(bIn));
        try {
            PemObject pemObject = pRdr.readPemObject();
            return pemObject.getContent();
        } finally {
            pRdr.close();
        }
    }
}
;