mirror of
https://gitee.com/dromara/hutool.git
synced 2025-07-16 07:59:46 +08:00
add ECKeySpecUtil
This commit is contained in:
parent
7468032a8d
commit
9281b0f8e6
@ -25,7 +25,7 @@ import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.RandomUtil;
|
||||
import org.dromara.hutool.crypto.asymmetric.AsymmetricAlgorithm;
|
||||
import org.dromara.hutool.crypto.bc.ECKeyUtil;
|
||||
import org.dromara.hutool.crypto.bc.SmUtil;
|
||||
import org.dromara.hutool.crypto.bc.SM2Constant;
|
||||
import org.dromara.hutool.crypto.provider.GlobalProviderFactory;
|
||||
import org.dromara.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
|
||||
@ -421,7 +421,7 @@ public class KeyUtil {
|
||||
public static KeyPair generateKeyPair(final String algorithm, final int keySize, final byte[] seed) {
|
||||
// SM2算法需要单独定义其曲线生成
|
||||
if ("SM2".equalsIgnoreCase(algorithm)) {
|
||||
final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(SmUtil.SM2_CURVE_NAME);
|
||||
final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(SM2Constant.SM2_CURVE_NAME);
|
||||
return generateKeyPair(algorithm, keySize, seed, sm2p256v1);
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ public interface AsymmetricDecryptor {
|
||||
*
|
||||
* @param data 数据,Hex(16进制)或Base64字符串
|
||||
* @param keyType 密钥类型
|
||||
* @return 解密后的密文
|
||||
* @return 解密后的密文,UTF-8编码
|
||||
* @since 4.5.2
|
||||
*/
|
||||
default String decryptStr(final String data, final KeyType keyType) {
|
||||
|
@ -33,13 +33,17 @@ import org.bouncycastle.util.BigIntegers;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.codec.binary.HexUtil;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.crypto.CryptoException;
|
||||
import org.dromara.hutool.crypto.SecureUtil;
|
||||
import org.dromara.hutool.crypto.bc.ECKeyUtil;
|
||||
import org.dromara.hutool.crypto.bc.SmUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
@ -73,19 +77,24 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
private ECPrivateKeyParameters privateKeyParams;
|
||||
private ECPublicKeyParameters publicKeyParams;
|
||||
|
||||
/**
|
||||
* 自定义随机数
|
||||
*/
|
||||
private SecureRandom random;
|
||||
/**
|
||||
* 是否去除压缩04压缩标识
|
||||
*/
|
||||
private boolean removeCompressedFlag;
|
||||
|
||||
private DSAEncoding encoding = StandardDSAEncoding.INSTANCE;
|
||||
private Digest digest = new SM3Digest();
|
||||
private SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
|
||||
|
||||
// ------------------------------------------------------------------ Constructor start
|
||||
// region ----- Constructors
|
||||
|
||||
/**
|
||||
* 构造,生成新的私钥公钥对
|
||||
* 构造,生成新的随机私钥公钥对
|
||||
*/
|
||||
public SM2() {
|
||||
this(null, (byte[]) null);
|
||||
@ -113,29 +122,11 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
*/
|
||||
public SM2(final byte[] privateKey, final byte[] publicKey) {
|
||||
this(
|
||||
ECKeyUtil.decodePrivateKeyParams(privateKey),
|
||||
ECKeyUtil.decodePublicKeyParams(publicKey)
|
||||
ECKeyUtil.generateSm2PrivateKey(privateKey),
|
||||
ECKeyUtil.generateSm2PublicKey(publicKey)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 <br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||
*
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
public SM2(final PrivateKey privateKey, final PublicKey publicKey) {
|
||||
this(ECKeyUtil.toPrivateParams(privateKey), ECKeyUtil.toPublicParams(publicKey));
|
||||
if (null != privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
if (null != publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 <br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
@ -147,7 +138,11 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public SM2(final String privateKeyDValue, final String publicKeyPointXHex, final String publicKeyPointYHex) {
|
||||
this(ECKeyUtil.toSm2PrivateParams(privateKeyDValue), ECKeyUtil.toSm2PublicParams(publicKeyPointXHex, publicKeyPointYHex));
|
||||
this(
|
||||
SecureUtil.decode(privateKeyDValue),
|
||||
SecureUtil.decode(publicKeyPointXHex),
|
||||
SecureUtil.decode(publicKeyPointYHex)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,8 +156,23 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* @since 5.2.0
|
||||
*/
|
||||
public SM2(final byte[] privateKeyDValue, final byte[] publicKeyPointX, final byte[] publicKeyPointY) {
|
||||
this(ECKeyUtil.toSm2PrivateParams(privateKeyDValue),
|
||||
ECKeyUtil.toSm2PublicParams(publicKeyPointX, publicKeyPointY));
|
||||
this(ECKeyUtil.generateSm2PrivateKey(privateKeyDValue),
|
||||
ECKeyUtil.generateSm2PublicKey(publicKeyPointX, publicKeyPointY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 <br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||
*
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
public SM2(final PrivateKey privateKey, final PublicKey publicKey) {
|
||||
super(ALGORITHM_SM2, new KeyPair(publicKey, privateKey));
|
||||
this.privateKeyParams = ECKeyUtil.toPrivateParams(this.privateKey);
|
||||
this.publicKeyParams = ECKeyUtil.toPublicParams(this.publicKey);
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,8 +189,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
this.publicKeyParams = publicKeyParams;
|
||||
this.init();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ Constructor end
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 初始化<br>
|
||||
@ -191,6 +200,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
*/
|
||||
public SM2 init() {
|
||||
if (null == this.privateKeyParams && null == this.publicKeyParams) {
|
||||
// 随机密钥对
|
||||
super.initKeys();
|
||||
this.privateKeyParams = ECKeyUtil.toPrivateParams(this.privateKey);
|
||||
this.publicKeyParams = ECKeyUtil.toPublicParams(this.publicKey);
|
||||
@ -205,7 +215,91 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
return this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------- Encrypt
|
||||
// region ----- Encrypt
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C3 SM3的摘要值
|
||||
* C2 密文数据
|
||||
* </pre>
|
||||
*
|
||||
* @param data 被加密的字符串,UTF8编码
|
||||
* @return 加密后的Base64
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
*/
|
||||
public String encryptBase64(final String data) {
|
||||
return encryptBase64(data, KeyType.PublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C3 SM3的摘要值
|
||||
* C2 密文数据
|
||||
* </pre>
|
||||
*
|
||||
* @param in 被加密的数据流
|
||||
* @return 加密后的Base64
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public String encryptBase64(final InputStream in) throws IORuntimeException {
|
||||
return encryptBase64(in, KeyType.PublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C3 SM3的摘要值
|
||||
* C2 密文数据
|
||||
* </pre>
|
||||
*
|
||||
* @param data 被加密的bytes
|
||||
* @return 加密后的Base64
|
||||
*/
|
||||
public String encryptBase64(final byte[] data) {
|
||||
return encryptBase64(data, KeyType.PublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C3 SM3的摘要值
|
||||
* C2 密文数据
|
||||
* </pre>
|
||||
*
|
||||
* @param data 被加密的字符串,UTF8编码
|
||||
* @return 加密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
*/
|
||||
public byte[] encrypt(final String data) {
|
||||
return encrypt(data, KeyType.PublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C3 SM3的摘要值
|
||||
* C2 密文数据
|
||||
* </pre>
|
||||
*
|
||||
* @param in 被加密的数据流
|
||||
* @return 加密后的bytes
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public byte[] encrypt(final InputStream in) throws IORuntimeException {
|
||||
return encrypt(in, KeyType.PublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,SM2非对称加密的结果由C1,C3,C2三部分组成,其中:
|
||||
@ -267,15 +361,60 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
final SM2Engine engine = getEngine();
|
||||
try {
|
||||
engine.init(true, pubKeyParameters);
|
||||
return engine.processBlock(data, 0, data.length);
|
||||
final byte[] result = engine.processBlock(data, 0, data.length);
|
||||
return this.removeCompressedFlag ? removeCompressedFlag(result) : result;
|
||||
} catch (final InvalidCipherTextException e) {
|
||||
throw new CryptoException(e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// --------------------------------------------------------------------------------- Decrypt
|
||||
// region ----- Decrypt
|
||||
|
||||
/**
|
||||
* 使用私钥解密
|
||||
*
|
||||
* @param data SM2密文数据,Hex(16进制)或Base64字符串
|
||||
* @return 解密后的字符串,UTF-8 编码
|
||||
*/
|
||||
public String decryptStr(final String data) {
|
||||
return decryptStr(data, KeyType.PrivateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密
|
||||
*
|
||||
* @param data SM2密文数据,Hex(16进制)或Base64字符串
|
||||
* @param charset 编码
|
||||
* @return 解密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
*/
|
||||
public String decryptStr(final String data, final Charset charset) {
|
||||
return decryptStr(data, KeyType.PrivateKey, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密
|
||||
*
|
||||
* @param in 密文数据流
|
||||
* @return 解密后的bytes
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public byte[] decrypt(final InputStream in) throws IORuntimeException {
|
||||
return super.decrypt(in, KeyType.PrivateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密
|
||||
*
|
||||
* @param data SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
|
||||
* @return 解密后的bytes
|
||||
*/
|
||||
public byte[] decrypt(final String data) {
|
||||
return super.decrypt(data, KeyType.PrivateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密
|
||||
@ -316,17 +455,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
*/
|
||||
public byte[] decrypt(byte[] data, final CipherParameters privateKeyParameters) throws CryptoException {
|
||||
Assert.isTrue(data.length > 1, "Invalid SM2 cipher text, must be at least 1 byte long");
|
||||
// 检查数据,gmssl等库生成的密文不包含04前缀(非压缩数据标识),此处检查并补充
|
||||
// 参考:https://blog.csdn.net/softt/article/details/139978608
|
||||
// 根据公钥压缩形态不同,密文分为两种压缩形式:
|
||||
// C1( 03 + X ) + C3(32个字节)+ C2
|
||||
// C1( 02 + X ) + C3(32个字节)+ C2
|
||||
// 非压缩公钥正常形态为04 + X + Y,由于各个算法库差异,04有时候会省略
|
||||
// 非压缩密文正常形态为04 + C1 + C3 + C2
|
||||
if (data[0] != 0x04 && data[0] != 0x02 && data[0] != 0x03) {
|
||||
// 默认非压缩形态
|
||||
data = ArrayUtil.insert(data, 0, 0x04);
|
||||
}
|
||||
data = prependCompressedFlag(data);
|
||||
|
||||
lock.lock();
|
||||
final SM2Engine engine = getEngine();
|
||||
@ -339,7 +468,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------------- Sign and Verify
|
||||
//endregion
|
||||
|
||||
// region ----- Sign and Verify
|
||||
|
||||
/**
|
||||
* 用私钥对信息生成数字签名
|
||||
@ -458,6 +589,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
public SM2 setPrivateKey(final PrivateKey privateKey) {
|
||||
@ -513,6 +645,18 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否移除压缩标记,默认为false<br>
|
||||
* 移除后的密文兼容gmssl等库
|
||||
*
|
||||
* @param removeCompressedFlag 是否移除压缩标记
|
||||
* @return this
|
||||
*/
|
||||
public SM2 setRemoveCompressedFlag(final boolean removeCompressedFlag) {
|
||||
this.removeCompressedFlag = removeCompressedFlag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置DSA signatures的编码为PlainDSAEncoding
|
||||
*
|
||||
@ -651,5 +795,42 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
this.digest.reset();
|
||||
return this.signer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除04压缩标识<br>
|
||||
* gmssl等库生成的密文不包含04前缀,此处兼容
|
||||
*
|
||||
* @param data 密文数据
|
||||
* @return 处理后的数据
|
||||
*/
|
||||
private static byte[] removeCompressedFlag(final byte[] data) {
|
||||
if (data[0] != 0x04) {
|
||||
return data;
|
||||
}
|
||||
final byte[] result = new byte[data.length - 1];
|
||||
System.arraycopy(data, 1, result, 0, result.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加压缩标识<br>
|
||||
* 检查数据,gmssl等库生成的密文不包含04前缀(非压缩数据标识),此处检查并补充
|
||||
* 参考:https://blog.csdn.net/softt/article/details/139978608
|
||||
* 根据公钥压缩形态不同,密文分为两种压缩形式:
|
||||
* C1( 03 + X ) + C3(32个字节)+ C2
|
||||
* C1( 02 + X ) + C3(32个字节)+ C2
|
||||
* 非压缩公钥正常形态为04 + X + Y,由于各个算法库差异,04有时候会省略
|
||||
* 非压缩密文正常形态为04 + C1 + C3 + C2
|
||||
*
|
||||
* @param data 待解密数据
|
||||
* @return 增加压缩标识后的数据
|
||||
*/
|
||||
private static byte[] prependCompressedFlag(byte[] data) {
|
||||
if (data[0] != 0x04 && data[0] != 0x02 && data[0] != 0x03) {
|
||||
// 默认非压缩形态
|
||||
data = ArrayUtil.insert(data, 0, 0x04);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------------- Private method end
|
||||
}
|
||||
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.crypto.bc;
|
||||
|
||||
import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec;
|
||||
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
|
||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* BC密钥规范工具类
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ECKeySpecUtil {
|
||||
|
||||
/**
|
||||
* 获取私钥规范
|
||||
*
|
||||
* @param d 私钥D值
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPrivateKeySpec
|
||||
*/
|
||||
public static ECPrivateKeySpec getPrivateKeySpec(final byte[] d, final ECParameterSpec parameterSpec) {
|
||||
return getPrivateKeySpec(BigIntegers.fromUnsignedByteArray(d), parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私钥规范
|
||||
*
|
||||
* @param d 私钥D值
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPrivateKeySpec
|
||||
*/
|
||||
public static ECPrivateKeySpec getPrivateKeySpec(final BigInteger d, final ECParameterSpec parameterSpec) {
|
||||
return new ECPrivateKeySpec(d, parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公钥规范
|
||||
*
|
||||
* @param q 公钥Q值
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPublicKeySpec
|
||||
*/
|
||||
public static ECPublicKeySpec getPublicKeySpec(final byte[] q, final ECParameterSpec parameterSpec) {
|
||||
return getPublicKeySpec(parameterSpec.getCurve().decodePoint(q), parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公钥规范
|
||||
*
|
||||
* @param x 公钥x坐标
|
||||
* @param y 公钥y坐标
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPublicKeySpec
|
||||
*/
|
||||
public static ECPublicKeySpec getPublicKeySpec(final byte[] x, final byte[] y, final ECParameterSpec parameterSpec) {
|
||||
return getPublicKeySpec(
|
||||
BigIntegers.fromUnsignedByteArray(x),
|
||||
BigIntegers.fromUnsignedByteArray(y),
|
||||
parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公钥规范
|
||||
*
|
||||
* @param x 公钥x坐标
|
||||
* @param y 公钥y坐标
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPublicKeySpec
|
||||
*/
|
||||
public static ECPublicKeySpec getPublicKeySpec(final BigInteger x, final BigInteger y, final ECParameterSpec parameterSpec) {
|
||||
return getPublicKeySpec(parameterSpec.getCurve().createPoint(x, y), parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公钥规范
|
||||
*
|
||||
* @param ecPoint 公钥坐标
|
||||
* @param parameterSpec {@link ECParameterSpec}
|
||||
* @return ECPublicKeySpec
|
||||
*/
|
||||
public static ECPublicKeySpec getPublicKeySpec(final ECPoint ecPoint, final ECParameterSpec parameterSpec) {
|
||||
return new ECPublicKeySpec(ecPoint, parameterSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link OpenSSHPrivateKeySpec}
|
||||
*
|
||||
* @param key 私钥,需为PKCS#1格式或OpenSSH格式
|
||||
* @return {@link OpenSSHPrivateKeySpec}
|
||||
*/
|
||||
public static OpenSSHPrivateKeySpec getOpenSSHPrivateKeySpec(final byte[] key) {
|
||||
return new OpenSSHPrivateKeySpec(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link OpenSSHPublicKeySpec}
|
||||
*
|
||||
* @param key 公钥,需为PKCS#1格式
|
||||
* @return {@link OpenSSHPublicKeySpec}
|
||||
*/
|
||||
public static OpenSSHPublicKeySpec getOpenSSHPublicKeySpec(final byte[] key) {
|
||||
return new OpenSSHPublicKeySpec(key);
|
||||
}
|
||||
}
|
@ -16,27 +16,26 @@
|
||||
|
||||
package org.dromara.hutool.crypto.bc;
|
||||
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
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.jce.spec.ECNamedCurveSpec;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.asn1.sec.ECPrivateKey;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
|
||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
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.OpenSSHPrivateKeySpec;
|
||||
import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec;
|
||||
import org.bouncycastle.jce.interfaces.ECPrivateKey;
|
||||
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.util.BigIntegers;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.crypto.CryptoException;
|
||||
import org.dromara.hutool.crypto.KeyUtil;
|
||||
import org.dromara.hutool.crypto.SecureUtil;
|
||||
@ -49,6 +48,8 @@ import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -59,15 +60,54 @@ import java.util.Objects;
|
||||
*/
|
||||
public class ECKeyUtil {
|
||||
|
||||
/**
|
||||
* 根据私钥参数获取公钥参数
|
||||
*
|
||||
* @param privateKeyParameters 私钥参数
|
||||
* @return 公钥参数
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static ECPublicKeyParameters getPublicParams(final ECPrivateKeyParameters privateKeyParameters) {
|
||||
final ECDomainParameters domainParameters = privateKeyParameters.getParameters();
|
||||
final ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), privateKeyParameters.getD());
|
||||
return new ECPublicKeyParameters(q, domainParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据私钥获取EC公钥
|
||||
*
|
||||
* @param privateKey EC私钥
|
||||
* @param spec 密钥规范
|
||||
* @return EC公钥
|
||||
*/
|
||||
public static PublicKey getECPublicKey(final ECPrivateKey privateKey, final ECParameterSpec spec) {
|
||||
final org.bouncycastle.jce.spec.ECPublicKeySpec keySpec =
|
||||
new org.bouncycastle.jce.spec.ECPublicKeySpec(getQFromD(privateKey.getD(), spec), spec);
|
||||
return KeyUtil.generatePublicKey("EC", keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据私钥D值获取公钥的点坐标(Q值)
|
||||
*
|
||||
* @param d 私钥d值
|
||||
* @param spec 密钥规范
|
||||
* @return 公钥的点坐标
|
||||
*/
|
||||
public static ECPoint getQFromD(final BigInteger d, final ECParameterSpec spec) {
|
||||
return spec.getG().multiply(d).normalize();
|
||||
}
|
||||
|
||||
// region ----- encode and decode
|
||||
|
||||
/**
|
||||
* 只获取私钥里的d,32位字节
|
||||
*
|
||||
* @param privateKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
||||
* @param privateKey {@link PublicKey},必须为org.bouncycastle.jce.interfaces.ECPrivateKey
|
||||
* @return 压缩得到的X
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static byte[] encodeECPrivateKey(final PrivateKey privateKey) {
|
||||
return ((BCECPrivateKey) privateKey).getD().toByteArray();
|
||||
return ((ECPrivateKey) privateKey).getD().toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +126,7 @@ public class ECKeyUtil {
|
||||
* 编码压缩EC公钥(基于BouncyCastle),即Q值<br>
|
||||
* 见:https://www.cnblogs.com/xinzhao/p/8963724.html
|
||||
*
|
||||
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||
* @param publicKey {@link PublicKey},必须为org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||
* @param isCompressed 是否压缩
|
||||
* @return 得到的Q
|
||||
* @since 5.5.9
|
||||
@ -112,7 +152,7 @@ public class ECKeyUtil {
|
||||
* 解码恢复EC压缩公钥,支持Base64和Hex编码,(基于BouncyCastle)
|
||||
*
|
||||
* @param encodeByte 压缩公钥
|
||||
* @param curveName EC曲线名,例如{@link SmUtil#SM2_DOMAIN_PARAMS}
|
||||
* @param curveName EC曲线名,例如{@link SM2Constant#SM2_DOMAIN_PARAMS}
|
||||
* @return 公钥
|
||||
* @since 4.4.4
|
||||
*/
|
||||
@ -125,6 +165,7 @@ public class ECKeyUtil {
|
||||
final ECNamedCurveSpec ecSpec = new ECNamedCurveSpec(curveName, curve, x9ECParameters.getG(), x9ECParameters.getN());
|
||||
return KeyUtil.generatePublicKey("EC", new ECPublicKeySpec(point, ecSpec));
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 密钥转换为AsymmetricKeyParameter
|
||||
@ -142,20 +183,7 @@ public class ECKeyUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据私钥参数获取公钥参数
|
||||
*
|
||||
* @param privateKeyParameters 私钥参数
|
||||
* @return 公钥参数
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static ECPublicKeyParameters getPublicParams(final ECPrivateKeyParameters privateKeyParameters) {
|
||||
final ECDomainParameters domainParameters = privateKeyParameters.getParameters();
|
||||
final ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), privateKeyParameters.getD());
|
||||
return new ECPublicKeyParameters(q, domainParameters);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------- Public Key
|
||||
// region ----- toXXPublicParams
|
||||
|
||||
/**
|
||||
* 转换为 ECPublicKeyParameters
|
||||
@ -164,7 +192,7 @@ public class ECKeyUtil {
|
||||
* @return ECPublicKeyParameters
|
||||
*/
|
||||
public static ECPublicKeyParameters toSm2PublicParams(final byte[] q) {
|
||||
return toPublicParams(q, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPublicParams(q, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,7 +202,7 @@ public class ECKeyUtil {
|
||||
* @return ECPublicKeyParameters
|
||||
*/
|
||||
public static ECPublicKeyParameters toSm2PublicParams(final String q) {
|
||||
return toPublicParams(q, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPublicParams(q, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,7 +213,7 @@ public class ECKeyUtil {
|
||||
* @return ECPublicKeyParameters
|
||||
*/
|
||||
public static ECPublicKeyParameters toSm2PublicParams(final String x, final String y) {
|
||||
return toPublicParams(x, y, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPublicParams(x, y, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,7 +224,7 @@ public class ECKeyUtil {
|
||||
* @return ECPublicKeyParameters
|
||||
*/
|
||||
public static ECPublicKeyParameters toSm2PublicParams(final byte[] xBytes, final byte[] yBytes) {
|
||||
return toPublicParams(xBytes, yBytes, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPublicParams(xBytes, yBytes, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,8 +324,10 @@ public class ECKeyUtil {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
// endreion
|
||||
|
||||
//--------------------------------------------------------------------------- Private Key
|
||||
|
||||
// region ----- toXXPrivateParams
|
||||
|
||||
/**
|
||||
* 转换为 ECPrivateKeyParameters
|
||||
@ -306,7 +336,7 @@ public class ECKeyUtil {
|
||||
* @return ECPrivateKeyParameters
|
||||
*/
|
||||
public static ECPrivateKeyParameters toSm2PrivateParams(final String d) {
|
||||
return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPrivateParams(d, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -316,7 +346,7 @@ public class ECKeyUtil {
|
||||
* @return ECPrivateKeyParameters
|
||||
*/
|
||||
public static ECPrivateKeyParameters toSm2PrivateParams(final byte[] d) {
|
||||
return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPrivateParams(d, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,7 +356,7 @@ public class ECKeyUtil {
|
||||
* @return ECPrivateKeyParameters
|
||||
*/
|
||||
public static ECPrivateKeyParameters toSm2PrivateParams(final BigInteger d) {
|
||||
return toPrivateParams(d, SmUtil.SM2_DOMAIN_PARAMS);
|
||||
return toPrivateParams(d, SM2Constant.SM2_DOMAIN_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -389,17 +419,20 @@ public class ECKeyUtil {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- 生成密钥 generateXXKey
|
||||
|
||||
/**
|
||||
* 将SM2算法的{@link ECPrivateKey} 转换为 {@link PrivateKey}
|
||||
* 将SM2算法的{@link ASN1Encodable}格式私钥 生成为 {@link PrivateKey}
|
||||
*
|
||||
* @param privateKey {@link ECPrivateKey}
|
||||
* @param privateKey {@link ASN1Encodable}格式的私钥
|
||||
* @return {@link PrivateKey}
|
||||
*/
|
||||
public static PrivateKey toSm2PrivateKey(final ECPrivateKey privateKey) {
|
||||
public static PrivateKey generatePrivateKey(final ASN1Encodable privateKey) {
|
||||
try {
|
||||
final PrivateKeyInfo info = new PrivateKeyInfo(
|
||||
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SmUtil.ID_SM2_PUBLIC_KEY_PARAM), privateKey);
|
||||
new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SM2Constant.ID_SM2_PUBLIC_KEY_PARAM), privateKey);
|
||||
return KeyUtil.generatePrivateKey("SM2", info.getEncoded());
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
@ -407,65 +440,45 @@ public class ECKeyUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link OpenSSHPrivateKeySpec}
|
||||
*
|
||||
* @param key 私钥,需为PKCS#1格式
|
||||
* @return {@link OpenSSHPrivateKeySpec}
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static KeySpec createOpenSSHPrivateKeySpec(final byte[] key) {
|
||||
return new OpenSSHPrivateKeySpec(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link OpenSSHPublicKeySpec}
|
||||
*
|
||||
* @param key 公钥,需为PKCS#1格式
|
||||
* @return {@link OpenSSHPublicKeySpec}
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static KeySpec createOpenSSHPublicKeySpec(final byte[] key) {
|
||||
return new OpenSSHPublicKeySpec(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试解析转换各种类型私钥为{@link ECPrivateKeyParameters},支持包括:
|
||||
* 生成SM2私钥,支持包括:
|
||||
*
|
||||
* <ul>
|
||||
* <li>D值</li>
|
||||
* <li>PKCS#8</li>
|
||||
* <li>PKCS#1</li>
|
||||
* <li>OpenSSH格式</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param privateKeyBytes 私钥
|
||||
* @return {@link ECPrivateKeyParameters}
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static ECPrivateKeyParameters decodePrivateKeyParams(final byte[] privateKeyBytes) {
|
||||
public static PrivateKey generateSm2PrivateKey(final byte[] privateKeyBytes) {
|
||||
if (null == privateKeyBytes) {
|
||||
return null;
|
||||
}
|
||||
final String algorithm = "SM2";
|
||||
KeySpec keySpec;
|
||||
// 尝试D值
|
||||
try {
|
||||
// 尝试D值
|
||||
return toSm2PrivateParams(privateKeyBytes);
|
||||
keySpec = ECKeySpecUtil.getPrivateKeySpec(privateKeyBytes, SM2Constant.SM2_EC_SPEC);
|
||||
return KeyUtil.generatePrivateKey(algorithm, keySpec);
|
||||
} catch (final Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
PrivateKey privateKey;
|
||||
//尝试PKCS#8
|
||||
try {
|
||||
privateKey = KeyUtil.generatePrivateKey("sm2", privateKeyBytes);
|
||||
keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
|
||||
return KeyUtil.generatePrivateKey(algorithm, keySpec);
|
||||
} catch (final Exception ignore) {
|
||||
// 尝试PKCS#1
|
||||
privateKey = KeyUtil.generatePrivateKey("sm2", createOpenSSHPrivateKeySpec(privateKeyBytes));
|
||||
}
|
||||
|
||||
return toPrivateParams(privateKey);
|
||||
// 尝试PKCS#1或OpenSSH格式
|
||||
keySpec = ECKeySpecUtil.getOpenSSHPrivateKeySpec(privateKeyBytes);
|
||||
return KeyUtil.generatePrivateKey(algorithm, keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试解析转换各种类型公钥为{@link ECPublicKeyParameters},支持包括:
|
||||
* 生成SM2公钥,支持包括:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Q值</li>
|
||||
@ -475,28 +488,46 @@ public class ECKeyUtil {
|
||||
*
|
||||
* @param publicKeyBytes 公钥
|
||||
* @return {@link ECPublicKeyParameters}
|
||||
* @since 5.5.9
|
||||
*/
|
||||
public static ECPublicKeyParameters decodePublicKeyParams(final byte[] publicKeyBytes) {
|
||||
if(null == publicKeyBytes){
|
||||
public static PublicKey generateSm2PublicKey(final byte[] publicKeyBytes) {
|
||||
if (null == publicKeyBytes) {
|
||||
return null;
|
||||
}
|
||||
final String algorithm = "SM2";
|
||||
KeySpec keySpec;
|
||||
// 尝试Q值
|
||||
try {
|
||||
// 尝试Q值
|
||||
return toSm2PublicParams(publicKeyBytes);
|
||||
keySpec = ECKeySpecUtil.getPublicKeySpec(publicKeyBytes, SM2Constant.SM2_EC_SPEC);
|
||||
return KeyUtil.generatePublicKey(algorithm, keySpec);
|
||||
} catch (final Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
PublicKey publicKey;
|
||||
//尝试X.509
|
||||
try {
|
||||
publicKey = KeyUtil.generatePublicKey("sm2", publicKeyBytes);
|
||||
keySpec = new X509EncodedKeySpec(publicKeyBytes);
|
||||
return KeyUtil.generatePublicKey(algorithm, keySpec);
|
||||
} catch (final Exception ignore) {
|
||||
// 尝试PKCS#1
|
||||
publicKey = KeyUtil.generatePublicKey("sm2", createOpenSSHPublicKeySpec(publicKeyBytes));
|
||||
}
|
||||
|
||||
return toPublicParams(publicKey);
|
||||
// 尝试PKCS#1
|
||||
keySpec = ECKeySpecUtil.getOpenSSHPublicKeySpec(publicKeyBytes);
|
||||
return KeyUtil.generatePublicKey(algorithm, keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试解析转换各种类型公钥为{@link ECPublicKeyParameters},支持包括:
|
||||
*
|
||||
* @param x 坐标X
|
||||
* @param y 坐标y
|
||||
* @return {@link ECPublicKeyParameters}
|
||||
*/
|
||||
public static PublicKey generateSm2PublicKey(final byte[] x, final byte[] y) {
|
||||
if (null == x || null == y) {
|
||||
return null;
|
||||
}
|
||||
return KeyUtil.generatePublicKey("sm2",
|
||||
ECKeySpecUtil.getPublicKeySpec(x, y, SM2Constant.SM2_EC_SPEC));
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import java.security.PublicKey;
|
||||
*/
|
||||
public class PemUtil {
|
||||
|
||||
// region ----- readPem
|
||||
/**
|
||||
* 读取PEM格式的私钥
|
||||
*
|
||||
@ -84,7 +85,7 @@ public class PemUtil {
|
||||
return KeyUtil.generatePrivateKey("EC", object.getContent());
|
||||
} catch (final Exception e) {
|
||||
// 尝试PKCS#1
|
||||
return KeyUtil.generatePrivateKey("EC", ECKeyUtil.createOpenSSHPrivateKeySpec(object.getContent()));
|
||||
return KeyUtil.generatePrivateKey("EC", ECKeySpecUtil.getOpenSSHPrivateKeySpec(object.getContent()));
|
||||
}
|
||||
}
|
||||
if (type.endsWith("PRIVATE KEY")) {
|
||||
@ -98,7 +99,7 @@ public class PemUtil {
|
||||
return KeyUtil.generatePublicKey("EC", object.getContent());
|
||||
} catch (final Exception ignore) {
|
||||
// 尝试PKCS#1
|
||||
return KeyUtil.generatePublicKey("EC", ECKeyUtil.createOpenSSHPublicKeySpec(object.getContent()));
|
||||
return KeyUtil.generatePublicKey("EC", ECKeySpecUtil.getOpenSSHPublicKeySpec(object.getContent()));
|
||||
}
|
||||
} else if (type.endsWith("PUBLIC KEY")) {
|
||||
return KeyUtil.generateRSAPublicKey(object.getContent());
|
||||
@ -155,7 +156,9 @@ public class PemUtil {
|
||||
IoUtil.closeQuietly(pemReader);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- writePem
|
||||
/**
|
||||
* 将私钥或公钥转换为PEM格式的字符串
|
||||
* @param type 密钥类型(私钥、公钥、证书)
|
||||
@ -221,4 +224,5 @@ public class PemUtil {
|
||||
IoUtil.closeQuietly(pemWriter);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.crypto.bc;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
|
||||
/**
|
||||
* SM2常量
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class SM2Constant {
|
||||
/**
|
||||
* SM2默认曲线
|
||||
*/
|
||||
public static final String SM2_CURVE_NAME = "sm2p256v1";
|
||||
/**
|
||||
* SM2椭圆曲线参数类
|
||||
*/
|
||||
public static final ECParameterSpec SM2_EC_SPEC = ECNamedCurveTable.getParameterSpec(SM2_CURVE_NAME);
|
||||
/**
|
||||
* SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper)
|
||||
*/
|
||||
public static final ECDomainParameters SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(SM2_EC_SPEC);
|
||||
/**
|
||||
* SM2国密算法公钥参数的Oid标识
|
||||
*/
|
||||
public static final ASN1ObjectIdentifier ID_SM2_PUBLIC_KEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
|
||||
}
|
@ -16,8 +16,6 @@
|
||||
|
||||
package org.dromara.hutool.crypto.bc;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
@ -29,10 +27,10 @@ import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.crypto.CryptoException;
|
||||
import org.dromara.hutool.crypto.asymmetric.SM2;
|
||||
import org.dromara.hutool.crypto.digest.mac.HMac;
|
||||
import org.dromara.hutool.crypto.digest.mac.HmacAlgorithm;
|
||||
import org.dromara.hutool.crypto.digest.SM3;
|
||||
import org.dromara.hutool.crypto.digest.mac.BCHMacEngine;
|
||||
import org.dromara.hutool.crypto.digest.mac.HMac;
|
||||
import org.dromara.hutool.crypto.digest.mac.HmacAlgorithm;
|
||||
import org.dromara.hutool.crypto.digest.mac.MacEngine;
|
||||
import org.dromara.hutool.crypto.symmetric.SM4;
|
||||
import org.dromara.hutool.crypto.symmetric.SymmetricCrypto;
|
||||
@ -64,18 +62,6 @@ import java.security.PublicKey;
|
||||
public class SmUtil {
|
||||
|
||||
private final static int RS_LEN = 32;
|
||||
/**
|
||||
* SM2默认曲线
|
||||
*/
|
||||
public static final String SM2_CURVE_NAME = "sm2p256v1";
|
||||
/**
|
||||
* SM2推荐曲线参数(来自https://github.com/ZZMarquis/gmhelper)
|
||||
*/
|
||||
public static final ECDomainParameters SM2_DOMAIN_PARAMS = BCUtil.toDomainParams(GMNamedCurves.getByName(SM2_CURVE_NAME));
|
||||
/**
|
||||
* SM2国密算法公钥参数的Oid标识
|
||||
*/
|
||||
public static final ASN1ObjectIdentifier ID_SM2_PUBLIC_KEY_PARAM = new ASN1ObjectIdentifier("1.2.156.10197.1.301");
|
||||
|
||||
/**
|
||||
* 创建SM2算法对象<br>
|
||||
@ -271,7 +257,7 @@ public class SmUtil {
|
||||
public static byte[] rsAsn1ToPlain(final byte[] rsDer) {
|
||||
final BigInteger[] decode;
|
||||
try {
|
||||
decode = StandardDSAEncoding.INSTANCE.decode(SM2_DOMAIN_PARAMS.getN(), rsDer);
|
||||
decode = StandardDSAEncoding.INSTANCE.decode(SM2Constant.SM2_DOMAIN_PARAMS.getN(), rsDer);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
@ -296,7 +282,7 @@ public class SmUtil {
|
||||
final BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
|
||||
final BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
|
||||
try {
|
||||
return StandardDSAEncoding.INSTANCE.encode(SM2_DOMAIN_PARAMS.getN(), r, s);
|
||||
return StandardDSAEncoding.INSTANCE.encode(SM2Constant.SM2_DOMAIN_PARAMS.getN(), r, s);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
@ -16,13 +16,20 @@
|
||||
|
||||
package org.dromara.hutool.crypto.asymmetric;
|
||||
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class Issue3728Test {
|
||||
@Test
|
||||
void sm2Test() {
|
||||
String publicKey="04beab9c2b800c03263b2d9cfcc832eb6827d5b62dc2ec7f8503c8832799af13b057d6b5bf5bc6c144753f3aa8b6cef8acb00a379a4fbed2f90c546fc2b4586bb0";
|
||||
String privateKey="3920cfc4828339b34da62b97b44d49d3a9c7dc84d9e6732d4b18f681a339519c";
|
||||
final String publicKey="04beab9c2b800c03263b2d9cfcc832eb6827d5b62dc2ec7f8503c8832799af13b057d6b5bf5bc6c144753f3aa8b6cef8acb00a379a4fbed2f90c546fc2b4586bb0";
|
||||
final String privateKey="3920cfc4828339b34da62b97b44d49d3a9c7dc84d9e6732d4b18f681a339519c";
|
||||
final SM2 sm2 = new SM2(privateKey, publicKey);
|
||||
|
||||
final String data = "你好 hutool";
|
||||
final byte[] encrypt = sm2.encrypt(data);
|
||||
final byte[] decrypt = sm2.decrypt(encrypt);
|
||||
Assertions.assertEquals(data, StrUtil.utf8Str(decrypt));
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.crypto.bc.ECKeyUtil;
|
||||
import org.dromara.hutool.crypto.KeyUtil;
|
||||
import org.dromara.hutool.crypto.SecureUtil;
|
||||
import org.dromara.hutool.crypto.bc.SM2Constant;
|
||||
import org.dromara.hutool.crypto.bc.SmUtil;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
@ -35,6 +36,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ -208,8 +210,8 @@ public class SM2Test {
|
||||
final byte[] data = KeyUtil.encodeECPublicKey(publicKey);
|
||||
final String encodeHex = HexUtil.encodeStr(data);
|
||||
final String encodeB64 = Base64.encode(data);
|
||||
final PublicKey Hexdecode = KeyUtil.decodeECPoint(encodeHex, SmUtil.SM2_CURVE_NAME);
|
||||
final PublicKey B64decode = KeyUtil.decodeECPoint(encodeB64, SmUtil.SM2_CURVE_NAME);
|
||||
final PublicKey Hexdecode = KeyUtil.decodeECPoint(encodeHex, SM2Constant.SM2_CURVE_NAME);
|
||||
final PublicKey B64decode = KeyUtil.decodeECPoint(encodeB64, SM2Constant.SM2_CURVE_NAME);
|
||||
Assertions.assertEquals(HexUtil.encodeStr(publicKey.getEncoded()), HexUtil.encodeStr(Hexdecode.getEncoded()));
|
||||
Assertions.assertEquals(HexUtil.encodeStr(publicKey.getEncoded()), HexUtil.encodeStr(B64decode.getEncoded()));
|
||||
}
|
||||
@ -286,7 +288,8 @@ public class SM2Test {
|
||||
final String priKey = "MHcCAQEEIE29XqAFV/rkJbnJzCoQRJLTeAHG2TR0h9ZCWag0+ZMEoAoGCCqBHM9VAYItoUQDQgAESkOzNigIsH5ehFvr9y" +
|
||||
"QNQ66genyOrm+Q4umCA4aWXPeRzmcTAWSlTineiReTFN2lqor2xaulT8u3a4w3AM/F6A==";
|
||||
|
||||
final PrivateKey privateKey = KeyUtil.generatePrivateKey("sm2", new OpenSSHPrivateKeySpec(SecureUtil.decode(priKey)));
|
||||
final PrivateKey privateKey = KeyUtil.generatePrivateKey("sm2", new OpenSSHPrivateKeySpec(
|
||||
Objects.requireNonNull(SecureUtil.decode(priKey))));
|
||||
final ECPrivateKeyParameters privateKeyParameters = ECKeyUtil.toPrivateParams(privateKey);
|
||||
|
||||
final SM2 sm2 = new SM2(privateKeyParameters, ECKeyUtil.getPublicParams(privateKeyParameters));
|
||||
|
@ -18,9 +18,14 @@ package org.dromara.hutool.crypto.bc;
|
||||
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.jce.interfaces.ECPrivateKey;
|
||||
import org.dromara.hutool.crypto.KeyUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class ECKeyUtilTest {
|
||||
|
||||
/**
|
||||
@ -42,4 +47,11 @@ public class ECKeyUtilTest {
|
||||
final ECPrivateKeyParameters keyParameters = ECKeyUtil.toSm2PrivateParams(privateKeyHex);
|
||||
Assertions.assertNotNull(keyParameters);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getECPublicKeyTest() {
|
||||
final KeyPair sm2 = KeyUtil.generateKeyPair("sm2");
|
||||
final PublicKey ecPublicKey = ECKeyUtil.getECPublicKey((ECPrivateKey) sm2.getPrivate(), SM2Constant.SM2_EC_SPEC);
|
||||
Assertions.assertEquals(sm2.getPublic(), ecPublicKey);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user