所需依賴
<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填充加密
* 優(yōu)點:簡單第步,利于并行計算,誤差不會被傳遞
* 缺點:加密模式易被確定
* @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填充加密
* 優(yōu)點:安全性高
* 缺點:不利于并行計算衷畦,誤差傳遞骂维,需要初始化向量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);
}