SM4加解密详细流程

加解密详细流程:
1.双方通过SmUtil.sm2(),各自生成一对SM2公私钥,然后互相交换公钥
2.加密: 用发送方公钥的前16位作为SM4密钥,加密得到第一次密文,然后用接受方提供的SM2公钥将密文进行二次非对称加密得到最终密文
3.解密:用接受方SM2私钥将密文进行非对称解密得到一次解密的密文,然后再用发送方SM2公钥的前16位作为SM4密钥解密;

  • 导入依赖
	<!-- 胡图工具类 -->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.8.20</version>
		</dependency>
		<!-- sm4加解密 -->
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.70</version>
		</dependency>
  • 创建工具类
package com.example.test_sm2.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SymmetricCrypto;

/**
 * @Author 亿只王菜菜
 * @Date 2023/11/14 15:19
 * @Description: SM4加解密工具类
 */
public final class SM4Util {


    /**
     * 加密
     *
     * @param msg             需要加密的数据
     * @param betterPublicKey 对方公钥
     * @param easPublicKey    我方公钥
     * @return {@link String} 返回解密后的xml格式数据
     * @throws Exception 异常
     */
    public static String encryptAndSign(String msg, String betterPublicKey, String easPublicKey) throws Exception {
        if (!StrUtil.isBlank(msg)) {
            // 用我方公钥的前16 位字符加密内层
            String sM4Key = easPublicKey.substring(0, 16);
            // 创建一个SM4对称加密算法的工具类实例
            SymmetricCrypto sm4 = SmUtil.sm4(sM4Key.getBytes());
            // 对msg使用UTF-8编码格式进行加密
            String sM4encrypt = sm4.encryptHex(msg, "UTF-8");

            // 通过对方公钥获取非对称加密外层
            SM2 sm2 = SmUtil.sm2(null, betterPublicKey);
            // KeyType.PublicKey 指定使用公钥对以上对称加密后的数据,进行非对称加密,得出最终密文
            String sendJson = sm2.encryptHex(sM4encrypt, KeyType.PublicKey);
            return sendJson;
        }
        return null;
    }


    /**
     * 解密
     *
     * @param msg             需要解密的数据
     * @param easPrivateKey   我方(加密方的对方)私钥
     * @param betterPublicKey 对方公钥
     * @return {@link String} 返回解密后的数据
     * @throws Exception 异常
     */
    public static String checkSignAndDecrypt(String msg, String easPrivateKey, String betterPublicKey) throws Exception {
        if (!StrUtil.isBlank(msg)) {
            // 先解外层非对称加密,用我方私钥
            SM2 sm21 = SmUtil.sm2(easPrivateKey, null);
            // 解除最外层
            String sm21DecryptStr = sm21.decryptStr(msg, KeyType.PrivateKey);
            // 再解内层对称加密,
            String sM4Key11 = betterPublicKey.substring(0, 16);
            SymmetricCrypto sm41 = SmUtil.sm4(sM4Key11.getBytes());
            // 对内层进行解密
            return sm41.decryptStr(sm21DecryptStr);
        }
        return null;
    }
}

  • 编写测试类
package com.example.test_sm2.util;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;

/**
 * @Author 亿只王菜菜
 * @Date 2023/11/14 16:11
 * @Description: 测试加密解密
 */
public class Test {

    /**
     * 加密解密流程:
     * <p>
     * 1.双方通过SmUtil.sm2(),各自生成一对SM2公私钥,然后互相交换公钥
     * <p>
     * 2.加密: 用发送方公钥的前16位作为SM4密钥,加密得到第一次密文,然后用接受方提供的SM2公钥将密文进行二次非对称加密得到最终密文
     * <p>
     * 3.解密:用接受方SM2私钥将密文进行非对称解密得到一次解密的密文,然后再用发送方SM2公钥的前16位作为SM4密钥解密;
     */

    public static void main(String[] args) throws Exception {
        String json = "[{\n" +
                "\t\"startTime\": \"2021-01-01 00:00:00\",\n" +
                "\t\"endTime\": \"2037-09-26 14:30:30\",\n" +
                "\t\"currentPage\": \"1\",\n" +
                "\t\"bankAcc\": \"1001300000868920\"\n" +
                "}]";
        test(json);

    }



