加解密详细流程:
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);
}
}
自此结束~~~