所需依赖
org.bouncycastle
bcprov-jdk15on
1.65
true
加解密工具类
/**
* 国密SM4加解密
*
* @author zhangjian
* @date 2020-11-05
*/
public class SM4Helper {
static {
//加入BouncyCastleProvider的支持 BouncyCastle->开源密码包,扩充密码算法支持
Security.addProvider(new BouncyCastleProvider());
}
//算法名称
public static final String ALGORITHM_NAME = "SM4";
//ECB P5填充
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
//CBC P5填充
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
//密钥长度
public static final int DEFAULT_KEY_SIZE = 128;
private static final Log LOG = LogFactory.getLog(SM2Helper.class);
/**
* 获取密钥
* @return 密钥
* @throws Exception 异常
*/
public static byte[] generateKey() {
try {
return generateKey(DEFAULT_KEY_SIZE);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* 获取指定长度密钥
* @param keySize 密钥的长度
* @return 密钥
* @throws Exception 异常
*/
public static byte[] generateKey(int keySize) {
try {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME,
BouncyCastleProvider.PROVIDER_NAME);
kg.init(keySize, new SecureRandom());
return kg.generateKey().getEncoded();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* ECB P5填充加密
* 优点:简单,利于并行计算,误差不会被传递
* 缺点:加密模式易被确定
* @param key 密钥
* @param data 明文数据
* @return 加密结果
* @throws Exception 异常
*/
public static String encryptEcbPadding(String key, String data) {
try {
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE,
BinaryUtils.parseHexString(key));
byte[] encryptBytes = cipher.doFinal(data.getBytes("UTF-8"));
return BinaryUtils.toHexStr(encryptBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* ECB P5填充解密
* @param key 密钥
* @param cipherText 加密后的数据
* @return 解密结果
*/
public static String decryptEcbPadding(String key, String cipherText) {
try {
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE,
BinaryUtils.parseHexString(key));
byte[] decryptBytes = cipher.doFinal(BinaryUtils.parseHexString(cipherText));
return new String(decryptBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* CBC P5填充加密
* 优点:安全性高
* 缺点:不利于并行计算,误差传递,需要初始化向量iv
* @param key 密钥
* @param iv 偏移量,CBC每轮迭代会和上轮结果进行异或操作,由于首轮没有可进行异或的结果,
* 所以需要设置偏移量,一般用密钥做偏移量
* @param data 明文数据
* @return 加密结果
*/
public static String encryptCbcPadding(String key, String iv, String data) {
try {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE,
BinaryUtils.parseHexString(key), BinaryUtils.parseHexString(iv));
byte[] encryptBytes = cipher.doFinal(data.getBytes("UTF-8"));
return BinaryUtils.toHexStr(encryptBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* CBC P5填充解密
* @param key 密钥
* @param iv 偏移量,CBC每轮迭代会和上轮结果进行异或操作,由于首轮没有可进行异或的结果,
* 所以需要设置偏移量,一般用密钥做偏移量
* @param cipherText 加密数据
* @return 解密结果
*/
public static String decryptCbcPadding(String key, String iv, String cipherText) {
try {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE,
BinaryUtils.parseHexString(key), BinaryUtils.parseHexString(iv));
byte[] decryptBytes = cipher.doFinal(BinaryUtils.parseHexString(cipherText));
return new String(decryptBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* ECB P5填充加解密Cipher初始化
* @param algorithmName 算法名称
* @param mode 1 加密 2解密
* @param key 密钥
* @return Cipher
*/
private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key);
return cipher;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
/**
* CBC P5填充加解密Cipher初始化
* @param algorithmName 算法名称
* @param mode 1 加密 2解密
* @param key 密钥
* @param iv 偏移量,CBC每轮迭代会和上轮结果进行异或操作,由于首轮没有可进行异或的结果,
* 所以需要设置偏移量,一般用密钥做偏移量
* @return Cipher
*/
private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key, byte[] iv) {
try {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, sm4Key, ivParameterSpec);
return cipher;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
}
单元测试
@Test
public void testEncryptAndDecryptContent(){
//明文数据
String SRC_DATA = "你好";
byte[] sm4key = SM4Helper.generateKey();
String key = BinaryUtils.toHexStr(sm4key);
System.out.println("SM4密钥:" + key);
byte[] iv = SM4Helper.generateKey();
String ivStr = BinaryUtils.toHexStr(iv);
System.out.println("iv偏移量密钥:" + ivStr);
String cipherText;
/*********************ECB加解密*************************/
cipherText = SM4Helper.encryptEcbPadding(key, SRC_DATA);
System.out.println("SM4 ECB Padding 加密结果16进制:\n" + cipherText);
String decryptedData;
decryptedData = SM4Helper.decryptEcbPadding(key, cipherText);
System.out.println("SM4 ECB Padding 解密结果:\n" + decryptedData);
Assert.assertEquals(SRC_DATA, decryptedData);
/*********************CBC加解密*************************/
cipherText = SM4Helper.encryptCbcPadding(key, ivStr, SRC_DATA);
System.out.println("SM4 CBC Padding 加密结果16进制:\n" + cipherText);
decryptedData = SM4Helper.decryptCbcPadding(key, ivStr, cipherText);
System.out.println("SM4 CBC Padding 解密结果:\n" + decryptedData);
Assert.assertEquals(SRC_DATA, decryptedData);
}