所需依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.65</version>
<optional>true</optional>
</dependency>
曲线参数
/**
* 国密办SM2曲线推荐参数
* @author zhangjian
* @date 2020-11-06
*/
public class SM2Params {
//P256V1Curve 代表国密SM2推荐参数定义的椭圆曲线:
public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
//椭圆基点G的阶
public static final BigInteger SM2_ECC_N = CURVE.getOrder();
//椭圆曲线上所有点的个数与SM2_ECC_N相除的整数部分
public static final BigInteger SM2_ECC_H = CURVE.getCofactor();
//椭圆基点G x轴坐标
public static final BigInteger SM2_ECC_GX = new BigInteger(
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
//椭圆基点G y轴坐标
public static final BigInteger SM2_ECC_GY = new BigInteger(
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
//椭圆基点G
public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
//设置椭圆曲线参数
public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT,
SM2_ECC_N, SM2_ECC_H);
}
加解密工具类
**
* 国密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 publicKey = getPublicKeyParameters();
String privateKey = getPrivateKeyParameters();
String data = "123456";
//C1C2C3 mode
String encrypt = encrypt(data, publicKey);
System.out.println("加密结果:" + encrypt);
String decrypt = SM2Helper.decrypt(encrypt, privateKey);
System.out.println("解密数据" + decrypt);
Assert.assertEquals(data, decrypt);
}