Java实现SM2前后端加解密

Sm2加解密原理,非对称加密,公钥加密,私钥解密。公私钥对成对生成,加密端解密端各自保存。用公钥加密必须要用对应的私钥才能解密,保证安全性。

这里我们实现的功能是前端加密,后端解密,这样前端暴露了公钥,后端持有配对的私钥,保证安全性。

1.jsp引入sm2

<script type="text/javascript" src="${pageContext.request.contextPath}/jslib/jquery-easyui-1.5.2/jquery.easyui.min.js" charset="utf-8"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/jslib/sm2/crypto-js.js" charset="utf-8"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/jslib/sm2/sm2.js" charset="utf-8"></script>

2.js加密

改写sm2.js,输入公钥

sm2.js下载路径

https://download.csdn.net/download/liuyuxuan2388/88612724

function sm2EncryptPwd(data) {

var publicKey = '04XXXXXXXXXXXXXXXXXXXXXXXXXXXCB42F6044B1BA8640B42AE038252F3490F02029E37B91D6E0796DE0B3D67166CDD971E556EF1D6B54ED350031817DED6B0C40';

return sm2Encrypt(data, publicKey, 1);

}

公钥用后端的生成公私钥对方法生成,请勿使用文章提到的公私钥对。

例如login.js使用了加密,即

login.js,调用加密函数

var encryptedPwd = sm2EncryptPwd(pwd);

3.后端Java解密

引用依赖库 bcprov-jdk15on-1.68.jar

<dependency>

  <groupId>org.bouncycastle</groupId>

  <artifactId>bcprov-jdk15on</artifactId>

  <version>1.68</version>

</dependency>

login.java

私钥附上对应的值

pwd = new String(SM2Utils.decrypt(pwd, sm2PrivKey));

Sm2Utils.java

package com.sgsg.basic.encrypt.util;