    /**
     * 测试使用普通格式的util
     */
    private static void test(String json) throws Exception {
        // 我方公钥私钥
        SM2 mySm = SmUtil.sm2();
        String myPrivateKey = mySm.getPrivateKeyBase64();
        String myPublicKey = mySm.getPublicKeyBase64();

        // 对方方公钥私钥
        SM2 youSm = SmUtil.sm2();
        String youPrivateKey = youSm.getPrivateKeyBase64();
        String youPublicKey = youSm.getPublicKeyBase64();


        System.out.println("我的生成的私钥-->" + myPrivateKey);
        System.out.println("我的生成的公钥-->" + myPublicKey);
        System.out.println("对方的生成的私钥-->" + youPrivateKey);
        System.out.println("对方的生成的公钥-->" + youPublicKey);
        System.out.println("《------------------------------------------------------------------------------------------------------》");
        System.out.println("加密前的数据--》" + json);

        // 加密
        String data = SM4Util.encryptAndSign(json, youPublicKey, myPublicKey);


        System.out.println("加密后的数据--》" + data);

        // 解密
        String newData = SM4Util.checkSignAndDecrypt(data, youPrivateKey, myPublicKey);


        System.out.println("解密后的数据--》" + newData);
    }

}

注:自此以上加解密逻辑完毕,以下代码展示的是将数据加密后拼接为xml格式,且xml格式为自定义,主要应用于WebService数据传输,如不需要可以直接使用 SM4Util,不用管以下代码

  • 创建xml解析相关异常类
package com.example.test_sm2.util;

/**
 * @Author 亿只王菜菜
 * @Date 2023/11/14 15:19
 * @Description: 自定义xml异常类
 */
public class XmlException extends RuntimeException {

    private static final long serialVersionUID = 381260478228427716L;

    public static final String XML_PAYLOAD_EMPTY = "xml.payload.empty";
    public static final String XML_ENCODE_ERROR = "xml.encoding.invalid";
    public static final String FILE_NOT_FOUND = "xml.file.not.found";
    public static final String XML_PARSE_ERROR = "xml.parse.error";
    public static final String XML_READ_ERROR = "xml.read.error";
    public static final String XML_VALIDATE_ERROR = "xml.validate.error";
    public static final String XML_TRANSFORM_ERROR = "xml.transform.error";

    public XmlException() {
        super();
    }

    public XmlException(String key, Throwable cause) {
        super(key, cause);
    }

    public XmlException(String key) {
        super(key);
    }

    public XmlException(Throwable cause) {
        super(cause);
    }

}  
  • 创建按照xml格式创建的sm4工具类
package com.example.test_sm2.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import org.w3c.dom.Element;

/**
 * @Author 亿只王菜菜
 * @Date 2023/11/14 15:19
 * @Description: SM4——Xml加解密工具类
 */
public final class SM4XmlUtil {


    /**
     * 加密
     *
     * @param msg             需要加密的数据
     * @param betterPublicKey 对方公钥
     * @param easPublicKey    我方公钥
     * @return {@link String} 返回解密后的xml格式数据
     * @throws Exception 异常
     */
    public static String encryptAndSign(String msg, String betterPublicKey, String easPublicKey) throws Exception {
        StringBuilder sb = new StringBuilder();
        if (!StrUtil.isBlank(msg)) {
            sb.append("<?xml version=\"1.0\" encoding=\"" + "UTF-8" + "\"?>");
            sb.append("<ciphertext>");
            // 用我方公钥的前16 位字符加密内层
            String sM4Key = easPublicKey.substring(0, 16);
            // 创建一个SM4对称加密算法的工具类实例
            SymmetricCrypto sm4 = SmUtil.sm4(sM4Key.getBytes());
            // 对msg使用UTF-8编码格式进行加密
            String sM4encrypt = sm4.encryptHex(msg, "UTF-8");
            sb.append("<encryption_type>SM4</encryption_type>");
            // 通过对方公钥获取非对称加密外层
            SM2 sm2 = SmUtil.sm2(null, betterPublicKey);
            // KeyType.PublicKey 指定使用公钥对以上对称加密后的数据,进行非对称加密,得出最终密文
            String sendJson = sm2.encryptHex(sM4encrypt, KeyType.PublicKey);
            sb.append("<sign>").append(sendJson).append("</sign>");
            sb.append("<sign_type>SM2</sign_type>");
            sb.append("</ciphertext>");
        }
        return sb.toString();
    }


