对称加密详解
对称加密详解
对称加密是一种使用相同密钥进行加密和解密的加密方法。下面我将从原理、算法、应用场景到代码实现全面介绍对称加密。
基本概念
核心特征
单一密钥:加密和解密使用同一个密钥
加解密速度快:相比非对称加密快几个数量级
适合大数据量加密:常用于文件加密、网络通信加密等场景
加密过程
明文 + 密钥 → [加密算法] → 密文
解密过程
密文 + 密钥 → [解密算法] → 明文
主要对称加密算法
1. DES (Data Encryption Standard)
密钥长度:56位(已不安全)
分组大小:64位
现状:已被破解,不推荐使用
2. 3DES (Triple DES)
原理:对每个数据块应用三次DES加密
密钥长度:112位或168位
现状:仍在使用但逐渐被淘汰
3. AES (Advanced Encryption Standard)
密钥长度:128位、192位、256位
分组大小:128位
现状:目前最常用的对称加密算法,安全高效
4. ChaCha20
流密码算法:特别适合移动设备
性能优秀:在缺乏AES硬件加速的设备上表现更好
AES加密解密完整示例
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;/*** AES对称加密工具类* 支持GCM模式(推荐,提供完整性验证)*/
public class AESEncryption {// AES密钥长度private static final int AES_KEY_SIZE = 256;// GCM认证标签长度private static final int GCM_TAG_LENGTH = 128;// GCM IV长度private static final int GCM_IV_LENGTH = 12; // 推荐12字节/*** 生成AES密钥*/public static SecretKey generateKey() throws Exception {KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(AES_KEY_SIZE);return keyGenerator.generateKey();}/*** 从Base64字符串恢复密钥*/public static SecretKey loadKey(String base64Key) {byte[] decodedKey = Base64.getDecoder().decode(base64Key);return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");}/*** AES-GCM加密(推荐使用)* GCM模式提供机密性和完整性验证*/public static String encryptGCM(String plaintext, SecretKey key) throws Exception {// 生成随机IVbyte[] iv = new byte[GCM_IV_LENGTH];SecureRandom random = new SecureRandom();random.nextBytes(iv);// 初始化加密器Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);// 执行加密byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));// 组合IV和密文: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);return Base64.getEncoder().encodeToString(encryptedData);}/*** AES-GCM解密*/public static String decryptGCM(String encryptedData, SecretKey key) throws Exception {byte[] decodedData = Base64.getDecoder().decode(encryptedData);// 分离IV和密文byte[] iv = new byte[GCM_IV_LENGTH];byte[] ciphertext = new byte[decodedData.length - GCM_IV_LENGTH];System.arraycopy(decodedData, 0, iv, 0, iv.length);System.arraycopy(decodedData, iv.length, ciphertext, 0, ciphertext.length);// 初始化解密器Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);// 执行解密byte[] plaintext = cipher.doFinal(ciphertext);return new String(plaintext, "UTF-8");}/*** AES-CBC加密(兼容性更好)*/public static String encryptCBC(String plaintext, SecretKey key) throws Exception {// 生成随机IVbyte[] iv = new byte[16]; // AES块大小是16字节SecureRandom random = new SecureRandom();random.nextBytes(iv);Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));// 组合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);return Base64.getEncoder().encodeToString(encryptedData);}/*** AES-CBC解密*/public static String decryptCBC(String encryptedData, SecretKey key) throws Exception {byte[] decodedData = Base64.getDecoder().decode(encryptedData);byte[] iv = new byte[16];byte[] ciphertext = new byte[decodedData.length - 16];System.arraycopy(decodedData, 0, iv, 0, iv.length);System.arraycopy(decodedData, iv.length, ciphertext, 0, ciphertext.length);Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));byte[] plaintext = cipher.doFinal(ciphertext);return new String(plaintext, "UTF-8");}/*** 密钥转换为Base64字符串(用于存储或传输)*/public static String keyToString(SecretKey key) {return Base64.getEncoder().encodeToString(key.getEncoded());}
}/*** 使用示例和测试类*/
class AESExample {public static void main(String[] args) {try {System.out.println("=== AES对称加密演示 ===\n");// 1. 生成密钥SecretKey key = AESEncryption.generateKey();String keyString = AESEncryption.keyToString(key);System.out.println("生成的AES密钥: " + keyString.substring(0, 32) + "...");String originalText = "这是一段需要加密的敏感数据!Secret message: Hello World!";System.out.println("原始文本: " + originalText);System.out.println("原始文本长度: " + originalText.length() + " 字符");// 2. 使用GCM模式加密(推荐)System.out.println("\n--- GCM模式加密 ---");String encryptedGCM = AESEncryption.encryptGCM(originalText, key);System.out.println("加密后: " + encryptedGCM.substring(0, 50) + "...");String decryptedGCM = AESEncryption.decryptGCM(encryptedGCM, key);System.out.println("解密后: " + decryptedGCM);System.out.println("GCM解密验证: " + originalText.equals(decryptedGCM));// 3. 使用CBC模式加密System.out.println("\n--- CBC模式加密 ---");String encryptedCBC = AESEncryption.encryptCBC(originalText, key);System.out.println("加密后: " + encryptedCBC.substring(0, 50) + "...");String decryptedCBC = AESEncryption.decryptCBC(encryptedCBC, key);System.out.println("解密后: " + decryptedCBC);System.out.println("CBC解密验证: " + originalText.equals(decryptedCBC));// 4. 从字符串恢复密钥测试System.out.println("\n--- 密钥恢复测试 ---");SecretKey restoredKey = AESEncryption.loadKey(keyString);String testEncrypted = AESEncryption.encryptGCM("测试密钥恢复", restoredKey);String testDecrypted = AESEncryption.decryptGCM(testEncrypted, restoredKey);System.out.println("密钥恢复测试: " + "测试密钥恢复".equals(testDecrypted));// 5. 性能测试System.out.println("\n--- 性能测试 ---");performanceTest(key);} catch (Exception e) {e.printStackTrace();}}private static void performanceTest(SecretKey key) throws Exception {// 生成1MB测试数据StringBuilder testData = new StringBuilder();for (int i = 0; i < 10000; i++) {testData.append("这是一段测试数据用于性能评估。");}String largeText = testData.toString();long startTime = System.currentTimeMillis();int iterations = 10;for (int i = 0; i < iterations; i++) {String encrypted = AESEncryption.encryptGCM(largeText, key);AESEncryption.decryptGCM(encrypted, key);}long endTime = System.currentTimeMillis();double avgTime = (endTime - startTime) / (double) iterations;System.out.printf("加密解密 %d KB 数据平均耗时: %.2f 毫秒%n", largeText.length() / 1024, avgTime);System.out.printf("吞吐量: %.2f MB/秒%n", (largeText.length() / (1024.0 * 1024)) / (avgTime / 1000.0));}
}/*** 文件加密工具类*/
class FileEncryptionTool {private static final int BUFFER_SIZE = 8192;/*** 加密文件*/public static void encryptFile(String inputFile, String outputFile, SecretKey key) throws Exception {try (java.io.FileInputStream fis = new java.io.FileInputStream(inputFile);java.io.FileOutputStream fos = new java.io.FileOutputStream(outputFile)) {// 生成随机IVbyte[] iv = new byte[16];SecureRandom random = new SecureRandom();random.nextBytes(iv);// 写入IV到文件开头fos.write(iv);Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));try (javax.crypto.CipherOutputStream cos = new javax.crypto.CipherOutputStream(fos, cipher)) {byte[] buffer = new byte[BUFFER_SIZE];int bytesRead;while ((bytesRead = fis.read(buffer)) != -1) {cos.write(buffer, 0, bytesRead);}}}}/*** 解密文件*/public static void decryptFile(String inputFile, String outputFile, SecretKey key) throws Exception {try (java.io.FileInputStream fis = new java.io.FileInputStream(inputFile);java.io.FileOutputStream fos = new java.io.FileOutputStream(outputFile)) {// 读取IVbyte[] iv = new byte[16];if (fis.read(iv) != iv.length) {throw new IllegalStateException("文件已损坏或格式不正确");}Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));try (javax.crypto.CipherOutputStream cos = new javax.crypto.CipherOutputStream(fos, cipher)) {byte[] buffer = new byte[BUFFER_SIZE];int bytesRead;while ((bytesRead = fis.read(buffer)) != -1) {cos.write(buffer, 0, bytesRead);}}}}
}
对称加密的工作模式
1. ECB (Electronic Codebook) - 不推荐
每个块独立加密
相同明文块产生相同密文块
安全性差,会暴露数据模式
2. CBC (Cipher Block Chaining) - 常用
每个明文块与前一个密文块进行异或操作
需要初始化向量(IV)
提供更好的安全性
3. GCM (Galois/Counter Mode) - 推荐
提供机密性和完整性验证
并行计算,性能好
现代应用的首选模式
安全最佳实践
1. 密钥管理
// 安全的密钥生成
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, SecureRandom.getInstanceStrong()); // 使用强随机数
2. IV使用原则
不要重复使用IV:每次加密使用随机IV
IV不需要保密:但必须不可预测
GCM模式IV:推荐12字节长度
3. 认证加密
优先选择提供认证的加密模式(如GCM),避免仅使用加密模式(如CBC)而不进行完整性验证。
对称加密 vs 非对称加密
特性 | 对称加密 | 非对称加密 |
---|---|---|
密钥数量 | 1个共享密钥 | 公钥+私钥对 |
速度 | 快(适合大数据量) | 慢(比对称加密慢100-1000倍) |
主要用途 | 数据加密 | 密钥交换、数字签名 |
典型算法 | AES, ChaCha20 | RSA, ECC |
实际应用场景
HTTPS通信:使用对称加密加密实际数据传输
文件加密:加密存储敏感文件
数据库字段加密:保护数据库中的敏感数据
消息加密:即时通讯应用的消息加密
对称加密是现代密码学的基石,正确使用对称加密可以有效地保护数据的机密性。在实际应用中,通常会将对称加密与非对称加密结合使用,发挥各自优势。