icu.xuyijie.sm4utils.util.SM4Utils Maven / Gradle / Ivy
package icu.xuyijie.sm4utils.util;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 徐一杰
* @date 2022/10/11
*
* ECB 加密模式
* 不使用自定义 secretKey,一般用于后端自行加解密,如果是前端加密后端解密,则需要自定义secretKey,前后端一致才能正确解密
* 经过ECB加密的密文为:SM4Utils.encryptData_ECB("123456");
* 经过ECB解密的密文为:SM4Utils.decryptData_ECB("UQZqWWcVSu7MIrMzWRD/wA==");
* 使用自定义 secretKey,传入的 secretKey 必须为16位,可包含字母、数字、标点
* 经过ECB加密的密文为:SM4Utils.encryptData_ECB("123456");
* 经过ECB解密的密文为:SM4Utils.decryptData_ECB("UQZqWWcVSu7MIrMzWRD/wA==");
*
* CBC 加密模式(更加安全),需要两个密钥
* 经过CBC加密的密文为:SM4Utils.encryptData_CBC("123456");
* 经过CBC解密的密文为:SM4Utils.decryptData_CBC("hbMK6/IeJ3UTzaTgLb3f3A==");
* 同样可以自定义 secretKey 和 iv,需要两个密钥前后端都一致
* 经过CBC加密的密文为:SM4Utils.encryptData_CBC("123456","asdfghjklzxcvb!_","1234567890123456");
* 经过CBC解密的密文为:SM4Utils.decryptData_CBC("sTyCl3G6TF311kIENzsKNg==","asdfghjklzxcvb!_","1234567890123456");
*/
public class SM4Utils {
private SM4Utils() {
}
private static final Logger logger = Logger.getLogger(SM4Utils.class.getPackage().getName());
/**
* 默认 SECRET_KEY
* 当时用ECB模式的时候,和前端key一致
* secretKey 必须为16位,可包含字母、数字、标点
*/
private static final String SECRET_KEY = "GJwsXX_BzW=gJWJW";
/**
* 默认 IV
* 当时用CBC模式的时候,SECRET_KEY和IV都需要传值,解密要和加密的SECRET_KEY和IV一致,更加安全
* iv 必须为 16 位,可包含字母、数字、标点
*/
private static final String IV = "ZkR_SiNoSOFT=568";
static final String ECB = "ECB";
static final String CBC = "CBC";
private static final boolean HEX_STRING = false;
private static final Pattern P = Pattern.compile("\\s*|\t|\r|\n");
private static String encryptData(String type, String plainText, String secretKey, String iv) {
try {
SM4Context ctx = new SM4Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_ENCRYPT;
byte[] keyBytes = secretKey == null ? SECRET_KEY.getBytes() : secretKey.getBytes();
SM4 sm4 = new SM4();
sm4.sm4SetKeyEnc(ctx, keyBytes);
String cipherText;
if (ECB.equals(type)) {
byte[] encrypted = sm4.sm4CryptEcb(ctx, plainText.getBytes(StandardCharsets.UTF_8));
cipherText = Base64.getEncoder().encodeToString(encrypted);
if (cipherText != null && !cipherText.trim().isEmpty()) {
Matcher m = P.matcher(cipherText);
cipherText = m.replaceAll("");
}
} else {
byte[] ivBytes = iv == null ? IV.getBytes() : iv.getBytes();
byte[] encrypted = sm4.sm4CryptCbc(ctx, ivBytes, plainText.getBytes(StandardCharsets.UTF_8));
cipherText = Base64.getEncoder().encodeToString(encrypted);
if (cipherText != null && !cipherText.trim().isEmpty()) {
Matcher m = P.matcher(cipherText);
cipherText = m.replaceAll("");
}
}
return cipherText;
} catch (Exception e) {
logger.log(Level.WARNING, "加密失败!", e);
return null;
}
}
private static String decryptData(String type, String cipherText, String secretKey, String iv) {
try {
SM4Context ctx = new SM4Context();
ctx.isPadding = true;
ctx.mode = SM4.SM4_DECRYPT;
SM4 sm4 = new SM4();
byte[] bytes = secretKey == null ? SECRET_KEY.getBytes() : secretKey.getBytes();
if (ECB.equals(type)) {
sm4.sm4SetKeyDec(ctx, bytes);
byte[] decrypted = sm4.sm4CryptEcb(ctx, Base64.getDecoder().decode(cipherText));
return new String(decrypted, StandardCharsets.UTF_8);
} else {
byte[] keyBytes;
byte[] ivBytes;
if (HEX_STRING) {
keyBytes = Util.hexStringToBytes(secretKey);
ivBytes = Util.hexStringToBytes(iv);
} else {
keyBytes = bytes;
ivBytes = iv == null ? IV.getBytes() : iv.getBytes();
}
sm4.sm4SetKeyDec(ctx, keyBytes);
byte[] decrypted = sm4.sm4CryptCbc(ctx, ivBytes, Base64.getDecoder().decode(cipherText));
return new String(decrypted, StandardCharsets.UTF_8);
}
} catch (Exception e) {
logger.log(Level.WARNING, "解密失败!请检查密钥和密文是否对应", e);
return null;
}
}
/**
* ECB模式加密,自定义密钥,加解密密钥需一致
*
* @param plainText 要加密的数据
* @param secretKey 密钥,必须为 16 位,可包含字母、数字、标点
* @return 加密后的字符串
*/
public static String encryptData_ECB(String plainText, String secretKey) {
return encryptData(ECB, plainText, secretKey, null);
}
/**
* ECB模式加密,默认密钥
*
* @param plainText 要加密的数据
* @return 加密后的字符串
*/
public static String encryptData_ECB(String plainText) {
return encryptData(ECB, plainText, null, null);
}
/**
* ECB模式解密,自定义密钥,加解密密钥需一致
*
* @param cipherText 要解密的数据
* @param secretKey 密钥,必须为 16 位,可包含字母、数字、标点
* @return 解密后的字符串
*/
public static String decryptData_ECB(String cipherText, String secretKey) {
return decryptData(ECB, cipherText, secretKey, null);
}
/**
* ECB模式解密,默认密钥
*
* @param cipherText 要解密的数据
* @return 解密后的字符串
*/
public static String decryptData_ECB(String cipherText) {
return decryptData(ECB, cipherText, null, null);
}
/**
* CBC模式加密,SECRET_KEY和IV都需要传值,解密要和加密的SECRET_KEY和IV一致,更加安全
*
* @param plainText 要加密的数据
* @param secretKey 密钥一,必须为 16 位,可包含字母、数字、标点
* @param iv 密钥二,必须为 16 位,可包含字母、数字、标点
* @return 加密后的字符串
*/
public static String encryptData_CBC(String plainText, String secretKey, String iv) {
return encryptData(CBC, plainText, secretKey, iv);
}
/**
* CBC模式加密,SECRET_KEY和IV都需要传值,解密要和加密的SECRET_KEY和IV一致,更加安全
*
* @param plainText 要加密的数据
* @return 加密后的字符串
*/
public static String encryptData_CBC(String plainText) {
return encryptData(CBC, plainText, null, null);
}
/**
* CBC模式解密,SECRET_KEY和IV都需要传值,解密要和加密的SECRET_KEY和IV一致,更加安全
*
* @param cipherText 要解密的数据
* @param secretKey 密钥一,必须为 16 位,可包含字母、数字、标点
* @param iv 密钥二,必须为 16 位,可包含字母、数字、标点
* @return 解密后的字符串
*/
public static String decryptData_CBC(String cipherText, String secretKey, String iv) {
return decryptData(CBC, cipherText, secretKey, iv);
}
/**
* CBC模式解密,SECRET_KEY和IV都需要传值,解密要和加密的SECRET_KEY和IV一致,更加安全
*
* @param cipherText 要解密的数据
* @return 解密后的字符串
*/
public static String decryptData_CBC(String cipherText) {
return decryptData(CBC, cipherText, null, null);
}
// public static void main(String[] args) {
// System.out.println("经过ECB加密的密文为:" + SM4Utils.encryptData_ECB("41150320000416041X"));
// System.out.println("经过ECB解密的密文为:" + SM4Utils.decryptData_ECB("ZaCySfpl8DLflqpnM67eqBuFHqHevz6NvJY7i77t4zk="));
// System.out.println("经过CBC自定义密钥加密的密文为:" + SM4Utils.encryptData_CBC("41150320000416041X", "1sdfghjklzxcvbnm", ".234567890@$^-_*"));
// System.out.println("经过CBC自定义密钥解密的密文为:" + SM4Utils.decryptData_CBC("eB/OnKKFHwNLBl7s4QvGX7rtqS0LqfmU8AuXnhqjCbM=", "1sdfghjklzxcvbnm", ".234567890@$^-_*"));
// System.out.println(SM4Utils.encryptData_CBC("圣诞卡是你打死你"));
// System.out.println(SM4Utils.decryptData_CBC("fhMAEzAxdPNTe9I346GNTgQmnjpBfjJHQCFj+TxQDUg="));
// }
}