import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
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.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

	/**
	 * SM2工具类
	 * @author van
	 */
	public class SM2Utils {

	    /**
	     * 生成 SM2 公私钥对
	     *
	     * @return
	     * @throws NoSuchAlgorithmException
	     * @throws InvalidAlgorithmParameterException
	     */
	    public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
	        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
	        // 获取一个椭圆曲线类型的密钥对生成器
	        final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
	        // 产生随机数
	        SecureRandom secureRandom = new SecureRandom();
	        // 使用SM2参数初始化生成器
	        kpg.initialize(sm2Spec, secureRandom);
	        // 获取密钥对
	        KeyPair keyPair = kpg.generateKeyPair();
	        return keyPair;
	    }

	    /**
	     * 生产hex秘钥对
	     */
	    public static void geneSM2HexKeyPair(){
	        try {
	            KeyPair keyPair = geneSM2KeyPair();
	            PrivateKey privateKey = keyPair.getPrivate();
	            PublicKey publicKey = keyPair.getPublic();
	            System.out.println("========  EC X Y keyPair    ========");
	            System.out.println(privateKey);
	            System.out.println(publicKey);
	            System.out.println("========  hex keyPair       ========");
	            System.out.println("hex priKey: " + getPriKeyHexString(privateKey));
	            System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));
	            System.out.println("========  base64 keyPair    ========");
	            System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
	            System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
	            System.out.println("========  generate finished ========");
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
	    /**
	     * 获取私钥(16进制字符串,头部不带00长度共64)
	     *
	     * @param privateKey 私钥PrivateKey型
	     * @return
	     */
	    public static String getPriKeyHexString(PrivateKey privateKey) {
	        // OK
//	        BCECPrivateKey s=(BCECPrivateKey)privateKey;
//	        String priKeyHexString = Hex.toHexString(s.getD().toByteArray());
//	        if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){
//	            return priKeyHexString.substring(2);
//	        }
	        // OK
	        BCECPrivateKey key = (BCECPrivateKey) privateKey;
	        BigInteger intPrivateKey = key.getD();
	        String priKeyHexString = intPrivateKey.toString(16);
	        return priKeyHexString;
	    }
	    /**
	     * 获取私钥 base64字符串
	     *
	     * @param privateKey 私钥PrivateKey型
	     * @return
	     */
	    public static String getPriKeyBase64String(PrivateKey privateKey) {
	        return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
	    }

	    /**
	     * 获取公钥(16进制字符串,头部带04长度共130)
	     *
	     * @param publicKey 公钥PublicKey型
	     * @return
	     */
	    public static String getPubKeyHexString(PublicKey publicKey) {
	        BCECPublicKey key = (BCECPublicKey) publicKey;
	        return Hex.toHexString(key.getQ().getEncoded(false));
	    }
	    /**
	     * 获取公钥 base64字符串
	     *
	     * @param publicKey 公钥PublicKey型
	     * @return
	     */
	    public static String getPubKeyBase64String(PublicKey publicKey) {
	        return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
	    }

	    /**
	     * SM2加密算法
	     *
	     * @param publicKey 公钥
	     * @param data      明文数据
	     * @return
	     */
	    public static String encrypt(String data, PublicKey publicKey) {
	        return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
	    }

	    /**
	     * @param data
	     * @param publicKey
	     * @return
	     * @author 
	     * @version 1.0
	     * 2023年4月12日下午4:41:24
	     */
	    public static String encrypt(byte[] data, PublicKey publicKey) {
	        BCECPublicKey key = (BCECPublicKey) publicKey;
	        return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
	    }

	    /**
	     * @param data
	     * @param pubKeyHexString
	     * @return
	     * @author 
	     * @version 1.0
	     * 2023年4月12日下午4:46:37
	     */
	    public static String encrypt(String data, String pubKeyHexString) {
	        return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
	    }

	    /**
	     * SM2加密算法
	     *
	     * @param pubKeyHexString 公钥(16进制字符串)
	     * @param data            明文数据
	     * @return hex字符串
	     */
	    public static String encrypt(byte[] data, String pubKeyHexString) {
	        // 获取一条SM2曲线参数
	        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
	        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
	        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
	        //提取公钥点
	        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
	        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
	        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

	        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
	        // 设置sm2为加密模式
	        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

	        byte[] arrayOfBytes = null;
	        try {
	            arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);
	        } catch (Exception e) {
	            System.out.println("SM2加密时出现异常:" + e.getMessage());
	        }
	        return Hex.toHexString(arrayOfBytes);

	    }

	    /**
	     * SM2解密算法
	     * @param cipherData    hex格式密文
	     * @param privateKey    密钥PrivateKey型
	     * @return              明文
	     */
	    public static String decrypt(String cipherData, PrivateKey privateKey) {
	        return decrypt(Hex.decode(cipherData), privateKey);
	    }

	    /**
	     * @param cipherData
	     * @param privateKey
	     * @return
	     * @author 
	     * @version 1.0
	     * 2023年4月12日下午4:46:50
	     */
	    public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
	        BCECPrivateKey key = (BCECPrivateKey) privateKey;
	        return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
	    }

	    /**
	     * @param cipherData
	     * @param priKeyHexString
	     * @return
	     * @author 
	     * @version 1.0
	     * 2023年4月12日下午4:46:53
	     */
	    public static String decrypt(String cipherData, String priKeyHexString) {
	        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
	        if (!cipherData.startsWith("04")) {
	            cipherData = "04" + cipherData;
	        }
	        return decrypt(Hex.decode(cipherData), priKeyHexString);
	    }

	    /**
	     * SM2解密算法
	     *
	     * @param cipherData      密文数据
	     * @param priKeyHexString 私钥(16进制字符串)
	     * @return
	     */
	    public static String decrypt(byte[] cipherData, String priKeyHexString) {
	        //获取一条SM2曲线参数
	        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
	        //构造domain参数
	        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

	        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
	        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

	        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
	        // 设置sm2为解密模式
	        sm2Engine.init(false, privateKeyParameters);

	        String result = "";
	        try {
	            byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
	            return new String(arrayOfBytes);
	        } catch (Exception e) {
	            System.out.println("SM2解密时出现异常:" + e.getMessage());
	        }
	        return result;
	    }

	    /**
	     * @param data
	     * @param priKeyHexString hex私钥,长度64
	     * @return hex格式签名值
	     * @throws Exception
	     */
	    public static String sign(String data, String priKeyHexString) throws Exception {
	        return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
	    }

	    /**
	     * 签名
	     * @param data              原始数据,字节数组
	     * @param priKeyHexString   hex私钥,64长度
	     * @return                  Hex字符串
	     * @throws Exception
	     */
	    public static String sign(byte[] data, String priKeyHexString) throws Exception {
	        String signValue = null;
	        SM2Signer signer = new SM2Signer();
	        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
	        //构造domain参数
	        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
	        CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
	        signer.init(true, param);
	        signer.update(data, 0, data.length);
	        signValue = Hex.toHexString(signer.generateSignature());
	        return signValue;
	    }

	    /**
	     * 验签
	     * @param data                  数据
	     * @param signValue             签名值(hex型)
	     * @param pKHexString    hex130长度公钥
	     * @return 
	     */
	    public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {
	        return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
	    }

	    /**
	     * 验签
	     * @param data                  原始数据字节数组
	     * @param sign                  字节数组()
	     * @param pKHexString    hex130长度公钥
	     * @return                      true or false
	     * @throws Exception
	     */
	    public static boolean verify(byte[] data, byte[] sign, String pKHexString) throws Exception {
	        SM2Signer signer = new SM2Signer();
	        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
	        //构造domain参数
	        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
	        if (pKHexString.length() == 128) {
	            pKHexString = "04" + pKHexString;
	        }
	        ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pKHexString));
	        CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
	        signer.init(false, param);
	        signer.update(data, 0, data.length);
	        return signer.verifySignature(sign);
	    }

	    /**
	     * 私钥生成公钥
	     * @param priKeyHexString 私钥Hex格式,必须64位
	     * @return 公钥Hex格式,04开头,130位
	     * @throws Exception 例如:
	     */
	    public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {
	        if (priKeyHexString == null || priKeyHexString.length() != 64) {
	            System.err.println("priKey 必须是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");
	            return "";
	        }
	        String pubKeyHexString = null;
	        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
	        //构造domain参数
	        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);

	        ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
	        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);
	        PrivateKey privateKey = null;
	        privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);

	        // 临时解决办法
	        String pointString = privateKey.toString();
