什么是加密?
加密(Encryption)是将明文(Plaintext)通过加密算法转换为密文(Ciphertext)的过程,目的是保护数据的机密性、完整性和真实性。
1 2
| 明文 + 密钥 → [加密算法] → 密文 密文 + 密钥 → [解密算法] → 明文
|
加密的核心目标
- 机密性(Confidentiality): 只有授权用户才能访问数据
- 完整性(Integrity): 数据在传输过程中未被篡改
- 真实性(Authenticity): 验证数据来源的真实性
- 不可否认性(Non-repudiation): 发送方不能否认发送过该数据
加密算法分类
加密算法主要分为三大类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 加密算法 ├── 对称加密(Symmetric Encryption) │ ├── DES │ ├── 3DES │ ├── AES (推荐) │ ├── SM4 (国密) │ └── ChaCha20 │ ├── 非对称加密(Asymmetric Encryption) │ ├── RSA │ ├── ECC (椭圆曲线加密) │ ├── SM2 (国密) │ ├── DSA │ └── Diffie-Hellman │ └── 哈希算法(Hash Algorithm) ├── MD5 (不推荐) ├── SHA-1 (不推荐) ├── SHA-256 (推荐) ├── SHA-512 ├── SM3 (国密) └── bcrypt / scrypt / Argon2
|
对称加密算法
对称加密使用同一个密钥进行加密和解密,速度快,适合大量数据加密,但无法在不安全的网络中安全地交换密钥。
1 2
| 加密: 明文 + 密钥 → 密文 解密: 密文 + 密钥 → 明文
|
特点:
- 速度快(比非对称加密快100-1000倍)
- 密钥管理复杂(需要安全传输密钥,无法在不安全的网络中安全地交换密钥)
- 适合大量数据加密
1. AES(Advanced Encryption Standard)
AES 是目前最安全、应用最广泛的对称加密算法,是 DES 的替代者。
特点:
- 密钥长度: 128、192、256 位
- 分组长度: 128 位
- 安全性: 非常高,目前无法破解
- 速度: 快
工作模式:
- ECB(Electronic Codebook): 最简单,不安全,不推荐
- CBC(Cipher Block Chaining): 常用,需要 IV(初始化向量)
- CTR(Counter): 支持并行,性能好
- GCM(Galois/Counter Mode): 推荐,提供加密+认证
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import java.security.SecureRandom; import java.util.Base64;
public class AESUtil { private static final String TRANSFORMATION = "AES/GCM/NoPadding"; private static final int KEY_SIZE = 256; private static final int GCM_TAG_LENGTH = 128; private static final int GCM_IV_LENGTH = 12;
public static SecretKey generateKey() throws Exception { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(KEY_SIZE, new SecureRandom()); return keyGen.generateKey(); }
public static String encrypt(String plaintext, SecretKey key) throws Exception { byte[] iv = new byte[GCM_IV_LENGTH]; new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH, iv)); byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
byte[] combined = new byte[iv.length + ciphertext.length]; System.arraycopy(iv, 0, combined, 0, iv.length); System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(combined); }
public static String decrypt(String encryptedData, SecretKey key) throws Exception { byte[] decoded = Base64.getDecoder().decode(encryptedData);
byte[] iv = new byte[GCM_IV_LENGTH]; byte[] ciphertext = new byte[decoded.length - GCM_IV_LENGTH]; System.arraycopy(decoded, 0, iv, 0, iv.length); System.arraycopy(decoded, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
return new String(cipher.doFinal(ciphertext)); } }
|
2. SM4(国密算法)
SM4 是中国国家密码管理局发布的商用密码算法,是中国金融、政务等领域的强制标准。
特点:
- 密钥长度: 128 位
- 分组长度: 128 位
- 安全性: 与 AES-128 相当
- 应用: 中国金融、政务、电信等行业
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| 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.Security; import java.util.Base64;
public class SM4Util { static { Security.addProvider(new BouncyCastleProvider()); }
public static SecretKey generateKey() throws Exception { KeyGenerator keyGen = KeyGenerator.getInstance("SM4", "BC"); keyGen.init(128); return keyGen.generateKey(); }
public static String encrypt(String plaintext, SecretKey key) throws Exception { byte[] iv = new byte[16]; new java.security.SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC"); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
byte[] combined = new byte[iv.length + ciphertext.length]; System.arraycopy(iv, 0, combined, 0, iv.length); System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(combined); }
public static String decrypt(String encryptedData, SecretKey key) throws Exception { byte[] decoded = Base64.getDecoder().decode(encryptedData);
byte[] iv = new byte[16]; byte[] ciphertext = new byte[decoded.length - 16]; System.arraycopy(decoded, 0, iv, 0, iv.length); System.arraycopy(decoded, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS5Padding", "BC"); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new String(cipher.doFinal(ciphertext)); } }
|
SM4 与 AES 对比
| 特性 |
SM4 |
AES |
| 密钥长度 |
128 位 |
128、192、256 位 |
| 安全性 |
与 AES-128 相当 |
非常高 |
| 性能 |
略慢于 AES |
快(有硬件加速) |
| 应用场景 |
中国金融、政务 |
国际通用 |
| 法律要求 |
国内强制 |
无强制要求 |
选择建议:
- 国内金融、政务、电信等行业: 必须使用 SM4
- 国际业务或通用场景: 推荐使用 AES
非对称加密算法
非对称加密使用一对密钥(公钥和私钥),公钥加密,私钥解密,或私钥签名,公钥验证。
1 2 3 4 5
| 加密: 明文 + 公钥 → 密文 解密: 密文 + 私钥 → 明文
签名: 数据 + 私钥 → 签名 验证: 数据 + 签名 + 公钥 → 验证结果
|
特点:
- 无需提前共享密钥
- 速度慢(比对称加密慢100-1000倍)
- 适合小量数据加密、数字签名、密钥交换
1. RSA
RSA 是应用最广泛的非对称加密算法。
特点:
- 密钥长度: 1024、2048、3072、4096 位
- 安全性: 2048 位及以上被认为是安全的
- 用途: 数据加密、数字签名、密钥交换
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import javax.crypto.Cipher; import java.security.*; import java.util.Base64;
public class RSAUtil { private static final int KEY_SIZE = 2048;
public static KeyPair generateKeyPair() throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(KEY_SIZE); return keyGen.generateKeyPair(); }
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes())); }
public static String decrypt(String ciphertext, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext))); }
public static String sign(String data, PrivateKey privateKey) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(data.getBytes()); return Base64.getEncoder().encodeToString(signature.sign()); }
public static boolean verify(String data, String signatureStr, PublicKey publicKey) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(data.getBytes()); return signature.verify(Base64.getDecoder().decode(signatureStr)); } }
|
应用场景:
- HTTPS/TLS: RSA 用于密钥交换,AES 用于数据加密
- 数字签名: JWT、代码签名、文档签名
- 密钥交换: 交换对称密钥
2. ECC(椭圆曲线加密)
ECC 是一种基于椭圆曲线数学的非对称加密算法。
特点:
- 密钥更短: 256 位 ECC ≈ 3072 位 RSA 安全性
- 速度更快: 签名和验证速度更快
- 占用空间小: 适合移动设备、IoT
- 广泛应用: Bitcoin、Ethereum、TLS 1.3
哈希算法
哈希算法(散列算法)将任意长度的数据转换为固定长度的哈希值(摘要)。
特点:
- 单向性: 无法从哈希值反推原始数据
- 确定性: 相同输入总是产生相同输出
- 雪崩效应: 输入微小变化,输出完全不同
- 抗碰撞: 很难找到两个不同输入产生相同哈希值
用途:
1. SHA-256(推荐)
SHA-256 是 SHA-2 家族的一员,产生 256 位(32 字节)哈希值,目前被认为是安全的。
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.security.MessageDigest; import java.util.HexFormat;
public class SHA256Util { public static String hash(String input) throws Exception { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(input.getBytes()); return HexFormat.of().formatHex(hash); }
public static boolean verifyIntegrity(String data, String expectedHash) throws Exception { return hash(data).equals(expectedHash); } }
|
应用场景:
- 文件完整性校验: 下载文件后验证哈希值
- Git 对象存储: 每个文件和 commit 都有唯一的 SHA 哈希
- 区块链: 比特币使用 SHA-256
- 数字签名: 配合 RSA/ECDSA 使用
2. SM3(国密哈希算法)
SM3 是中国国家密码管理局发布的商用密码哈希算法。
特点:
- 输出长度: 256 位(32 字节)
- 结构: 类似 SHA-256
- 安全性: 与 SHA-256 相当
- 应用: 中国金融、政务、数字签名等领域
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import org.bouncycastle.crypto.digests.SM3Digest; import java.util.HexFormat;
public class SM3Util { public static String hash(String input) { byte[] inputBytes = input.getBytes(); SM3Digest digest = new SM3Digest(); digest.update(inputBytes, 0, inputBytes.length);
byte[] hash = new byte[digest.getDigestSize()]; digest.doFinal(hash, 0);
return HexFormat.of().formatHex(hash); }
public static boolean verifyIntegrity(String data, String expectedHash) { return hash(data).equals(expectedHash); } }
|
SM3 与 SHA-256 对比
| 特性 |
SM3 |
SHA-256 |
| 输出长度 |
256 位 |
256 位 |
| 性能 |
与 SHA-256 相当 |
快 |
| 安全性 |
与 SHA-256 相当 |
非常高 |
| 应用场景 |
中国金融、政务 |
国际通用 |
| 法律要求 |
国内强制 |
无强制要求 |
选择建议:
- 国内金融、政务系统: 必须使用 SM3
- 国际业务或通用场景: 推荐使用 SHA-256
- 数字证书签名(国内): SM2 + SM3
3. 密码哈希算法:bcrypt
普通哈希算法(如 SHA-256)计算速度太快,不适合存储密码。密码哈希算法通过加盐(Salt)和慢哈希防止暴力破解。
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public static String hashPassword(String password) { return encoder.encode(password); }
public static boolean verifyPassword(String password, String hashedPassword) { return encoder.matches(password, hashedPassword); } }
|
HMAC(基于哈希的消息认证码)
HMAC 结合了哈希算法和密钥,用于验证消息的完整性和真实性。
与普通哈希的区别:
- 普通哈希: 无密钥,任何人都可以计算
- HMAC: 有密钥,只有拥有密钥的人才能生成和验证
核心实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.HexFormat;
public class HMACUtil { public static String hmac(String data, String key) throws Exception { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); return HexFormat.of().formatHex(mac.doFinal(data.getBytes())); }
public static boolean verify(String data, String hmacValue, String key) throws Exception { return hmac(data, key).equals(hmacValue); } }
|
应用场景:
- API 签名验证: 防止请求被篡改
- JWT: 使用 HMAC-SHA256 签名
- Webhook 验证: GitHub、支付宝等使用 HMAC 验证回调
混合加密方案(最佳实践)
实际应用中,通常结合对称加密和非对称加密,兼顾安全性和性能。
1 2 3 4 5 6 7 8 9
| 混合加密流程: 1. 生成随机对称密钥(AES) 2. 用对称密钥加密数据(快速) 3. 用接收方公钥加密对称密钥(安全) 4. 发送:加密后的数据 + 加密后的密钥
解密流程: 1. 用私钥解密对称密钥 2. 用对称密钥解密数据
|
典型应用:
- HTTPS/TLS: 握手阶段使用 RSA/ECDH 交换密钥,数据传输使用 AES-GCM
- PGP/GPG: 文件加密使用 AES,密钥加密使用 RSA
- VPN: 密钥交换使用非对称加密,数据传输使用对称加密
加密算法选型指南
1. 对称加密选择
| 场景 |
推荐算法 |
理由 |
| 通用场景 |
AES-256-GCM |
安全、快速、提供认证 |
| 移动/IoT |
ChaCha20-Poly1305 |
在无硬件加速时性能好 |
| 数据库加密 |
AES-256-CBC |
广泛支持,稳定可靠 |
| 国内金融/政务 |
SM4-CBC/CTR |
国密强制标准 |
| 国密 + 国际 |
SM4 + AES |
双算法兼容 |
不推荐: DES、3DES、RC4
2. 非对称加密选择
| 场景 |
推荐算法 |
密钥长度 |
| 数字签名 |
RSA |
2048/3072/4096 位 |
| 密钥交换 |
ECDH(椭圆曲线 DH) |
256 位 |
| TLS/SSL |
RSA 2048+ 或 ECC P-256 |
- |
| 区块链 |
ECDSA |
256 位 |
| 移动应用 |
ECC |
256 位(更小更快) |
| 国内金融/政务 |
SM2 |
256 位 |
| 国密 SSL |
SM2 + SM3 |
256 位 |
3. 哈希算法选择
| 场景 |
推荐算法 |
理由 |
| 密码存储 |
bcrypt / Argon2 |
慢哈希,防暴力破解 |
| 数据完整性 |
SHA-256 / SHA-512 |
安全,广泛支持 |
| 数字签名 |
SHA-256 |
配合 RSA/ECDSA |
| 区块链 |
SHA-256 |
比特币标准 |
| HMAC |
HMAC-SHA256 |
消息认证 |
| 国内金融/政务 |
SM3 |
国密强制标准 |
| 国密数字签名 |
SM3 |
配合 SM2 使用 |
不推荐: MD5、SHA-1(安全场景)
4. 密钥长度建议
| 算法 |
最低长度 |
推荐长度 |
说明 |
| AES |
128 位 |
256 位 |
256 位提供更高安全性 |
| SM4 |
128 位 |
128 位 |
固定 128 位 |
| RSA |
2048 位 |
3072 位 |
2048 位可用到 2030 年 |
| ECC |
224 位 |
256 位 |
256 位 ECC ≈ 3072 位 RSA |
| SM2 |
256 位 |
256 位 |
固定 256 位 |
常见安全问题与最佳实践
1. 永远不要自己实现加密算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class MyEncryption { public static String encrypt(String data) { return data.chars() .map(c -> c + 123) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString(); } }
import javax.crypto.Cipher;
public class SecureEncryption { public static String encrypt(String data, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); return null; } }
|
2. 不要在代码中硬编码密钥
1 2 3 4 5 6 7 8 9 10 11
| public class BadExample { private static final String SECRET_KEY = "MySecretKey123"; }
@Configuration public class GoodExample { @Value("${encryption.secret-key}") private String secretKey; }
|
3. 使用安全的随机数生成器
1 2 3 4 5 6 7 8 9
| Random random = new Random(); byte[] iv = new byte[16]; random.nextBytes(iv);
SecureRandom secureRandom = new SecureRandom(); byte[] iv = new byte[16]; secureRandom.nextBytes(iv);
|
4. 不要使用 ECB 模式
1 2 3 4 5
| Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
5. 密码存储必须加盐
1 2 3 4 5
| String hashedPassword = SHA256Util.hash(password);
String hashedPassword = passwordEncoder.encode(password);
|
国密算法总结
中国国密算法体系
中国密码管理局制定了一套完整的商用密码算法体系,称为”国密算法”:
| 算法 |
类型 |
替代算法 |
应用领域 |
| SM1 |
对称加密 |
DES/3DES |
芯片级应用(不公开) |
| SM2 |
非对称加密 |
RSA/ECC |
数字签名、密钥交换 |
| SM3 |
哈希算法 |
MD5/SHA-1 |
数据完整性、数字签名 |
| SM4 |
对称加密 |
AES |
数据加密 |
| SM7 |
对称加密 |
AES |
射频识别(RFID) |
| SM9 |
非对称加密 |
- |
基于身份的密码(IBE) |
法律依据:
- 《中华人民共和国密码法》(2020年1月1日施行)
- 《商用密码管理条例》
- 《金融行业标准 JR/T 0025-2012》
强制使用行业:
- 金融系统(银行、证券、保险)
- 政务系统(电子政务、电子证照)
- 电信运营商
- 重要信息基础设施
国密算法的优势
- 自主可控: 不依赖国外技术
- 安全合规: 满足国家法律法规要求
- 性能相当: 与国际算法性能相当
- 完整生态: 芯片、证书、协议全面支持
国密 SSL/TLS 算法套件
1 2 3 4 5 6 7 8 9
| 国密 SSL/TLS 使用的算法套件: - 加密算法: SM4 - 签名算法: SM2 + SM3 - 密钥交换: SM2
完整替代国际算法: RSA → SM2 SHA-256 → SM3 AES → SM4
|
总结
核心要点
- 对称加密(AES/SM4): 快速,适合大量数据,密钥管理复杂
- 非对称加密(RSA/ECC/SM2): 安全,适合密钥交换和数字签名,速度慢
- 哈希算法(SHA-256/SM3): 单向,用于完整性校验和密码存储
- 混合加密: HTTPS 等实际应用结合对称和非对称加密
选型建议
国际通用:
- 数据加密: AES-256-GCM
- 密码存储: bcrypt / Argon2
- 数字签名: RSA-2048 / ECDSA-256
- 消息认证: HMAC-SHA256
- 密钥交换: ECDH / RSA
国密标准(中国金融、政务、电信):
- 数据加密: SM4-CBC/CTR
- 哈希算法: SM3
- 数字签名: SM2 + SM3
- 密钥交换: SM2
- 消息认证: HMAC-SM3
安全原则
- 使用成熟的加密库,不要自己实现
- 密钥不要硬编码,使用密钥管理系统
- 使用安全的随机数生成器(SecureRandom)
- 密码存储必须加盐(bcrypt)
- 定期更新加密算法和密钥长度
- 关注安全漏洞和算法更新