Base64, AES, RSA,SM2,SM4 对称加密、非对称加密
文章末尾附完整代码示例
加密技术概述
- 对称加密
- 使用同一把密钥进行加解密。代表算法:AES、SM4。
- 优点:速度快、适合大数据量加密。
- 缺点:密钥分发困难,易被截获后脱密。
- 非对称加密
- 使用公钥加密、私钥解密(或反之用于签名)。代表算法:RSA、SM2。
- 优点:无需共享私钥,便于密钥交换与签名验签。
- 缺点:速度慢,不适合直接加密大数据。
- 典型应用场景与对比
- 数据传输:对称加密加密数据,非对称加密加密对称密钥(混合加密)。
- 数字签名:非对称加密(私钥签名、公钥验签)。
- 批量文件/数据库加密:对称加密。
- 密钥交换:非对称加密。
Base64编码原理
- 编码规则与字符集
- 将每 3 字节(24 位)拆成 4 组,每组 6 位,对应 64 个字符集(A–Z、a–z、0–9、+、/),末尾用
=填充。
- 将每 3 字节(24 位)拆成 4 组,每组 6 位,对应 64 个字符集(A–Z、a–z、0–9、+、/),末尾用
- 用途
- 传输兼容性:把二进制数据转为可打印文本,避免传输过程中的字符集问题。
- 注意:Base64 不是加密,仅编码;常与加密算法的输出搭配使用(如密文 Base64 化)。
对称加密技术
AES(高级加密标准)
- 算法特点
- 分组密码。常用密钥长度:128/192/256 位(即 16/24/32 字节)。
- 工作模式
- 常见:ECB、CBC、GCM 等。
- 应用场景
- 文件/数据库加密、TLS(历史)、应用层数据保护。
- 常见坑
- 密钥长度不合法:需为 16/24/32 字节,否则抛出
IllegalArgumentException。 - 密文编码不匹配:Base64 与 Hex 不可混用。
- 密钥长度不合法:需为 16/24/32 字节,否则抛出
SM4(国密算法)
- 算法背景
- 中国商用密码标准,分组长度与密钥长度均为 128 位。
- 与 AES 对比
- 安全强度与实现复杂度均相近,SM4在国内合规场景更常用。
- 适用领域
- 政务、金融、信创与国产化生态。
非对称加密技术
RSA 算法
- 数学基础
- 基于大整数分解困难问题。
- 密钥生成
- 随机生成大素数对 p、q,计算 n=pq 与 φ(n),选择 e 并求 d,使 ed≡1 (mod φ(n)),得到公钥(n,e)与私钥(n,d)。
- 典型用途
- 密钥交换(加密对称密钥)、数字签名(私钥签名、公钥验签)。
SM2(国密算法)
- 基于 ECC
- 椭圆曲线密码学(ECC),以较短密钥提供与 RSA 同等级别的安全性。
- 对比 RSA
- 通常在相同安全级别下具有更短密钥与更好性能。
- 中国标准应用
- 电子认证、金融、区块链、政务系统等。
安全实践建议
- 混合加密方案
- 用对称算法(AES/SM4)加密业务数据;用非对称算法(RSA/SM2)加密对称密钥;传输对称密钥密文与数据密文。
- 国密合规性
- 金融、政务、信创等场景优先选用 SM 系列算法(SM2/SM3/SM4)。
- 密钥管理
- 不硬编码到源码。
- 使用 KMS/HSM/密钥托管服务或安全配置中心;按角色最小化访问权限;定期轮换与吊销;全链路加密与审计。
- 模式与参数
- 避免 ECB;优先 GCM(带认证)。妥善管理 IV/Nonce(随机、唯一、不可复用)。
性能与安全性权衡
- 选择标准
- 速度:AES/SM4 > SM2 ≈ ECC > RSA。
- 安全强度:取决于密钥长度、模式、填充与实现。
- 标准/合规:按行业规范(如国密)与国际标准(FIPS、NIST、ISO)。
- 未来趋势:抗量子
- 关注 NIST 甄选的 PQC(如 Kyber、Dilithium),并为“混合密钥协商”做技术储备。
示例代码
工具类(依赖hutool-crypto)
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.22</version>
</dependency>
Code
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class EncryptUtils {
/**
* 公钥
*/
public static final String PUBLIC_KEY = "publicKey";
/**
* 私钥
*/
public static final String PRIVATE_KEY = "privateKey";
/**
* Base64加密
*
* @param data 待加密数据
* @return 加密后字符串
*/
public static String encryptByBase64(String data) {
return Base64.encode(data, StandardCharsets.UTF_8);
}
/**
* Base64解密
*
* @param data 待解密数据
* @return 解密后字符串
*/
public static String decryptByBase64(String data) {
return Base64.decodeStr(data, StandardCharsets.UTF_8);
}
/**
* AES加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* AES加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByAesHex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* AES解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* sm4加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* sm4加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4Hex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* sm4解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* 产生sm2加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateSm2Key() {
Map<String, String> keyMap = new HashMap<>(2);
SM2 sm2 = SmUtil.sm2();
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
return keyMap;
}
/**
* sm2公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm2(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm2Hex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptBySm2(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("SM2需要传入私钥进行解密");
}
SM2 sm2 = SmUtil.sm2(privateKey, null);
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
/**
* 产生RSA加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateRsaKey() {
Map<String, String> keyMap = new HashMap<>(2);
RSA rsa = SecureUtil.rsa();
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
return keyMap;
}
/**
* rsa公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptByRsa(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByRsaHex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptByRsa(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
}
RSA rsa = SecureUtil.rsa(privateKey, null);
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
}
测试类
public static void main(String[] args) {
// base64加密解密
String originalText = "hello";
System.out.println("原文: " + originalText);
String s = EncryptUtils.encryptByBase64(originalText);
System.out.println("base64 密文: " + s);
System.out.println("base64 解密: " + EncryptUtils.decryptByBase64(s));
System.out.println("==========\n");
// aes 对称加密解密
System.out.println("原文: " + originalText);
String s1 = EncryptUtils.encryptByAes(originalText, "1234567890123456");
String s2 = EncryptUtils.encryptByAesHex(originalText, "1234567890123456");
System.out.println("aes密文: " + s1);
System.out.println("aes hex密文: " + s2);
System.out.println("aes 解密: " + EncryptUtils.decryptByAes(s1, "1234567890123456"));
System.out.println("aes hex 解密: " + EncryptUtils.decryptByAes(s2, "1234567890123456"));
System.out.println("==========\n");
// sm4 对称加密解密
System.out.println("原文: " + originalText);
String s3 = EncryptUtils.encryptBySm4(originalText, "1234567890123456");
String s4 = EncryptUtils.encryptBySm4Hex(originalText, "1234567890123456");
System.out.println("sm4密文: " + s3);
System.out.println("sm4 hex密文: " + s4);
System.out.println("sm4 解密: " + EncryptUtils.decryptBySm4(s3, "1234567890123456"));
System.out.println("sm4 hex 解密: " + EncryptUtils.decryptBySm4(s4, "1234567890123456"));
System.out.println("==========\n");
// sm2 非对称加密解密
System.out.println("原文: " + originalText);
Map<String, String> sm2Key = EncryptUtils.generateSm2Key();
String s5 = EncryptUtils.encryptBySm2(originalText, sm2Key.get(EncryptUtils.PUBLIC_KEY));
String s6 = EncryptUtils.encryptBySm2Hex(originalText, sm2Key.get(EncryptUtils.PUBLIC_KEY));
System.out.println("sm2密文: " + s5);
System.out.println("sm2 hex密文: " + s6);
System.out.println("sm2 解密: " + EncryptUtils.decryptBySm2(s5, sm2Key.get(EncryptUtils.PRIVATE_KEY)));
System.out.println("sm2 hex 解密: " + EncryptUtils.decryptBySm2(s6, sm2Key.get(EncryptUtils.PRIVATE_KEY)));
System.out.println("==========\n");
// rsa 非对称加密解密
System.out.println("原文: " + originalText);
Map<String, String> rsaKey = EncryptUtils.generateRsaKey();
String s7 = EncryptUtils.encryptByRsa(originalText, rsaKey.get(EncryptUtils.PUBLIC_KEY));
String s8 = EncryptUtils.encryptByRsaHex(originalText, rsaKey.get(EncryptUtils.PUBLIC_KEY));
System.out.println("rsa密文: " + s7);
System.out.println("rsa hex密文: " + s8);
System.out.println("rsa 解密: " + EncryptUtils.decryptByRsa(s7, rsaKey.get(EncryptUtils.PRIVATE_KEY)));
System.out.println("rsa hex 解密: " + EncryptUtils.decryptByRsa(s8, rsaKey.get(EncryptUtils.PRIVATE_KEY)));
System.out.println("==========");
}
结果

991

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



