什么是加密?

加密(Encryption)是将明文(Plaintext)通过加密算法转换为密文(Ciphertext)的过程,目的是保护数据的机密性完整性真实性

1
2
明文 + 密钥 → [加密算法] → 密文
密文 + 密钥 → [解密算法] → 明文

加密的核心目标

  1. 机密性(Confidentiality): 只有授权用户才能访问数据
  2. 完整性(Integrity): 数据在传输过程中未被篡改
  3. 真实性(Authenticity): 验证数据来源的真实性
  4. 不可否认性(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());

// 将 IV 和密文拼接
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
// 需要添加 Bouncy Castle 依赖
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
任意数据 → [哈希算法] → 固定长度哈希值

特点:

  1. 单向性: 无法从哈希值反推原始数据
  2. 确定性: 相同输入总是产生相同输出
  3. 雪崩效应: 输入微小变化,输出完全不同
  4. 抗碰撞: 很难找到两个不同输入产生相同哈希值

用途:

  • 密码存储
  • 数据完整性校验
  • 数字签名
  • 区块链

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 结合了哈希算法密钥,用于验证消息的完整性真实性

1
HMAC = Hash(密钥 + 消息)

与普通哈希的区别:

  • 普通哈希: 无密钥,任何人都可以计算
  • 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 random = new Random();
byte[] iv = new byte[16];
random.nextBytes(iv);

// ✅ 正确:使用 SecureRandom
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);

4. 不要使用 ECB 模式

1
2
3
4
5
// ❌ 错误:ECB 模式(相同明文产生相同密文)
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

// ✅ 正确:使用 CBC、CTR、GCM 等模式
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

5. 密码存储必须加盐

1
2
3
4
5
// ❌ 错误:直接哈希密码
String hashedPassword = SHA256Util.hash(password);

// ✅ 正确:使用 bcrypt(自动加盐)
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》

强制使用行业:

  1. 金融系统(银行、证券、保险)
  2. 政务系统(电子政务、电子证照)
  3. 电信运营商
  4. 重要信息基础设施

国密算法的优势

  1. 自主可控: 不依赖国外技术
  2. 安全合规: 满足国家法律法规要求
  3. 性能相当: 与国际算法性能相当
  4. 完整生态: 芯片、证书、协议全面支持

国密 SSL/TLS 算法套件

1
2
3
4
5
6
7
8
9
国密 SSL/TLS 使用的算法套件:
- 加密算法: SM4
- 签名算法: SM2 + SM3
- 密钥交换: SM2

完整替代国际算法:
RSA → SM2
SHA-256 → SM3
AES → SM4

总结

核心要点

  1. 对称加密(AES/SM4): 快速,适合大量数据,密钥管理复杂
  2. 非对称加密(RSA/ECC/SM2): 安全,适合密钥交换和数字签名,速度慢
  3. 哈希算法(SHA-256/SM3): 单向,用于完整性校验和密码存储
  4. 混合加密: HTTPS 等实际应用结合对称和非对称加密

选型建议

国际通用:

  • 数据加密: AES-256-GCM
  • 密码存储: bcrypt / Argon2
  • 数字签名: RSA-2048 / ECDSA-256
  • 消息认证: HMAC-SHA256
  • 密钥交换: ECDH / RSA

国密标准(中国金融、政务、电信):

  • 数据加密: SM4-CBC/CTR
  • 哈希算法: SM3
  • 数字签名: SM2 + SM3
  • 密钥交换: SM2
  • 消息认证: HMAC-SM3

安全原则

  1. 使用成熟的加密库,不要自己实现
  2. 密钥不要硬编码,使用密钥管理系统
  3. 使用安全的随机数生成器(SecureRandom)
  4. 密码存储必须加盐(bcrypt)
  5. 定期更新加密算法和密钥长度
  6. 关注安全漏洞和算法更新