SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。
随着密码技术和计算机技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。
依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
public class SM2Utils {
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
protected static BigInteger n = new BigInteger(
"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
protected static BigInteger p = new BigInteger(
"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
protected static BigInteger a = new BigInteger(
"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
protected static BigInteger b = new BigInteger(
"28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
protected static BigInteger gx = new BigInteger(
"32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
protected static BigInteger gy = new BigInteger(
"BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
protected static ECDomainParameters ecc_bc_spec;
protected static int w = (int) Math.ceil(n.bitLength() * 1.0 / 2) - 1;
protected static BigInteger _2w = new BigInteger("2").pow(w);
protected static final int DIGEST_LENGTH = 32;
private static SecureRandom random = new SecureRandom();
protected static ECCurve.Fp curve;
protected static ECPoint G;
protected static boolean debug = false;
static {
curve = new ECCurve.Fp(p, // q
a, // a
b); // b
G = curve.createPoint(gx, gy);
ecc_bc_spec = new ECDomainParameters(curve, G, n);
}
/**
* 生成公私钥对
* @param compressedPubKey 是否压缩公钥
* @return
*/
public static String[] genKeyPair(boolean compressedPubKey) throws NoSuchAlgorithmException,InvalidAlgorithmParameterException{
// 获取SM2 椭圆曲线推荐参数
X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
// 构造EC 算法参数
ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
// 设置SM2 算法的 OID
GMObjectIdentifiers.sm2p256v1.toString()
// 设置曲线方程
, ecParameters.getCurve()
// 椭圆曲线G点
, ecParameters.getG()
// 大整数N
, ecParameters.getN());
// 创建 密钥对生成器
KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2的算法区域初始化密钥生成器
gen.initialize(sm2Spec, new SecureRandom());
// 获取密钥对
KeyPair keyPair = gen.generateKeyPair();
byte buffer[] = ((BCECPublicKey)keyPair.getPublic()).getQ().getEncoded(false);
String publicKeyStr = Base64.encodeBase64String(buffer);
String privateKeyStr = ((BCECPrivateKey)keyPair.getPrivate()).getD().toString(16);
return new String[]{publicKeyStr, privateKeyStr};
}
/**
* 私钥签名
* @param privateKeyStr 私钥
* @param plainText 待签名内容
* @return
*/
public static String sign(String privateKeyStr, String plainText) throws SignatureException,NoSuchAlgorithmException,InvalidKeyException{
// 生成SM2sign with sm3 签名验签算法实例
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), new BouncyCastleProvider());
BCECPrivateKey privateKey = getPrivatekeyFromD(new BigInteger(privateKeyStr,16));
// 签名需要使用私钥,使用私钥 初始化签名实例
signature.initSign(privateKey);
// 签名原文
// 写入签名原文到算法中
signature.update(plainText.getBytes(StandardCharsets.UTF_8));
// 计算签名值
byte[] signatureValue = signature.sign();
return Base64.encodeBase64String(signatureValue);
}
/**
* 验证签名
* @param publicKeyStr 公钥
* @param plainText 待签名内容
* @param sign 签名值
* @return
*/
public static boolean verify(String publicKeyStr, String plainText, String sign) throws SignatureException, NoSuchAlgorithmException,InvalidKeyException{
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), new BouncyCastleProvider());
// 签名需要使用公钥,使用公钥 初始化签名实例
byte buffer[] = Base64.decodeBase64(publicKeyStr);
ECPoint ecPoint = curve.decodePoint(buffer);
signature.initVerify(getPublickeyFromXY(ecPoint.getXCoord().toBigInteger(), ecPoint.getYCoord().toBigInteger()));
// 写入待验签的签名原文到算法中
signature.update(plainText.getBytes(StandardCharsets.UTF_8));
// 验签
return signature.verify(Base64.decodeBase64(sign));
}
public static BCECPrivateKey getPrivatekeyFromD(BigInteger d){
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);
return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
}
public static BCECPublicKey getPublickeyFromXY(BigInteger x, BigInteger y){
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec);
return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
public static void main(String[] args) throws Exception{
String[] keyPairArray = genKeyPair(true);
String publicKeyStr = keyPairArray[0];
String privateKeyStr = keyPairArray[1];
String plainText = "HelloWorld";
String signStr = sign(privateKeyStr, plainText);
boolean verify = verify(publicKeyStr, plainText, signStr);
System.out.println(verify);
}
————————————————
版权声明:本文为CSDN博主「xiaozhang51」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaozhang51/article/details/102894439