|
@@ -0,0 +1,882 @@
|
|
|
+package com.csitc.framework.common.util.crypt;
|
|
|
+import java.io.*;
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.security.*;
|
|
|
+import java.security.spec.*;
|
|
|
+import java.util.*;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.bouncycastle.asn1.*;
|
|
|
+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.CipherParameters;
|
|
|
+import org.bouncycastle.crypto.CryptoException;
|
|
|
+import org.bouncycastle.crypto.InvalidCipherTextException;
|
|
|
+import org.bouncycastle.crypto.engines.SM2Engine;
|
|
|
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
|
|
|
+import org.bouncycastle.crypto.params.*;
|
|
|
+import org.bouncycastle.crypto.signers.SM2Signer;
|
|
|
+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.jcajce.spec.SM2ParameterSpec;
|
|
|
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
|
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
|
|
+import org.bouncycastle.math.ec.ECCurve;
|
|
|
+import org.bouncycastle.math.ec.ECPoint;
|
|
|
+import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
|
|
+import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
|
|
|
+import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
|
|
+
|
|
|
+import org.bouncycastle.jce.spec.ECParameterSpec;
|
|
|
+import org.bouncycastle.util.Strings;
|
|
|
+import org.bouncycastle.util.io.pem.PemObject;
|
|
|
+import org.bouncycastle.util.io.pem.PemReader;
|
|
|
+import org.bouncycastle.util.io.pem.PemWriter;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+
|
|
|
+//SM2 基础库
|
|
|
+public class Sm2Lib
|
|
|
+{
|
|
|
+ //SM2算法默认用户ID,目前开放平台不会使用非默认用户ID
|
|
|
+ public static String DEFAULT_USER_ID = "1234567812345678";
|
|
|
+ public static String DEFAULT_CHARSET = "utf8";
|
|
|
+
|
|
|
+ public static final String SM2_ECC_GY_VAL = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
|
|
|
+ public static final String SM2_ECC_GX_VAL = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
|
|
|
+ public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
|
|
|
+ public final static BigInteger SM2_ECC_N = CURVE.getOrder();
|
|
|
+ public final static BigInteger SM2_ECC_H = CURVE.getCofactor();
|
|
|
+ public final static BigInteger SM2_ECC_GX = new BigInteger(SM2_ECC_GX_VAL, 16);
|
|
|
+ public final static BigInteger SM2_ECC_GY = new BigInteger(SM2_ECC_GY_VAL, 16);
|
|
|
+ public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
|
|
|
+ public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, SM2_ECC_N, SM2_ECC_H);
|
|
|
+
|
|
|
+ public final static BigInteger SM2_ECC_P = CURVE.getQ();
|
|
|
+ public final static BigInteger SM2_ECC_A = CURVE.getA().toBigInteger();
|
|
|
+ public final static BigInteger SM2_ECC_B = CURVE.getB().toBigInteger();
|
|
|
+
|
|
|
+ //public static final int CURVE_LEN = BCECUtils.getCurveLength(DOMAIN_PARAMS);
|
|
|
+
|
|
|
+ public static final EllipticCurve JDK_CURVE = new EllipticCurve(new ECFieldFp(SM2_ECC_P), SM2_ECC_A, SM2_ECC_B);
|
|
|
+ public static final java.security.spec.ECPoint JDK_G_POINT = new java.security.spec.ECPoint(
|
|
|
+ G_POINT.getAffineXCoord().toBigInteger(), G_POINT.getAffineYCoord().toBigInteger());
|
|
|
+ public static final java.security.spec.ECParameterSpec JDK_EC_SPEC = new java.security.spec.ECParameterSpec(
|
|
|
+ JDK_CURVE, JDK_G_POINT, SM2_ECC_N, SM2_ECC_H.intValue());
|
|
|
+
|
|
|
+ private static BouncyCastleProvider provider;
|
|
|
+
|
|
|
+ static {
|
|
|
+ provider = new BouncyCastleProvider();
|
|
|
+ Security.addProvider(provider);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String decrypt(String cipherTextBase64, String privateKey) throws Exception {
|
|
|
+ return decrypt(cipherTextBase64, privateKey, DEFAULT_CHARSET);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String decrypt(String cipherTextBase64, String privateKey, String charset) throws Exception {
|
|
|
+ byte[] cipher = Base64.getDecoder().decode(cipherTextBase64);
|
|
|
+ boolean isMatch = Pattern.matches("^[0-9a-f]+$", privateKey.toLowerCase());
|
|
|
+ if (isMatch) {
|
|
|
+ ECPrivateKeyParameters ecPrivateKeyParameters = BCECUtils.createECPrivateKeyParameters(privateKey, DOMAIN_PARAMS);
|
|
|
+ byte[] buf = decrypt(ecPrivateKeyParameters, cipher);
|
|
|
+ //将解密后的明文按指定字符集编码后返回
|
|
|
+ try {
|
|
|
+ String strContent = new String(buf, charset);
|
|
|
+ return strContent;
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ byte[] privateKeyByte = Base64.getDecoder().decode(privateKey);
|
|
|
+ // 解析X509格式SM2私钥
|
|
|
+ PrivateKey sm2PrivateKey = parsePKCS8PrivateKey(privateKeyByte);
|
|
|
+ // 使用SM2私钥解密
|
|
|
+ byte[] buf = sm2Decrypt(cipher, sm2PrivateKey);
|
|
|
+ //将解密后的明文按指定字符集编码后返回
|
|
|
+ try {
|
|
|
+ String strContent = new String(buf, charset);
|
|
|
+ return strContent;
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String encrypt(String plainText, String publicKey) throws Exception {
|
|
|
+ return encrypt(plainText, publicKey, DEFAULT_CHARSET);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String encrypt(String plainText, String publicKey, String charset) throws Exception {
|
|
|
+ byte[] plain = plainText.getBytes(charset);
|
|
|
+ boolean isMatch = Pattern.matches("^[0-9a-f]+$", publicKey.toLowerCase());
|
|
|
+ if (isMatch) {
|
|
|
+ ECPoint point = CURVE.decodePoint(ByteUtils.fromHexString(publicKey));
|
|
|
+ ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(point, DOMAIN_PARAMS);
|
|
|
+ byte[] cipher = encrypt(ecPublicKeyParameters, plain);
|
|
|
+ //将密文Base64编码后返回
|
|
|
+ String strContent = Base64.getEncoder().encodeToString(cipher);
|
|
|
+ return strContent;
|
|
|
+ } else {
|
|
|
+ // 解析PKCS8格式SM2私钥
|
|
|
+ byte[] publicKeyByte = Base64.getDecoder().decode(publicKey);
|
|
|
+ PublicKey sm2PublicKey = parseX509PublicKey(publicKeyByte);
|
|
|
+ byte[] cipher = sm2Encrypt(plain, sm2PublicKey);
|
|
|
+ //将密文Base64编码后返回
|
|
|
+ String strContent = Base64.getEncoder().encodeToString(cipher);
|
|
|
+ return strContent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String sign(String content, String privateKey) throws Exception {
|
|
|
+ return sign(content, privateKey, null, DEFAULT_CHARSET);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String sign(String content, String privateKey, String userId, String charset) throws Exception {
|
|
|
+ if (StringUtils.isEmpty(userId)) {
|
|
|
+ userId = DEFAULT_USER_ID;
|
|
|
+ }
|
|
|
+ byte[] message = content.getBytes(charset);
|
|
|
+ boolean isMatch = Pattern.matches("^[0-9a-f]+$", privateKey.toLowerCase());
|
|
|
+ if (isMatch) {
|
|
|
+ ECPrivateKeyParameters ecPrivateKeyParameters = BCECUtils.createECPrivateKeyParameters(privateKey, DOMAIN_PARAMS);
|
|
|
+ byte[] signature = sign(ecPrivateKeyParameters, Strings.toByteArray(userId), message);
|
|
|
+ try {
|
|
|
+ String sign = Base64.getEncoder().encodeToString(signature);
|
|
|
+ return sign;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ byte[] privateKeyByte = Base64.getDecoder().decode(privateKey);
|
|
|
+ PrivateKey sm2PrivateKey = parsePKCS8PrivateKey(privateKeyByte);
|
|
|
+ byte[] signature = sm2Sign(message, sm2PrivateKey, userId);
|
|
|
+ String sign = Base64.getEncoder().encodeToString(signature);
|
|
|
+ return sign;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean verify(String content, String publicKey, String sign) throws Exception {
|
|
|
+ return verify(content, publicKey, sign, null, DEFAULT_CHARSET);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean verify(String content, String publicKey, String sign, String userId, String charset) throws Exception {
|
|
|
+ if (StringUtils.isEmpty(userId)) {
|
|
|
+ userId = DEFAULT_USER_ID;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] message = content.getBytes(charset);
|
|
|
+ byte[] signature = Base64.getDecoder().decode(sign);
|
|
|
+ boolean isMatch = Pattern.matches("^[0-9a-f]+$", publicKey.toLowerCase());
|
|
|
+ if (isMatch) {
|
|
|
+ ECPoint point = CURVE.decodePoint(ByteUtils.fromHexString(publicKey));
|
|
|
+ ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(point, DOMAIN_PARAMS);
|
|
|
+ boolean valid = verify(ecPublicKeyParameters, Strings.toByteArray(userId), message, signature);
|
|
|
+ return valid;
|
|
|
+ } else {
|
|
|
+ byte[] publicKeyByte = Base64.getDecoder().decode(publicKey);
|
|
|
+ PublicKey sm2PublicKey = parseX509PublicKey(publicKeyByte);
|
|
|
+ boolean valid = sm2Verify(signature, message, sm2PublicKey, userId);
|
|
|
+ return valid;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] sm2Encrypt(byte[] plain, PublicKey sm2PublicKey) throws Exception {
|
|
|
+ try {
|
|
|
+ Cipher sm2CipherEngine = Cipher.getInstance("SM2", "BC");
|
|
|
+ sm2CipherEngine.init(Cipher.ENCRYPT_MODE, sm2PublicKey);
|
|
|
+ return sm2CipherEngine.doFinal(plain);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] sm2Decrypt(byte[] cipher, PrivateKey sm2PrivateKey) throws Exception {
|
|
|
+ try {
|
|
|
+ Cipher sm2CipherEngine = Cipher.getInstance("SM2", "BC");
|
|
|
+ sm2CipherEngine.init(Cipher.DECRYPT_MODE, sm2PrivateKey);
|
|
|
+ return sm2CipherEngine.doFinal(cipher);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] sm2Sign(byte[] message, PrivateKey sm2PrivateKey, String sm2UserId) throws Exception {
|
|
|
+ try {
|
|
|
+ Signature sm2SignEngine = Signature.getInstance("SM3withSM2");
|
|
|
+ sm2SignEngine.setParameter(new SM2ParameterSpec(
|
|
|
+ Strings.toByteArray(sm2UserId)));
|
|
|
+ sm2SignEngine.initSign(sm2PrivateKey);
|
|
|
+ sm2SignEngine.update(message);
|
|
|
+ return sm2SignEngine.sign();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean sm2Verify(byte[] signature, byte[] message, PublicKey publicKey, String sm2UserId) {
|
|
|
+ try {
|
|
|
+ Signature sm2SignEngine = Signature.getInstance("SM3withSM2");
|
|
|
+ sm2SignEngine.setParameter(new SM2ParameterSpec(Strings.toByteArray(sm2UserId)));
|
|
|
+ sm2SignEngine.initVerify(publicKey);
|
|
|
+ sm2SignEngine.update(message);
|
|
|
+ return sm2SignEngine.verify(signature);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static PublicKey parseX509PublicKey(byte[] x509PublicKey) throws Exception {
|
|
|
+ try {
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
|
|
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509PublicKey);
|
|
|
+ return keyFactory.generatePublic(keySpec);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static PrivateKey parsePKCS8PrivateKey(byte[] pkcs8PriateKey) throws Exception {
|
|
|
+ try {
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
|
|
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8PriateKey);
|
|
|
+ return keyFactory.generatePrivate(keySpec);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new Exception(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String getSignContent(Map<String, String> sortedParams) {
|
|
|
+ StringBuffer content = new StringBuffer();
|
|
|
+ List<String> keys = new ArrayList<>(sortedParams.keySet());
|
|
|
+ Collections.sort(keys);
|
|
|
+ int index = 0;
|
|
|
+ for (int i = 0; i < keys.size(); i++) {
|
|
|
+ String key = keys.get(i);
|
|
|
+ String value = sortedParams.get(key);
|
|
|
+ if (areNotEmpty(key, value)) {
|
|
|
+ content.append((index == 0 ? "" : "&") + key + "=" + value);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return content.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean areNotEmpty(String... values) {
|
|
|
+ boolean result = true;
|
|
|
+ if (values == null || values.length == 0) {
|
|
|
+ result = false;
|
|
|
+ } else {
|
|
|
+ for (String value : values) {
|
|
|
+ result &= !isEmpty(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isEmpty(String value) {
|
|
|
+ int strLen;
|
|
|
+ if (value == null || (strLen = value.length()) == 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < strLen; i++) {
|
|
|
+ if ((Character.isWhitespace(value.charAt(i)) == false)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param pubKeyParams 公钥
|
|
|
+ * @param srcData 原文
|
|
|
+ * @return 默认输出C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
|
|
+ * @throws InvalidCipherTextException
|
|
|
+ */
|
|
|
+ private static byte[] encrypt(final ECPublicKeyParameters pubKeyParams, final byte[] srcData) throws InvalidCipherTextException {
|
|
|
+ return encrypt(SM2Engine.Mode.C1C3C2, pubKeyParams, srcData);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param priKeyParams 私钥
|
|
|
+ * @param sm2Cipher 默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
|
|
+ * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
|
|
+ * @throws InvalidCipherTextException
|
|
|
+ */
|
|
|
+ private static byte[] decrypt(final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher)
|
|
|
+ throws InvalidCipherTextException {
|
|
|
+ return decrypt(SM2Engine.Mode.C1C3C2, priKeyParams, sm2Cipher);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param mode 指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
|
|
+ * @param pubKeyParams 公钥
|
|
|
+ * @param srcData 原文
|
|
|
+ * @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
|
|
+ * @throws InvalidCipherTextException
|
|
|
+ */
|
|
|
+ private static byte[] encrypt(final SM2Engine.Mode mode, final ECPublicKeyParameters pubKeyParams, final byte[] srcData)
|
|
|
+ throws InvalidCipherTextException {
|
|
|
+ final SM2Engine engine = new SM2Engine(mode);
|
|
|
+ final ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParams, new SecureRandom());
|
|
|
+ engine.init(true, pwr);
|
|
|
+ return engine.processBlock(srcData, 0, srcData.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param mode 指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
|
|
|
+ * @param priKeyParams 私钥
|
|
|
+ * @param sm2Cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
|
|
|
+ * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
|
|
|
+ * @throws InvalidCipherTextException
|
|
|
+ */
|
|
|
+ private static byte[] decrypt(final SM2Engine.Mode mode, final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher)
|
|
|
+ throws InvalidCipherTextException {
|
|
|
+ final SM2Engine engine = new SM2Engine(mode);
|
|
|
+ engine.init(false, priKeyParams);
|
|
|
+ return engine.processBlock(sm2Cipher, 0, sm2Cipher.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 签名
|
|
|
+ *
|
|
|
+ * @param priKeyParams 私钥
|
|
|
+ * @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
|
|
+ * @param srcData 源数据
|
|
|
+ * @return DER编码后的签名值
|
|
|
+ * @throws CryptoException
|
|
|
+ */
|
|
|
+ private static byte[] sign(final ECPrivateKeyParameters priKeyParams, final byte[] withId, final byte[] srcData)
|
|
|
+ throws CryptoException {
|
|
|
+ SM2Signer signer = new SM2Signer();
|
|
|
+ CipherParameters param = null;
|
|
|
+ ParametersWithRandom pwr = new ParametersWithRandom(priKeyParams, new SecureRandom());
|
|
|
+ if (withId != null) {
|
|
|
+ param = new ParametersWithID(pwr, withId);
|
|
|
+ } else {
|
|
|
+ param = pwr;
|
|
|
+ }
|
|
|
+ signer.init(true, param);
|
|
|
+ signer.update(srcData, 0, srcData.length);
|
|
|
+ return signer.generateSignature();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验签
|
|
|
+ *
|
|
|
+ * @param pubKeyParams 公钥
|
|
|
+ * @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
|
|
+ * @param srcData 原文
|
|
|
+ * @param sign DER编码的签名值
|
|
|
+ * @return 验签成功返回true,失败返回false
|
|
|
+ */
|
|
|
+ private static boolean verify(final ECPublicKeyParameters pubKeyParams, final byte[] withId, final byte[] srcData,
|
|
|
+ final byte[] sign) {
|
|
|
+ final SM2Signer signer = new SM2Signer();
|
|
|
+ CipherParameters param;
|
|
|
+ if (withId != null) {
|
|
|
+ param = new ParametersWithID(pubKeyParams, withId);
|
|
|
+ } else {
|
|
|
+ param = pubKeyParams;
|
|
|
+ }
|
|
|
+ signer.init(false, param);
|
|
|
+ signer.update(srcData, 0, srcData.length);
|
|
|
+ return signer.verifySignature(sign);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成秘钥对(工具函数)
|
|
|
+ * @return 0 公钥, 1私钥
|
|
|
+ */
|
|
|
+ public static String[] genKey()
|
|
|
+ {
|
|
|
+ AsymmetricCipherKeyPair kPair = BCECUtils.generateKeyPairParameter(DOMAIN_PARAMS, new SecureRandom());
|
|
|
+ ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters)kPair.getPrivate();
|
|
|
+ ECPublicKeyParameters ecPub = (ECPublicKeyParameters)kPair.getPublic();
|
|
|
+ BigInteger privateKey = ecPriv.getD();
|
|
|
+ ECPoint publicKey = ecPub.getQ();
|
|
|
+
|
|
|
+ byte[] priv = privateKey.toByteArray();
|
|
|
+ byte[] pub = publicKey.getEncoded(false);
|
|
|
+ if (priv.length == 33)
|
|
|
+ {
|
|
|
+ byte[] newPriv = new byte[32];
|
|
|
+ System.arraycopy(priv, 1, newPriv, 0, 32);
|
|
|
+ priv = newPriv;
|
|
|
+ }
|
|
|
+ String[] keyPairs = new String[2];
|
|
|
+ keyPairs[0] = ByteUtils.toHexString(pub);
|
|
|
+ keyPairs[1] = ByteUtils.toHexString(priv);
|
|
|
+
|
|
|
+ return keyPairs;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 这个工具类的方法,也适用于其他基于BC库的ECC算法
|
|
|
+ */
|
|
|
+class BCECUtils {
|
|
|
+ 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 Sm2Lib#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 Sm2Lib#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 Sm2Lib#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 Sm2Lib#CURVE}
|
|
|
+ * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link Sm2Lib#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 Sm2Lib#CURVE}
|
|
|
+ * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link Sm2Lib#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 Sm2Lib#CURVE}
|
|
|
+ * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link Sm2Lib#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 BCECUtils.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私钥.
|
|
|
+ * 相对RSA私钥的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(Sm2Lib.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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|