常用加密算法
【摘要】 1、常用加密算法 1.1、MD5加密 适用场景MD5 作为一种广泛使用的哈希函数,主要用于以下场景:数据完整性校验:验证文件传输过程中是否被篡改。数字签名:确保信息传输的完整性和来源的真实性。密码存储:在数据库中存储用户密码的哈希值,而非明文。 功能特性长度固定:无论输入长度如何,输出都是128位(16字节)的哈希值。快速计算:MD5 算法计算速度快,适用于大量数据的哈希处理。不可逆性:M...
1、常用加密算法
1.1、MD5加密
适用场景
MD5 作为一种广泛使用的哈希函数,主要用于以下场景:
- 数据完整性校验:验证文件传输过程中是否被篡改。
- 数字签名:确保信息传输的完整性和来源的真实性。
- 密码存储:在数据库中存储用户密码的哈希值,而非明文。
功能特性
- 长度固定:无论输入长度如何,输出都是128位(16字节)的哈希值。
- 快速计算:MD5 算法计算速度快,适用于大量数据的哈希处理。
- 不可逆性:MD5 加密是不可逆的,因此通常`只有加密过程,而没有解密过程
原理及流程图
MD5 算法将输入的消息经过填充、分段、循环处理等步骤,最终生成一个128位的哈希值。
用字符串"Hello"的加密过程说明 MD5 算法的基本处理流程:
1. 原始输入(明文) | |(转换为二进制) V 2. 二进制数据 "Hello" | |(填充到512位的倍数) V 3. 填充后的二进制数据 | |(添加原始长度的64位表示) V 4. 长度附加后的二进制数据 | |(分割成512位的消息块) V 5. 消息块1 ... 消息块n | |(每块512位,共16个32位字) V 6. 初始MD5值(ABCD) | |(通过主循环处理每个块) V 7. 主循环(每块16步) |_________|_________| | | | V V V 8. 经过F, G, H, I函数的32位字 |_________|_________| | | | V V V 9. 更新后的MD5值 | V 10. 最终MD5散列值(128位)代码实现
使用 Java 实现 MD5 加密的示例代码:
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Example { //用于将字节数组转换为十六进制表示的字符串 public static String toHexString(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } public static String getMD5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] messageDigest = md.digest(input.getBytes()); return toHexString(messageDigest); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static void main(String[] args) { String password = "Hello, World!"; //输出结果:65a8e27d8879283831b664bd8b7f0ad4 System.out.println("MD5 Hash: " + getMD5(password)); } }注意事项
- MD5 算法已经不再被认为是安全的,因此不推荐用于需要高安全性的加密场景。
- MD5 算法的输出可以作为数据完整性校验,但不应用于加密和解密操作。
1.2、AES 加密
适用场景
AES(高级加密标准)是一种广泛使用的
对称加密算法,适用于以下场景:
- 数据传输安全:在网络通信中加密数据,保护数据传输过程中的隐私和完整性。
- 文件和数据存储:对存储在服务器或云端的敏感文件进行加密。
- VPN 连接:确保远程连接的安全性。
功能特性
- 安全性:AES 提供强大的安全性,是目前最推荐使用的对称加密算法之一。
- 灵活性:支持128、192和256位的密钥长度,适应不同的安全级别需求。
- 效率高:AES算法
效率高,适合大量数据的快速加密。原理及流程图
例如,使用AES加密算法加密字符串 “hello”
1. 明文 "hello" | |(转换为二进制格式) V 2. 128位二进制明文 | |(可能需要填充) V 3. 填充后的128位二进制数据 | |(密钥扩展) V 4. 生成轮密钥 | |(初始轮密钥加) V 5. 与轮密钥异或后的数据块 | |(主轮加密处理) V |_________________________| | | | SubBytes(字节代换) | |_________________________| | | | ShiftRows(行移位) | |_________________________| | | | MixColumns (列混淆) | |_________________________| | | | AddRoundKey(轮密钥加) | |_________________________| V V 6. 密文数据块 | V 7. 密文(二进制格式)示例代码
对字符串 “hello” 进行AES加密的示例:
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.security.NoSuchAlgorithmException; public class AESExample { public static void main(String[] args) { try { // 实例化密钥生成器,并初始化为AES(128位) KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 初始化128位 ,可修改 keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); // 实例化Cipher对象,用于AES加密 Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 转换明文字符串 "hello" 为二进制数据 String plainText = "hello"; byte[] plainTextBytes = plainText.getBytes(); // 加密得到密文 byte[] encryptedBytes = cipher.doFinal(plainTextBytes); // 打印加密后的密文(通常以十六进制形式表示) System.out.println("Encrypted bytes: " + bytesToHex(encryptedBytes)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } // 将字节数组转换为十六进制字符串 private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }注意事项
- AES加密是
对称加密,加密和解密使用相同的密钥。- 密钥长度的选择需要根据具体的应用场景和安全需求来确定。
1.3、DES加密
适用场景
DES(数据加密标准)是一种较早的
对称加密算法,由于密钥长度(56位有效密钥,相对较短),其安全性相对较低,一般一些旧的系统中仍然使用。主要适用场景包括:
- 数据传输:在安全性要求不是特别高的场合,DES仍可用于数据传输的加密。
功能特性
- 对称加密:使用相同的密钥进行加密和解密。
- 密钥长度:实际有效的密钥长度为56位,另外8位用于奇偶性校验。
- 效率:由于算法较老,在现代硬件上的相对速度较慢。
- 安全性:由于密钥长度较短,DES容易受到暴力破解攻击,因此认为不安全的。
原理及流程图
我们通过以下步骤使用DES加密字符串 “hello”:
1. 明文 "hello" | |(转换为二进制并填充) V 2. 64位二进制明文 | |(初始置换 IP) V 3. 初始置换后的数据块 | |(密钥加) V 4. 与密钥异或后的数据块 | |(Feistel网络处理) V |_________________________| | | | 密钥加 | |_________________________| | | | 置换 | |_________________________| | | | 代换 | |_________________________| | | | 循环左移 | |_________________________| V V 5. Feistel网络输出 | |(逆置换 IP^-1) V 6. 逆置换后的密文块 | V 7. 密文示例代码(Java)
以下是使用Java代码对字符串 “hello” 进行DES加密的示例:
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class DESExample { public static void main(String[] args) { try { // 实例化密钥生成器,并初始化为DES KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); keyGenerator.init(56); // DES密钥长度为56位 SecretKey secretKey = keyGenerator.generateKey(); // 实例化Cipher对象,用于DES加密 Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(new byte[8]); // 使用8位的IV cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); // 转换明文字符串 "hello" 为二进制数据 String plainText = "hello"; byte[] plainTextBytes = plainText.getBytes("UTF-8"); // 加密得到密文 byte[] encryptedBytes = cipher.doFinal(plainTextBytes); // 打印加密后的密文(通常以Base64形式表示) System.out.println("Encrypted text: " + Base64.getEncoder().encodeToString(encryptedBytes)); } catch (Exception e) { e.printStackTrace(); } } }注意事项
- DES由于其较短的密钥长度,已不适用于安全性要求高的场合。
- 在实际使用中,推荐使用AES或其他更安全的加密算法。
1.4、国密算法加密
适用场景
国密算法,即中国国家商用密码算法标准,包括但不限于SM1、SM2、SM3、SM4等。主要用于:
- 金融服务:在金融服务行业中用于保证交易安全。
- 政府通信:确保政府数据传输的安全性。
- 企业数据保护:用于保护商业机密和客户数据。
- 安全令牌:用于安全令牌的加密。
功能特性
- 标准性:国密算法符合中国的商用密码标准。
- 安全性:可抵御多种已知攻击方式。
- 多样性:包含多种算法,如SM2用于数字签名,SM4用于数据加密。
原理及流程图
使用字符串 “hello” 进行说明:
1. 明文 "hello" | |(转换为二进制格式) V 2. 二进制明文数据 | |(配置密钥和参数) V 3. 初始化加密模块 | |(设置SM4密钥和IV) V 4. 使用SM4加密明文 | |(多轮迭代加密处理) V 5. 输出SM4密文示例代码(Java)
依赖:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.76</version> </dependency>使用Java伪代码对字符串 “hello” 进行SM4加密的示例:
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import java.security.NoSuchAlgorithmException; import java.security.Security; public class SM4Example { public static void main(String[] args) { try { // 添加Bouncy Castle安全提供者 Security.addProvider(new BouncyCastleProvider()); // 实例化密钥生成器,并初始化为SM4 KeyGenerator keyGenerator = KeyGenerator.getInstance("SM4"); keyGenerator.init(128); // SM4密钥长度为128位 SecretKey secretKey = keyGenerator.generateKey(); // 实例化Cipher对象,用于SM4加密 Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding"); IvParameterSpec iv = new IvParameterSpec(new byte[16]); // 16字节的IV cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); // 转换明文字符串 "hello" 为二进制数据 String plainText = "hello"; byte[] plainTextBytes = plainText.getBytes("UTF-8"); // 加密得到密文 byte[] encryptedBytes = cipher.doFinal(plainTextBytes); // 打印加密后的密文(通常以十六进制或Base64形式表示) System.out.println("Encrypted text: " + bytesToHex(encryptedBytes)); } catch (Exception e) { e.printStackTrace(); } } // 将字节数组转换为十六进制字符串 private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }注意事项
- 密钥管理是加密过程中的一个重要环节,密钥应该安全地生成、存储和分发。
- 国密算法的使用需要遵守相关的法律法规和标准。
1.5、 RSA 加密
适用场景
使用场景:
- 安全电子邮件:使用 RSA 对邮件内容进行加密,确保邮件传输过程中的安全性。
- SSL/TLS:在 SSL/TLS 协议中,RSA 用于加密交换对称密钥,以建立安全通信。
- 数字签名:RSA 用于生成数字签名,验证软件或文档的完整性和来源。
- 身份验证:用于身份验证过程,确保通信双方的身份。
功能特性
- 非对称加密:使用一对密钥,公钥加密,私钥解密。
- 安全性:难以破解,安全性高。
- 密钥长度:通常使用 1024 位、2048 位或更长的密钥,以提高安全性。
原理及流程图
例如,明文"hello"加密解密过程如下:
1. 明文 "hello" | |(转换为数值 m) V 2. 使用公钥 (e, n) 加密 |___________| | | | m^e mod n| |___________| | V 3. 得到密文 c | |(使用私钥 (d, n) 解密) V |___________| | | | c^d mod n| |___________| | V 4. 恢复明文 m | V 5. 明文 "hello"1.6程序实现
使用Java实现RSA加密和解密的简单示例:
import javax.crypto.Cipher; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSAExample { public static void main(String[] args) throws Exception { // 生成密钥对 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 公钥和私钥 PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); // 明文 String plainText = "hello"; byte[] data = plainText.getBytes(); // 加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encryptedData = cipher.doFinal(data); System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encryptedData)); // 解密 cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedData = cipher.doFinal(encryptedData); String decryptedText = new String(decryptedData); System.out.println("Decrypted: " + decryptedText); } }注意事项
- 密钥长度:推荐使用2048位或更长的密钥以确保安全性。
- 密钥管理:公钥可以公开,但私钥必须保密,防止泄露。
- 性能:RSA加密和解密的速度相对较慢,不适合加密大量数据。
1.7、总结
综上,实际使用中,常常根据自身系统特征选择。比如:执行效率、数据量、安全合规要求等,这里给出上述简易版整理。
加密算法 名称 有效长度 特性 是否加密解密 优点 缺点 MD5 MD5 散列算法 128位 散列函数 不可逆 速度快,简单 安全性低,易受碰撞攻击 AES AES 加密算法 128/192/256位 对称加密 是 安全性高,效率高 无严重弱点 DES DES 加密算法 56位 对称加密 是 速度快,曾广泛使用 密钥短,安全性低 国密SM4 SM4 加密算法 128位 对称加密 是 符合国内标准,安全性好 使用不如AES广泛 RSA RSA 加密算法 1024/2048位 非对称加密 是 非对称加密,适合数字签名 计算量大,速度慢
1.8、加密工具类
1.8.1、加密算法
| 类型 | 用途 | 特点 | 示例 | 推荐使用 | 安全性评价 | 适用场景 |
|---|---|---|---|---|---|---|
| 对称加密 | 加密大量数据 | 加密解密速度快,密钥管理难 | DES | 否 | 弱 | 老旧系统兼容性 |
| 3DES | 否 | 中 | 有限制的老旧系统 | |||
| AES (Rijndael) | 是 | 强 | 大量数据加密,现代应用 | |||
| SM4 (SMS4) | 是 | 强 | 国密标准,国内应用 | |||
| Blowfish | 否 | 中 | 小规模应用 | |||
| 非对称加密 | 安全通信,数字签名 | 安全性高,但速度慢 | RSA | 是 | 强 | 数字签名,密钥交换 |
| ECC (Elliptic Curve Cryptography) | 是 | 强 | 密钥交换,移动设备 | |||
| 散列函数 | 数据完整性校验,密码存储 | 单向,不可逆 | MD5 | 否 | 弱 | 消息摘要(非安全性要求) |
| SHA-1 | 否 | 弱 | 消息摘要(非安全性要求) | |||
| SHA-256 | 是 | 强 | 安全消息摘要,密码哈希 | |||
| SM3 | 是 | 强 | 国密标准,国内应用 | |||
| 编码 | 编码二进制数据为文本形式 | 不是加密,可逆转换 | Base64 | 否 | N/A | 传输二进制数据,邮件附件等 |
备注:
- DES:由于密钥长度较短(56位),已经容易受到攻击,因此不再推荐使用。
- 3DES:通过使用三倍的密钥长度来增强安全性,但在性能上不如AES。
- AES:是目前最广泛使用的对称加密算法之一,支持128、192和256位密钥长度。
- SM4:是中国国家密码管理局制定的一种分组密码算法,适用于国内应用。
- RSA:是一种非常常见的非对称加密算法,广泛用于数字签名和密钥交换。
- ECC:与RSA相比,在相同安全水平下可以使用更短的密钥,更适合移动设备。
- MD5 和 SHA-1:由于已知的碰撞攻击,不再推荐用于安全性要求高的场合。
- SHA-256:是目前较为安全的散列函数之一,适合用于密码存储和其他需要高强度散列的应用。
- Base64:是一种编码方式,而不是加密算法,主要用于将二进制数据转换为可以在文本环境中安全传输的形式。
依赖
<!--Hutool: 这是一个小而全的Java工具类库,它提供了许多便利的API,包括字符串处理、加密解密等。-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<!--Bouncy Castle: 这是一个提供加密算法支持的Java库。Hutool在处理某些加密算法(如SM系列算法)时依赖于Bouncy Castle。-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 请使用最新的版本号 -->
</dependency>
工具类
package com.lxx.encryption;
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;
/**
* @author lxx
* 日期: 2025/1/7
*/
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加密
* Base64 编码适合网络传输和存储
* @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加密
* Hex 编码适合调试和日志记录等场景。
* @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);
}
/**
* md5加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByMd5(String data) {
return SecureUtil.md5(data);
}
/**
* sha256加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySha256(String data) {
return SecureUtil.sha256(data);
}
/**
* sm3加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm3(String data) {
return SmUtil.sm3(data);
}
}
测试类
package com.lxx.encryption;
import cn.hutool.core.lang.Console;
import org.junit.Test;
import java.util.Map;
/**
* @author lxx
* 日期: 2025/1/7
*/
public class EncryptionTest {
/**
* base64加密解密测试
*/
@Test
public void test() {
String data = "1234Hello你好";
String password = "syf12345abcdefgh";
base64(data, null);
System.out.println();
aes(data, password);
System.out.println();
sm4(data,password);
System.out.println();
sm2(data);
System.out.println();
rsa(data);
System.out.println();
other(data);
}
/**
* <简述>base64加密解密测试
* <详细描述>
* @param data 待加密数据
// * @param password 密钥
*/
public static void base64(String data, String password) {
Console.log("**********加密方式{}**********", "base64");
//base64 加密
String s = EncryptUtils.encryptByBase64(data);
Console.log("加密前{},加密后:{}", data, s);
String decryptByBase64 = EncryptUtils.decryptByBase64(s);
Console.log("解密后:{}", decryptByBase64);
}
/**
* <简述>aes加密解密测试
* <详细描述>
* @param data 待加密数据
// * @param password 密钥
*/
public static void aes(String data, String password) {
Console.log("**********加密方式{}**********", "aes");
String encryptByAes = EncryptUtils.encryptByAes(data, password);
Console.log("加密前{},加密后:{}", data, encryptByAes);
String decryptByAes = EncryptUtils.decryptByAes(encryptByAes, password);
Console.log("解密后:{}", decryptByAes);
}
/**
* <简述>sm4 非对称加密
* <详细描述>
* @param data 待加密数据
// * @param password 密钥
*/
public static void sm4(String data, String password) {
Console.log("**********加密方式{}**********", "sm4");
String encryptBySm4 = EncryptUtils.encryptBySm4(data, password);
Console.log("加密前{},加密后:{}", data, encryptBySm4);
String decryptBySm4 = EncryptUtils.decryptBySm4(encryptBySm4, password);
Console.log("解密后:{}", decryptBySm4);
}
/**
* 非对称的加密,需要生成公钥和私钥
* 公钥:用于加密
* 私钥:用于解密
* @param data 待加密数据
*/
public static void sm2(String data) {
Console.log("**********加密方式{}**********", "sm2");
Map<String, String> stringStringMap = EncryptUtils.generateSm2Key();
String PUBLIC_KEY = stringStringMap.get(EncryptUtils.PUBLIC_KEY);
String PRIVATE_KEY = stringStringMap.get(EncryptUtils.PRIVATE_KEY);
Console.log("公钥:{},私钥:{}", stringStringMap.get(EncryptUtils.PUBLIC_KEY), stringStringMap.get(EncryptUtils.PRIVATE_KEY));
String encryptBySm2 = EncryptUtils.encryptBySm2(data, PUBLIC_KEY);
Console.log("加密前{},加密后:{}", data, encryptBySm2);
String decryptBySm2 = EncryptUtils.decryptBySm2(encryptBySm2, PRIVATE_KEY);
Console.log("解密后:{}", decryptBySm2);
}
/**
* 非对称的加密,需要生成公钥和私钥
* 公钥:用于加密
* 私钥:用于解密
* @param data 待加密数据
*/
public static void rsa(String data) {
Console.log("**********加密方式{}**********", "rsa");
Map<String, String> stringStringMap = EncryptUtils.generateRsaKey();
String PUBLIC_KEY = stringStringMap.get(EncryptUtils.PUBLIC_KEY);
String PRIVATE_KEY = stringStringMap.get(EncryptUtils.PRIVATE_KEY);
Console.log("公钥:{}", stringStringMap.get(EncryptUtils.PUBLIC_KEY));
Console.log("私钥:{}", stringStringMap.get(EncryptUtils.PRIVATE_KEY));
String encryptByRsa = EncryptUtils.encryptByRsa(data, PUBLIC_KEY);
Console.log("加密前{},加密后:{}", data, encryptByRsa);
String decryptByRsa = EncryptUtils.decryptByRsa(encryptByRsa, PRIVATE_KEY);
Console.log("解密后:{}", decryptByRsa);
}
/**
* <简述>其他加密算法
* <详细描述>
* @param data 待加密数据
*/
public static void other(String data){
Console.log("**********加密方式{}**********", "other");
Console.log("md5加密前:{},加密后:{}", data, EncryptUtils.encryptByMd5(data));
Console.log("sha256加密前:{},加密后:{}", data, EncryptUtils.encryptBySha256(data));
Console.log("sm3加密前:{},加密后:{}", data, EncryptUtils.encryptBySm3(data));
}
}
1.9、测试类
package cn.hncht.plug.lzda;
import cn.hncht.plug.core.exception.DefaultException;
import cn.hncht.plug.lzda.utils.AESGCMCrypto;
import cn.hutool.crypto.asymmetric.RSA;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
/**
* @author lxx
* @date 2025年11月19日 16:22
*/
@SpringBootTest
public class TestDemo {
@Test
public void test() throws Exception {
// 定义工号和待加密的JSON数据
String gh = "123456";
String json = "{\"name\":\"张三\",\"age\":18}";
// 使用AES-GCM算法进行对称加密
String encrypt = encrypt(json, gh);
System.out.println("加密后数据:" + encrypt);
// 使用AES-GCM算法进行对称解密
String decrypt = decrypt(encrypt, gh);
System.out.println("解密后数据:" + decrypt);
// 生成非对称密钥对(RSA)
RSA rsa = new RSA();
// 固定的私钥和公钥(用于演示)
String privateKeyBase64 = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAP0CFxfv7Nr5+5Ok+zj0jFIEktQlfrsh6IRmA0q8X3P77eSp02iarikDhmLOPw2wo2Zw8wnSF00Xz/JylbVBBe+4iexK6EBDWaZN12QlT8ctlPEi6w3TX2hjoOAwX5UE/++AnGNdH/DwODJhr3bVjmq4CD2+n7nDRy75L8B3XaWpAgMBAAECgYALtKStIbd5h6KTOYk6gLz7v/Srnkr7XTWg54fcNbQsXXHrmAxdDPgFE4HMkTqDV4Jt10i1UfwsP89qChFhXA9qSPiSsjyduUTyxG/vmZgkS+18DsTmh/OSO2AaGXVmRXzZRB+3US2Aj8/kSoU/TJha/3trMpk7IzZAetyrP9e9gQJBAP9HTGt28DztwX0awKRrF44dB1gmvvoYgUN69nwGDjiFYvVB73y0tYJPCHCYmMp82aPESjm1AbveSI1nBVB8eikCQQD9uSYnCIoMVo0dFgD+6knmo75qjXzy9Ldy3E2keiOX5puIaQ2nAEr4LZsrNH8CpPDN0PbtNBwy0NOrCmsMdD+BAkAs41iovO1ZiWUwnnepcbtenfQgqA+E4rI4L3MQM/BkN/BCuClogE46b7NIOU0ErheHGIkExLNdWaTgWHEyyfChAkEArjMkosAWKAp0sHefD2ZEfvoFjpq+IwEpws7G8Bp7vZszRiD6oqJs1qBZXPyaB3K/ck/6eTnB8pG2AkUQ4mgeAQJBAI/+BNqll7VO/t883SGcsnBl7kBm3ODwrc8CdpNgYawbHnJ7ZYhaACm8lfcu0CwSPz8t9lV69bh/Klo+llo39Rc=";
String publicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD9AhcX7+za+fuTpPs49IxSBJLUJX67IeiEZgNKvF9z++3kqdNomq4pA4Zizj8NsKNmcPMJ0hdNF8/ycpW1QQXvuInsSuhAQ1mmTddkJU/HLZTxIusN019oY6DgMF+VBP/vgJxjXR/w8DgyYa921Y5quAg9vp+5w0cu+S/Ad12lqQIDAQAB";
// 使用RSA-OAEP算法进行非对称加密
String encryptedData = encryptGh(json, publicKeyBase64);
System.out.println("公钥加密后数据:" + encryptedData);
// 使用RSA-OAEP算法进行非对称解密
String decryptedData = decryptGh(encryptedData, privateKeyBase64);
System.out.println("私钥解密后数据:" + decryptedData);
}
/**
* 公钥加密
*
* @param dataToEncrypt 要加密的原始数据
* @param publicKeyStr Base64编码的公钥字符串
* @return Base64编码的加密字符串
*/
public static String encryptGh(String dataToEncrypt, String publicKeyStr) {
try {
// 将Base64编码的公钥字符串转换为PublicKey对象
PublicKey publicKey = getPublicKeyFromString(publicKeyStr);
// 必须与解密时使用完全相同的算法和参数
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
// 初始化为加密模式,并传入公钥和OAEP参数
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
// 加密数据,并指定字符集为UTF-8
byte[] encryptedBytes = cipher.doFinal(dataToEncrypt.getBytes(StandardCharsets.UTF_8));
// 将加密后的字节数组进行Base64编码,以便于传输和存储
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
throw new DefaultException("加密数据时发生错误: " + e.getMessage());
}
}
/**
* 从Base64编码的字符串中获取公钥对象
*
* @param publicKeyStr Base64编码的公钥字符串 (通常是PKCS#8或X.509格式)
* @return PublicKey对象
*/
private static PublicKey getPublicKeyFromString(String publicKeyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
}
/**
* 私钥解密得到工号数据
*
* @param encryptedData Base64编码的已加密数据
* @param privateKeyStr Base64编码的私钥字符串
* @return 解密后的原始数据
*/
public static String decryptGh(String encryptedData, String privateKeyStr) {
try {
// 将Base64编码的私钥字符串转换为PrivateKey对象
PrivateKey privateKey = getPrivateKeyFromString(privateKeyStr);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes);
} catch (Exception e) {
e.printStackTrace();
throw new DefaultException("解密数据时发生错误: " + e.getMessage());
}
}
/**
* 从Base64编码的PKCS#8私钥字符串生成PrivateKey对象
*
* @param privateKeyStr Base64编码的私钥字符串
* @return PrivateKey对象
*/
private static PrivateKey getPrivateKeyFromString(String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
//==================================================================================================================//
// --- 配置常量 ---
private static final String ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";
private static final String KEY_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA256"; // 确保是这个算法
private static final int GCM_IV_LENGTH = 12; // 字节
private static final int GCM_TAG_LENGTH = 16; // 字节 (128 bits)
private static final int PBKDF2_ITERATIONS = 100000; // 需要与前端一致
private static final int PBKDF2_KEY_LENGTH = 256; // 位 (256 bits) - 需要与前端一致
// --- 密钥派生 (前端 importKey) ---
// 注意:实际应用中,PBKDF2 通常需要一个盐值 (salt)。前端代码未明确显示 salt 的处理。
// 这里假设 salt 是固定的或者通过其他方式协商。
// 在生产环境中,强烈建议为每个密码使用随机盐,并将盐与加密数据一起存储或传输。
private static final byte[] FIXED_SALT = "fixed-salt-123".getBytes(StandardCharsets.UTF_8); // 匹配前端的盐值
/**
* 根据密码派生 AES 密钥 (模拟前端 importKey)
*
* @param gh 用于加密的密码
* @return AES SecretKey
* @throws Exception 如果密钥派生失败
*/
private static SecretKey deriveKey(String gh) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_DERIVATION_ALGORITHM);
KeySpec spec = new PBEKeySpec(gh.toCharArray(), FIXED_SALT, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
// 生成最终的 AES 密钥
return new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
}
// --- 加密 ---
/**
* AES-GCM 加密工具类 对称加密----与前端保持一致<br>
* 加密 JSON 数据
*
* @param jsonData 要加密的 JSON 字符串
* @param gh 用于加密的密码
* @return Base64 编码的加密数据 (包含 IV + 密文)
* @throws Exception 如果加密失败
*/
public static String encrypt(String jsonData, String gh) throws Exception {
SecretKey key = deriveKey(gh);
// 1. 生成随机 IV
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv); // 使用安全随机数生成器
// 2. 初始化 Cipher
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); // 位长度
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
// 3. 加密数据
byte[] plainTextBytes = jsonData.getBytes(StandardCharsets.UTF_8);
byte[] cipherText = cipher.doFinal(plainTextBytes);
// 4. 合并 IV 和密文
byte[] encryptedData = new byte[iv.length + cipherText.length];
System.arraycopy(iv, 0, encryptedData, 0, iv.length);
System.arraycopy(cipherText, 0, encryptedData, iv.length, cipherText.length);
// 5. Base64 编码并返回
return Base64.getEncoder().encodeToString(encryptedData);
}
// --- 解密 ---
/**
* AES-GCM 加密工具类 对称加密----与前端保持一致<br>
* 解密 Base64 编码的加密数据
*
* @param encryptedDataBase64 Base64 编码的加密数据 (包含 IV + 密文)
* @param gh 用于解密的密码
* @return 解密后的原始 JSON 字符串
* @throws Exception 如果解密失败
*/
public static String decrypt(String encryptedDataBase64, String gh) throws Exception {
SecretKey key = deriveKey(gh);
// 1. Base64 解码
byte[] encryptedDataWithIv = Base64.getDecoder().decode(encryptedDataBase64);
// 2. 提取 IV 和密文
if (encryptedDataWithIv.length < GCM_IV_LENGTH) {
throw new IllegalArgumentException("Invalid encrypted data format: too short");
}
byte[] iv = new byte[GCM_IV_LENGTH];
byte[] cipherText = new byte[encryptedDataWithIv.length - GCM_IV_LENGTH];
System.arraycopy(encryptedDataWithIv, 0, iv, 0, iv.length);
System.arraycopy(encryptedDataWithIv, iv.length, cipherText, 0, cipherText.length);
// 3. 初始化 Cipher
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); // 位长度
cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec);
// 4. 解密数据
byte[] decryptedTextBytes = cipher.doFinal(cipherText);
// 5. 转换为字符串并返回
return new String(decryptedTextBytes, StandardCharsets.UTF_8);
}
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)