//	        System.out.println(pointString);
	        String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();
	        String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim();
//	        System.out.println(pointString_X);
//	        System.out.println(pointString_Y);

	        pubKeyHexString = "04" + pointString_X + pointString_Y;
	        return pubKeyHexString;

	    }

	}

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要在Java实现SM2加解密,您可以使用Bouncy Castle等Java加密库中的SM2实现。以下是一个简单的示例代码,演示如何使用Bouncy Castle实现SM2加解密: ```java import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.util.encoders.Hex; import java.security.SecureRandom; public class SM2Example { public static void main(String[] args) throws Exception { // 生成密钥对 AsymmetricCipherKeyPair keyPair = generateKeyPair(); // 明文 String plainText = "Hello, SM2!"; System.out.println("明文:" + plainText); // 加密 byte[] cipherText = encrypt(plainText.getBytes(), keyPair); System.out.println("密文:" + Hex.toHexString(cipherText)); // 解密 byte[] decryptedText = decrypt(cipherText, keyPair); System.out.println("解密后的明文:" + new String(decryptedText)); } private static AsymmetricCipherKeyPair generateKeyPair() { SecureRandom random = new SecureRandom(); // 定义SM2曲线 ECDomainParameters domainParams = new ECDomainParameters( SM2Curve.p, SM2Curve.a, SM2Curve.b, SM2Curve.G, SM2Curve.n, SM2Curve.h); // 生成密钥对 ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams, random); keyPairGenerator.init(keyGenerationParams); return keyPairGenerator.generateKeyPair(); } private static byte[] encrypt(byte[] plainText, AsymmetricCipherKeyPair keyPair) throws Exception { ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); // 使用SM3作为摘要算法 SM3Digest sm3Digest = new SM3Digest(); byte[] z = new byte[32]; // 计算用户标识 System.arraycopy(sm3Digest.digest("1234567812345678".getBytes()), 0, z, 0, z.length); // 计算公钥 byte[] publicKeyBytes = publicKey.getQ().getEncoded(false); System.arraycopy(publicKeyBytes, 1, z, z.length - 64, 64); // 使用SM2进行加密 SM2Engine sm2Engine = new SM2Engine(); sm2Engine.init(true, new SM2Engine.SM2KeyParameters(true, z, publicKey)); return sm2Engine.processBlock(plainText, 0, plainText.length); } private static byte[] decrypt(byte[] cipherText, AsymmetricCipherKeyPair keyPair) throws Exception { ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); // 使用SM3作为摘要算法 SM3Digest sm3Digest = new SM3Digest(); byte[] z = new byte[32]; // 计算用户标识 System.arraycopy(sm3Digest.digest("1234567812345678".getBytes()), 0, z, 0, z.length); // 计算公钥 byte[] publicKeyBytes = publicKey.getQ().getEncoded(false); System.arraycopy(publicKeyBytes, 1, z, z.length - 64, 64); // 使用SM2进行解密 SM2Engine sm2Engine = new SM2Engine(); sm2Engine.init(false, new SM2Engine.SM2KeyParameters(false, z, privateKey)); return sm2Engine.processBlock(cipherText, 0, cipherText.length); } } ``` 需要注意的是,在该示例代码中,我们使用了Bouncy Castle中的SM2曲线参数和SM3摘要算法。同时,在加解密过程中,需要计算用户标识和公钥,并将其作为SM2算法的输入参数之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LemonSmile_

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值