java key iv对称加密,SM4对称加密算法工具类Java版

所需依赖

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);

}