国密SM4加密Java实现代码:
package com.vfei.common.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;
/**
* 国密SM4加密工具类
* SM4算法是中国国家密码管理局发布的对称加密算法
* 密钥长度:128位(16字节)
* 分组长度:128位(16字节)
* 加密模式:CBC
* 填充方式:PKCS5Padding
*/
public class SM4Encrypt {
/**
* 密钥空间大小(128位)
*/
public static final int SM4_KEY_SIZE = 128;
/**
* 密钥长度(16字节)
*/
public static final int SM4_KEY_LENGTH = 16;
/**
* 算法名称
*/
public static final String SM4_NAME = "SM4";
/**
* CBC模式 + PKCS5填充
*/
public static final String SM4_CBC_PKCS5 = "SM4/CBC/PKCS5Padding";
/**
* 默认初始向量(16字节)
*/
protected static final byte[] SM4_IV = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31};
static {
// 注册BouncyCastle安全提供者
Security.addProvider(new BouncyCastleProvider());
}
/**
* 生成SM4密钥(Base64格式)
*
* @return Base64编码的密钥
*/
public String generateSm4Key() {
try {
return Base64.getEncoder().encodeToString(generateSm4KeyBytes(SM4_KEY_SIZE));
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
}
return null;
}
/**
* 生成SM4密钥字节数组
*
* @param keySize 密钥空间大小
* @return 密钥字节数组
*/
private static byte[] generateSm4KeyBytes(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(SM4_NAME, BouncyCastleProvider.PROVIDER_NAME);
keyGenerator.init(keySize, new SecureRandom());
return keyGenerator.generateKey().getEncoded();
}
/**
* 文本加密(CBC模式)
*
* @param plainText 明文
* @param sm4Key Base64编码的密钥
* @return Base64编码的密文
*/
public String encryptText(String plainText, String sm4Key) {
try {
byte[] keyBytes = prepareKey(sm4Key);
byte[] dataBytes = plainText.getBytes(StandardCharsets.UTF_8);
byte[] encryptedBytes = encryptCbc(dataBytes, keyBytes, SM4_IV);
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文本解密(CBC模式)
*
* @param cipherText Base64编码的密文
* @param sm4Key Base64编码的密钥
* @return 明文
*/
public String decryptText(String cipherText, String sm4Key) {
try {
byte[] keyBytes = prepareKey(sm4Key);
byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
byte[] decryptedBytes = decryptCbc(encryptedBytes, keyBytes, SM4_IV);
return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 字节数组加密(CBC模式)
*
* @param plainBytes 明文字节数组
* @param keyBytes 密钥字节数组
* @param iv 初始向量
* @return 密文字节数组
*/
public byte[] encryptCbc(byte[] plainBytes, byte[] keyBytes, byte[] iv) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, SM4_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5, BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return cipher.doFinal(plainBytes);
}
/**
* 字节数组解密(CBC模式)
*
* @param cipherBytes 密文字节数组
* @param keyBytes 密钥字节数组
* @param iv 初始向量
* @return 明文字节数组
*/
public byte[] decryptCbc(byte[] cipherBytes, byte[] keyBytes, byte[] iv) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, SM4_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5, BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return cipher.doFinal(cipherBytes);
}
/**
* 准备密钥(处理并解码)
*
* @param sm4Key Base64编码的密钥
* @return 密钥字节数组
*/
private byte[] prepareKey(String sm4Key) {
// 密钥长度处理
sm4Key = keyPadding(sm4Key);
// Base64解码
return Base64.getDecoder().decode(sm4Key);
}
/**
* 密钥长度补齐处理
* 确保密钥长度符合SM4要求(16字节的Base64编码约24字符)
*
* @param key 原始密钥
* @return 处理后的密钥
*/
private String keyPadding(String key) {
if (key == null) {
throw new IllegalArgumentException("密钥不能为空");
}
// 可根据需要实现密钥长度校验和补齐逻辑
return key;
}
/**
* 测试方法
*/
public static void main(String[] args) {
SM4Encrypt encrypt = new SM4Encrypt();
// 生成密钥
String key = encrypt.generateSm4Key();
System.out.println("生成的密钥: " + key);
// 待加密数据
String plainText = "{\"phone\":\"13800138000\",\"name\":\"张三\"}";
System.out.println("明文: " + plainText);
// 加密
String encrypted = encrypt.encryptText(plainText, key);
System.out.println("加密后: " + encrypted);
// 解密
String decrypted = encrypt.decryptText(encrypted, key);
System.out.println("解密后: " + decrypted);
}
}
核心要点说明
| 要素 | 说明 |
|---|---|
| 算法标准 | GB/T 32907-2016 《信息安全技术 SM4分组密码算法》 |
| 密钥长度 | 固定128位(16字节) |
| 分组长度 | 128位(16字节) |
| 加密模式 | CBC (Cipher Block Chaining) |
| 填充方式 | PKCS5Padding |
| 初始向量 | 固定16字节数组 |
| 依赖库 | BouncyCastle (bcprov-jdk18on) |
Maven依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.76</version>
</dependency>
安全注意事项
- 密钥管理:密钥必须妥善保管,不能硬编码在代码中
- 初始向量(IV):每次加密应使用不同的IV,增强安全性
- 密钥派生:建议使用密钥派生函数(KDF)从密码生成密钥
- 异常处理:加密解密失败时应妥善处理异常,避免敏感信息泄露

9722

被折叠的 条评论
为什么被折叠?