    /**
     * 解密
     *
     * @param msg             需要解密的数据
     * @param easPrivateKey   我方(加密方的对方)私钥
     * @param betterPublicKey 对方公钥
     * @return {@link String} 返回解密后的数据
     * @throws Exception 异常
     */
    public static String checkSignAndDecrypt(String msg, String easPrivateKey, String betterPublicKey) throws Exception {
        if (!StrUtil.isBlank(msg)) {
            // 创建一个xml对象
            Element e = XmlUtils.getRootElementFromString(msg);
            String sign = XmlUtils.getElementValue(e, "sign");
            // 先解外层非对称加密,用我方私钥
            SM2 sm21 = SmUtil.sm2(easPrivateKey, null);
            // 解除最外层
            String sm21DecryptStr = sm21.decryptStr(sign, KeyType.PrivateKey);
            // 再解内层对称加密,
            String sM4Key11 = betterPublicKey.substring(0, 16);
            SymmetricCrypto sm41 = SmUtil.sm4(sM4Key11.getBytes());
            // 对内层进行解密
            return sm41.decryptStr(sm21DecryptStr);

        }
        return null;
    }
}

  • 新增测试
package com.example.test_sm2.util;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.SM2;

/**
 * @Author 亿只王菜菜
 * @Date 2023/11/14 16:11
 * @Description: 测试加密解密
 */
public class Test {

    /**
     * 加密解密流程:
     * <p>
     * 1.双方通过SmUtil.sm2(),各自生成一对SM2公私钥,然后互相交换公钥
     * <p>
     * 2.加密: 用发送方公钥的前16位作为SM4密钥,加密得到第一次密文,然后用接受方提供的SM2公钥将密文进行二次非对称加密得到最终密文
     * <p>
     * 3.解密:用接受方SM2私钥将密文进行非对称解密得到一次解密的密文,然后再用发送方SM2公钥的前16位作为SM4密钥解密;
     */

    public static void main(String[] args) throws Exception {
        String json = "[{\n" +
                "\t\"startTime\": \"2021-01-01 00:00:00\",\n" +
                "\t\"endTime\": \"2037-09-26 14:30:30\",\n" +
                "\t\"currentPage\": \"1\",\n" +
                "\t\"bankAcc\": \"1001300000868920\"\n" +
                "}]";
        // testXML(json);
        test(json);

    }

    /**
     * 测试使用xml格式的util
     */
    private static void testXML(String json) throws Exception {
        // 我方公钥私钥
        SM2 mySm = SmUtil.sm2();
        String myPrivateKey = mySm.getPrivateKeyBase64();
        String myPublicKey = mySm.getPublicKeyBase64();

        // 对方方公钥私钥
        SM2 youSm = SmUtil.sm2();
        String youPrivateKey = youSm.getPrivateKeyBase64();
        String youPublicKey = youSm.getPublicKeyBase64();


        System.out.println("我的生成的私钥-->" + myPrivateKey);
        System.out.println("我的生成的公钥-->" + myPublicKey);
        System.out.println("对方的生成的私钥-->" + youPrivateKey);
        System.out.println("对方的生成的公钥-->" + youPublicKey);
        System.out.println("《------------------------------------------------------------------------------------------------------》");
        System.out.println("加密前的数据--》" + json);

        // 加密
        String data = SM4XmlUtil.encryptAndSign(json, youPublicKey, myPublicKey);


        System.out.println("加密后的数据--》" + data);

        // 解密
        String newData = SM4XmlUtil.checkSignAndDecrypt(data, youPrivateKey, myPublicKey);


        System.out.println("解密后的数据--》" + newData);
    }


    /**
     * 测试使用普通格式的util
     */
    private static void test(String json) throws Exception {
        // 我方公钥私钥
        SM2 mySm = SmUtil.sm2();
        String myPrivateKey = mySm.getPrivateKeyBase64();
        String myPublicKey = mySm.getPublicKeyBase64();

        // 对方方公钥私钥
        SM2 youSm = SmUtil.sm2();
        String youPrivateKey = youSm.getPrivateKeyBase64();
        String youPublicKey = youSm.getPublicKeyBase64();


        System.out.println("我的生成的私钥-->" + myPrivateKey);
        System.out.println("我的生成的公钥-->" + myPublicKey);
        System.out.println("对方的生成的私钥-->" + youPrivateKey);
        System.out.println("对方的生成的公钥-->" + youPublicKey);
        System.out.println("《------------------------------------------------------------------------------------------------------》");
        System.out.println("加密前的数据--》" + json);

        // 加密
        String data = SM4Util.encryptAndSign(json, youPublicKey, myPublicKey);


        System.out.println("加密后的数据--》" + data);

        // 解密
        String newData = SM4Util.checkSignAndDecrypt(data, youPrivateKey, myPublicKey);


        System.out.println("解密后的数据--》" + newData);
    }

}

自此结束~~~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

亿只王菜菜

各位爷,赏口饭吃吧

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

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

打赏作者

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

抵扣说明:

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

余额充值