密码学完全指南:从基础到实战
目录
- 密码学发展历程
- 密码学基础概念
- 对称加密:AES
- 非对称加密:RSA
- 椭圆曲线密码学:ECC
- [哈希算法:SHA 家族](#哈希算法:SHA 家族)
- 传输层安全:TLS
- 公钥基础设施:PKI
- 数字签名
- 混合加密体系
- 后量子密码学
- 密码学安全最佳实践
- 算法对比速查表
- 参考资源
1. 密码学发展历程
1.1 古典密码学时代(公元前 ~ 19世纪)
| 时期 | 事件 | 说明 |
|---|---|---|
| 约公元前1900年 | 古埃及象形文字替换 | 已知最早的密码术实践 |
| 公元前5世纪 | 斯巴达密码棒(Scytale) | 最早的转置密码工具 |
| 公元前50年 | 凯撒密码 | 字母表偏移替换,Julius Caesar 用于军事通信 |
| 9世纪 | Al-Kindi 的频率分析 | 阿拉伯学者提出破解替换密码的系统方法 |
| 15世纪 | Vigenère 密码 | 多表替换密码,被誉为"不可破译的密码"(后被 Kasiski 破解) |
| 1883年 | Kerckhoffs 原则 | "密码系统的安全性应仅依赖于密钥的保密性" |
1.2 机电密码时代(20世纪前半叶)
| 时期 | 事件 | 说明 |
|---|---|---|
| 1918年 | Enigma 机器发明 | 德国 Arthur Scherbius 发明转子密码机 |
| 1932-1939年 | 波兰数学家破解 Enigma | Rejewski、Różycki 和 Zygalski 的开创性工作 |
| 1939-1945年 | 布莱切利庄园 | Alan Turing 等人利用 Bombe 机器系统性破解 Enigma |
| 1945年 | Shannon 发表《保密系统的通信理论》 | 奠定现代密码学的数学基础,引入信息论 |
1.3 现代密码学时代(1970年代至今)
| 年份 | 里程碑 | 说明 |
|---|---|---|
| 1973 | IBM 提出 Lucifer 算法 | 对称分组密码的先驱 |
| 1976 | DES(数据加密标准)发布 | 首个被广泛采用的标准化加密算法,密钥长度 56 位 |
| 1976 | Diffie-Hellman 密钥交换 | 首次提出公钥密码学概念,Whitfield Diffie 和 Martin Hellman |
| 1977 | RSA 算法发明 | Rivest、Shamir、Adleman 提出基于大数因式分解的非对称加密 |
| 1985 | 椭圆曲线密码学(ECC) | Neal Koblitz 和 Victor Miller 独立提出 |
| 1991 | PGP(Pretty Good Privacy) | Phil Zimmermann 发布,使加密走向大众 |
| 1994 | SSL 1.0/2.0 | Netscape 开发,用于保护 Web 通信 |
| 1995 | SHA-1 发布 | NIST 标准化的安全哈希算法(现已不推荐使用) |
| 1999 | TLS 1.0(RFC 2246) | 替代 SSL,成为互联网安全通信标准 |
| 2001 | AES 标准发布 | Rijndael 算法胜出 NIST 竞赛,取代 DES |
| 2001 | SHA-2 家族发布 | 包含 SHA-224/256/384/512 |
| 2006 | TLS 1.1 发布 | 增加对 CBC 攻击的防护 |
| 2008 | TLS 1.2(RFC 5246) | 支持 AEAD 密码套件和可协商的哈希/签名算法 |
| 2012 | SHA-3(Keccak)标准化 | 基于海绵结构的全新哈希算法家族 |
| 2015 | Let's Encrypt 成立 | 免费自动化证书颁发,推动 HTTPS 普及 |
| 2018 | TLS 1.3(RFC 8446) | 简化握手、移除不安全算法、强制前向保密 |
| 2022 | NIST 后量子密码标准候选 | CRYSTALS-Kyber、CRYSTALS-Dilithium、FALCON、SPHINCS+ |
| 2024 | NIST 正式发布 PQC 标准 | FIPS 203 (ML-KEM)、FIPS 204 (ML-DSA)、FIPS 205 (SLH-DSA) |
2. 密码学基础概念
2.1 三大支柱
- 机密性(Confidentiality):确保数据仅对授权方可见,通过加密实现
- 完整性(Integrity):确保数据未被篡改,通过哈希/MAC 实现
- 认证性(Authentication):确认通信方的身份,通过数字签名/证书实现
附加目标还包括不可否认性(Non-repudiation)------发送方无法否认其发送的消息。
2.2 对称加密 vs 非对称加密
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥数量 | 1 个共享密钥 | 公钥 + 私钥 对 |
| 速度 | 快(适合大数据) | 慢(适合小数据/密钥交换) |
| 密钥分发 | 困难(需安全通道) | 简便(公钥可公开) |
| 代表算法 | AES、ChaCha20、3DES | RSA、ECC、ElGamal |
| 典型用途 | 数据加密、磁盘加密 | 密钥交换、数字签名、身份认证 |
2.3 实际系统中的混合模式
现代安全系统几乎都采用混合加密模式:非对称加密(RSA 或 ECC)用于安全交换会话密钥,对称加密(AES)用于高速加密实际数据。TLS、HTTPS、VPN 等协议均采用此方式。
3. 对称加密:AES
3.1 概述
AES(Advanced Encryption Standard,高级加密标准)由比利时密码学家 Joan Daemen 和 Vincent Rijmen 设计(原名 Rijndael),2001 年被 NIST 选定为联邦信息处理标准(FIPS 197),替代老旧的 DES。
核心参数:
| 参数 | 值 |
|---|---|
| 分组大小 | 128 位(16 字节) |
| 密钥长度 | 128 / 192 / 256 位 |
| 加密轮数 | 10 / 12 / 14 轮(对应不同密钥长度) |
| 结构 | 替代-置换网络(SPN) |
3.2 AES 工作原理
每一轮包含四个操作:
- SubBytes(字节替换):通过 S-Box 非线性替换每个字节
- ShiftRows(行移位):对状态矩阵的每行循环左移不同偏移量
- MixColumns(列混合):对每列进行矩阵乘法变换(最后一轮省略)
- AddRoundKey(轮密钥加):将当前状态与该轮子密钥进行 XOR
3.3 AES 工作模式
| 模式 | 全称 | 特点 | 推荐度 |
|---|---|---|---|
| ECB | Electronic Codebook | 相同明文产生相同密文,不安全 | ❌ 不推荐 |
| CBC | Cipher Block Chaining | 需要 IV,支持并行解密 | ⚠️ 可用但有 Padding Oracle 风险 |
| CTR | Counter | 将分组密码变为流密码,支持并行 | ✅ 推荐 |
| GCM | Galois/Counter Mode | CTR + GMAC 认证,AEAD 模式 | ✅✅ 强烈推荐 |
| CCM | Counter with CBC-MAC | 类似 GCM,用于资源受限环境 | ✅ 推荐 |
最佳实践:优先使用 AES-256-GCM,它同时提供加密和完整性认证(AEAD)。
3.4 Java 实现:AES-256-GCM
java
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;
public class AesGcmExample {
private static final int GCM_IV_LENGTH = 12; // 推荐 12 字节
private static final int GCM_TAG_LENGTH = 128; // 128 位认证标签
// 生成 AES-256 密钥
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
return keyGen.generateKey();
}
// 加密
public static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] iv,
byte[] aad) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
if (aad != null) {
cipher.updateAAD(aad); // 附加认证数据(明文传输但受完整性保护)
}
return cipher.doFinal(plaintext);
}
// 解密
public static byte[] decrypt(byte[] ciphertext, SecretKey key, byte[] iv,
byte[] aad) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
if (aad != null) {
cipher.updateAAD(aad);
}
return cipher.doFinal(ciphertext);
}
public static void main(String[] args) throws Exception {
// 1. 生成密钥
SecretKey key = generateKey();
System.out.println("AES Key (Base64): "
+ Base64.getEncoder().encodeToString(key.getEncoded()));
// 2. 生成随机 IV(每次加密必须不同!)
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv);
// 3. 附加认证数据(可选,用于验证上下文信息)
byte[] aad = "metadata:user=alice,action=transfer".getBytes();
// 4. 加密
String message = "这是一条需要加密的敏感消息。Hello, Cryptography!";
byte[] ciphertext = encrypt(message.getBytes("UTF-8"), key, iv, aad);
System.out.println("密文 (Base64): "
+ Base64.getEncoder().encodeToString(ciphertext));
// 5. 解密
byte[] decrypted = decrypt(ciphertext, key, iv, aad);
System.out.println("解密结果: " + new String(decrypted, "UTF-8"));
}
}
3.5 Python 实现:AES-256-GCM
python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
import base64
def aes_gcm_demo():
# 1. 生成 256 位密钥
key = AESGCM.generate_key(bit_length=256)
print(f"AES Key (Base64): {base64.b64encode(key).decode()}")
# 2. 创建 AESGCM 实例
aesgcm = AESGCM(key)
# 3. 生成随机 nonce(12 字节,每次加密必须唯一)
nonce = os.urandom(12)
# 4. 附加认证数据(可选)
aad = b"metadata:user=alice,action=transfer"
# 5. 加密
message = "这是一条需要加密的敏感消息。Hello, Cryptography!"
plaintext = message.encode("utf-8")
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
print(f"密文 (Base64): {base64.b64encode(ciphertext).decode()}")
# 6. 解密
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print(f"解密结果: {decrypted.decode('utf-8')}")
# 7. 篡改演示:修改密文后解密将失败
try:
tampered = bytearray(ciphertext)
tampered[0] ^= 0xFF
aesgcm.decrypt(nonce, bytes(tampered), aad)
except Exception as e:
print(f"篡改检测成功!错误: {e}")
if __name__ == "__main__":
aes_gcm_demo()
3.6 基于密码的密钥派生(PBKDF2 + AES)
在实际应用中,用户提供的是密码而非密钥,需要通过密钥派生函数将密码转换为加密密钥。
Python 示例:
python
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os, base64
def password_based_encryption(password: str, plaintext: str):
# 从密码派生密钥
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32, # 256 位密钥
salt=salt,
iterations=600_000, # OWASP 推荐 >= 600,000 次迭代
)
key = kdf.derive(password.encode())
# AES-GCM 加密
nonce = os.urandom(12)
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)
# 存储时需保存 salt + nonce + ciphertext
stored = salt + nonce + ciphertext
print(f"加密数据 (Base64): {base64.b64encode(stored).decode()}")
return stored
def password_based_decryption(password: str, stored: bytes):
# 解析各部分
salt = stored[:16]
nonce = stored[16:28]
ciphertext = stored[28:]
# 重新派生密钥
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(), length=32,
salt=salt, iterations=600_000,
)
key = kdf.derive(password.encode())
# 解密
aesgcm = AESGCM(key)
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
return plaintext.decode()
# 使用示例
stored = password_based_encryption("MyStr0ngP@ssw0rd!", "机密文件内容")
result = password_based_decryption("MyStr0ngP@ssw0rd!", stored)
print(f"解密结果: {result}")
4. 非对称加密:RSA
4.1 概述
RSA(Rivest-Shamir-Adleman)于 1977 年由 MIT 的三位科学家提出,是第一个实用的公钥加密算法。其安全性基于大整数因式分解问题的计算困难性。
核心参数:
| 参数 | 推荐值 |
|---|---|
| 密钥长度 | ≥ 2048 位(推荐 3072 或 4096 位) |
| 填充方案 | OAEP(加密)/ PSS(签名) |
| 哈希算法 | SHA-256 或 SHA-384 |
4.2 RSA 数学原理
- 选择两个大素数 p 和 q,计算 n = p × q
- 计算欧拉函数 φ(n) = (p-1)(q-1)
- 选择公钥指数 e(常用 65537),满足 gcd(e, φ(n)) = 1
- 计算私钥指数 d,满足 e × d ≡ 1 (mod φ(n))
- 公钥 = (n, e),私钥 = (n, d)
- 加密:C = M^e mod n;解密:M = C^d mod n
4.3 Java 实现:RSA 加密/解密 + 数字签名
java
import java.security.*;
import java.security.spec.*;
import javax.crypto.Cipher;
import java.util.Base64;
public class RsaExample {
// ========== RSA 密钥对生成 ==========
public static KeyPair generateKeyPair(int keySize) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(keySize, new SecureRandom());
return kpg.generateKeyPair();
}
// ========== RSA-OAEP 加密 ==========
public static byte[] encrypt(byte[] plaintext, PublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plaintext);
}
// ========== RSA-OAEP 解密 ==========
public static byte[] decrypt(byte[] ciphertext, PrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(ciphertext);
}
// ========== RSA-PSS 签名 ==========
public static byte[] sign(byte[] data, PrivateKey privateKey)
throws Exception {
Signature sig = Signature.getInstance("RSASSA-PSS");
PSSParameterSpec pssSpec = new PSSParameterSpec(
"SHA-256", "MGF1",
MGF1ParameterSpec.SHA256, 32, 1
);
sig.setParameter(pssSpec);
sig.initSign(privateKey);
sig.update(data);
return sig.sign();
}
// ========== RSA-PSS 验签 ==========
public static boolean verify(byte[] data, byte[] signature,
PublicKey publicKey) throws Exception {
Signature sig = Signature.getInstance("RSASSA-PSS");
PSSParameterSpec pssSpec = new PSSParameterSpec(
"SHA-256", "MGF1",
MGF1ParameterSpec.SHA256, 32, 1
);
sig.setParameter(pssSpec);
sig.initVerify(publicKey);
sig.update(data);
return sig.verify(signature);
}
public static void main(String[] args) throws Exception {
// 生成 3072 位密钥对
KeyPair kp = generateKeyPair(3072);
System.out.println("RSA 密钥对已生成 (3072 位)");
// --- 加密/解密演示 ---
String message = "RSA 加密消息:转账 100 万到账户 A";
byte[] ciphertext = encrypt(message.getBytes("UTF-8"), kp.getPublic());
System.out.println("密文 (Base64): "
+ Base64.getEncoder().encodeToString(ciphertext));
byte[] decrypted = decrypt(ciphertext, kp.getPrivate());
System.out.println("解密: " + new String(decrypted, "UTF-8"));
// --- 签名/验签演示 ---
byte[] data = "这是需要签名的合同文件内容".getBytes("UTF-8");
byte[] signature = sign(data, kp.getPrivate());
System.out.println("签名 (Base64): "
+ Base64.getEncoder().encodeToString(signature));
boolean valid = verify(data, signature, kp.getPublic());
System.out.println("签名验证结果: " + valid);
// 篡改数据后验签
byte[] tampered = "这是被篡改的合同文件内容".getBytes("UTF-8");
boolean tamperedValid = verify(tampered, signature, kp.getPublic());
System.out.println("篡改数据验证结果: " + tamperedValid);
}
}
4.4 Python 实现:RSA 完整示例
python
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
import base64
def rsa_demo():
# 1. 生成密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=3072,
)
public_key = private_key.public_key()
# 2. 导出 PEM 格式密钥(用于存储/传输)
pem_private = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(b"my-password"),
)
pem_public = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
print("公钥 PEM:")
print(pem_public.decode())
# 3. RSA-OAEP 加密
message = "RSA 加密消息:转账 100 万到账户 A"
ciphertext = public_key.encrypt(
message.encode(),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print(f"密文 (Base64): {base64.b64encode(ciphertext).decode()}")
# 4. 解密
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print(f"解密结果: {plaintext.decode()}")
# 5. RSA-PSS 数字签名
data = b"contract: payment of $1,000,000 to Account A"
signature = private_key.sign(
data,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
print(f"签名 (Base64): {base64.b64encode(signature).decode()}")
# 6. 验签
try:
public_key.verify(
signature, data,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
print("✅ 签名验证通过")
except Exception:
print("❌ 签名验证失败")
if __name__ == "__main__":
rsa_demo()
4.5 RSA 安全注意事项
- 永远不要使用 PKCS#1 v1.5 填充进行加密,应使用 OAEP
- 签名应使用 PSS 填充,而非 PKCS#1 v1.5
- 密钥长度至少 2048 位,推荐 3072 位或以上
- RSA 不适合加密大量数据,实际中用于加密对称密钥(密钥封装)
- RSA 的加密速度远慢于 AES,解密更慢
5. 椭圆曲线密码学:ECC
5.1 概述
ECC(Elliptic Curve Cryptography)于 1985 年由 Neal Koblitz 和 Victor Miller 独立提出。其安全性基于椭圆曲线离散对数问题(ECDLP)。ECC 的核心优势在于用更短的密钥提供与 RSA 相当的安全性。
等效安全强度对比:
| 对称密钥 (AES) | RSA 密钥 | ECC 密钥 | 备注 |
|---|---|---|---|
| 128 位 | 3,072 位 | 256 位 | 当前推荐最低安全级别 |
| 192 位 | 7,680 位 | 384 位 | 高安全需求 |
| 256 位 | 15,360 位 | 521 位 | 最高安全级别 |
5.2 常用椭圆曲线
| 曲线名称 | 密钥长度 | 标准 | 说明 |
|---|---|---|---|
| P-256 (secp256r1) | 256 位 | NIST | 最广泛使用,TLS 默认 |
| P-384 (secp384r1) | 384 位 | NIST | 高安全场景 |
| P-521 (secp521r1) | 521 位 | NIST | 最高安全级别 |
| Curve25519 | 255 位 | Bernstein | 高性能,用于 X25519 密钥交换 |
| Ed25519 | 255 位 | Bernstein | 用于 EdDSA 签名,高性能 |
| secp256k1 | 256 位 | SEC | Bitcoin 和以太坊使用 |
5.3 ECC 的三种核心应用
- ECDH(椭圆曲线 Diffie-Hellman):密钥协商
- ECDSA(椭圆曲线数字签名算法):数字签名
- ECIES(椭圆曲线集成加密方案):混合加密
5.4 Java 实现:ECDH 密钥交换 + ECDSA 签名
java
import java.security.*;
import java.security.spec.*;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class EccExample {
// ========== ECDH 密钥交换 ==========
public static void ecdhDemo() throws Exception {
System.out.println("=== ECDH 密钥交换 ===");
// Alice 生成密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair aliceKP = kpg.generateKeyPair();
// Bob 生成密钥对
KeyPair bobKP = kpg.generateKeyPair();
// Alice 计算共享密钥
KeyAgreement aliceKA = KeyAgreement.getInstance("ECDH");
aliceKA.init(aliceKP.getPrivate());
aliceKA.doPhase(bobKP.getPublic(), true);
byte[] aliceShared = aliceKA.generateSecret();
// Bob 计算共享密钥
KeyAgreement bobKA = KeyAgreement.getInstance("ECDH");
bobKA.init(bobKP.getPrivate());
bobKA.doPhase(aliceKP.getPublic(), true);
byte[] bobShared = bobKA.generateSecret();
// 验证双方密钥一致
System.out.println("Alice 共享密钥: "
+ Base64.getEncoder().encodeToString(aliceShared));
System.out.println("Bob 共享密钥: "
+ Base64.getEncoder().encodeToString(bobShared));
System.out.println("密钥一致: "
+ MessageDigest.isEqual(aliceShared, bobShared));
// 从共享密钥派生 AES 密钥(使用 HKDF 更佳,此处简化为截取)
SecretKey aesKey = new SecretKeySpec(aliceShared, 0, 32, "AES");
System.out.println("派生 AES-256 密钥: "
+ Base64.getEncoder().encodeToString(aesKey.getEncoded()));
}
// ========== ECDSA 数字签名 ==========
public static void ecdsaDemo() throws Exception {
System.out.println("\n=== ECDSA 数字签名 ===");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair kp = kpg.generateKeyPair();
byte[] data = "需要签名的交易数据: Alice -> Bob 100 BTC".getBytes("UTF-8");
// 签名
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(kp.getPrivate());
sig.update(data);
byte[] signature = sig.sign();
System.out.println("ECDSA 签名 (Base64): "
+ Base64.getEncoder().encodeToString(signature));
// 验签
sig.initVerify(kp.getPublic());
sig.update(data);
boolean valid = sig.verify(signature);
System.out.println("签名验证: " + valid);
}
public static void main(String[] args) throws Exception {
ecdhDemo();
ecdsaDemo();
}
}
5.5 Python 实现:ECDH + ECDSA + X25519/Ed25519
python
from cryptography.hazmat.primitives.asymmetric import ec, x25519, ed25519
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import base64
def ecdh_demo():
"""ECDH 密钥交换(P-256)"""
print("=== ECDH 密钥交换 (P-256) ===")
# Alice 和 Bob 分别生成密钥对
alice_private = ec.generate_private_key(ec.SECP256R1())
bob_private = ec.generate_private_key(ec.SECP256R1())
# 计算共享密钥
alice_shared = alice_private.exchange(ec.ECDH(), bob_private.public_key())
bob_shared = bob_private.exchange(ec.ECDH(), alice_private.public_key())
print(f"共享密钥一致: {alice_shared == bob_shared}")
# 通过 HKDF 派生 AES 密钥(正确做法)
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"ecdh-aes-key",
).derive(alice_shared)
print(f"派生 AES-256 密钥: {base64.b64encode(derived_key).decode()}")
def ecdsa_demo():
"""ECDSA 数字签名"""
print("\n=== ECDSA 数字签名 (P-256) ===")
private_key = ec.generate_private_key(ec.SECP256R1())
data = b"Transaction: Alice -> Bob 100 BTC"
# 签名
signature = private_key.sign(data, ec.ECDSA(hashes.SHA256()))
print(f"签名 (Base64): {base64.b64encode(signature).decode()}")
# 验签
try:
private_key.public_key().verify(signature, data, ec.ECDSA(hashes.SHA256()))
print("✅ 签名验证通过")
except Exception:
print("❌ 签名验证失败")
def x25519_demo():
"""X25519 密钥交换(现代高性能曲线)"""
print("\n=== X25519 密钥交换 ===")
alice_private = x25519.X25519PrivateKey.generate()
bob_private = x25519.X25519PrivateKey.generate()
alice_shared = alice_private.exchange(bob_private.public_key())
bob_shared = bob_private.exchange(alice_private.public_key())
print(f"共享密钥一致: {alice_shared == bob_shared}")
derived = HKDF(
algorithm=hashes.SHA256(), length=32,
salt=None, info=b"x25519-session-key",
).derive(alice_shared)
print(f"派生会话密钥: {base64.b64encode(derived).decode()}")
def ed25519_demo():
"""Ed25519 签名(现代高性能签名)"""
print("\n=== Ed25519 数字签名 ===")
private_key = ed25519.Ed25519PrivateKey.generate()
data = b"Critical system configuration update v2.1"
signature = private_key.sign(data)
print(f"签名 (Base64): {base64.b64encode(signature).decode()}")
print(f"签名长度: {len(signature)} 字节 (固定 64 字节)")
try:
private_key.public_key().verify(signature, data)
print("✅ Ed25519 签名验证通过")
except Exception:
print("❌ 验证失败")
if __name__ == "__main__":
ecdh_demo()
ecdsa_demo()
x25519_demo()
ed25519_demo()
5.6 ECC vs RSA 性能对比
| 操作 | RSA-3072 | ECC-256 | 差距 |
|---|---|---|---|
| 密钥生成 | ~600ms | ~10ms | ECC 快 ~60x |
| 签名 | ~5ms | ~2ms | ECC 快 ~2.5x |
| 验签 | ~0.3ms | ~3ms | RSA 验签更快 |
| 密钥大小 | 3072 位 | 256 位 | ECC 小 ~12x |
| 签名大小 | 384 字节 | 64 字节 | ECC 小 ~6x |
6. 哈希算法:SHA 家族
6.1 哈希函数的核心性质
- 确定性:相同输入永远产生相同输出
- 高效性:计算速度快
- 雪崩效应:输入微小变化导致输出巨大变化
- 抗原像性:给定哈希值,无法反推出原始输入
- 抗第二原像性:给定一个输入,无法找到产生相同哈希值的另一个输入
- 抗碰撞性:无法找到两个不同输入产生相同哈希值
6.2 SHA 家族演进
| 算法 | 输出长度 | 状态 | 说明 |
|---|---|---|---|
| MD5 | 128 位 | ❌ 已破解 | 2004 年发现实际碰撞 |
| SHA-1 | 160 位 | ❌ 已破解 | 2017 年 Google 证明碰撞(SHAttered) |
| SHA-224 | 224 位 | ✅ 安全 | SHA-2 家族成员 |
| SHA-256 | 256 位 | ✅✅ 推荐 | 最广泛使用的安全哈希 |
| SHA-384 | 384 位 | ✅✅ 推荐 | 高安全需求 |
| SHA-512 | 512 位 | ✅✅ 推荐 | 在 64 位系统上性能优于 SHA-256 |
| SHA-3-256 | 256 位 | ✅✅ 推荐 | 基于 Keccak 海绵结构,与 SHA-2 设计完全不同 |
| SHA-3-512 | 512 位 | ✅✅ 推荐 | 最高安全级别 |
| BLAKE2 | 可变 | ✅ 推荐 | 比 SHA-3 更快,广泛用于密码哈希 |
| BLAKE3 | 256 位 | ✅ 推荐 | 极高性能,支持并行计算 |
6.3 SHA-2 vs SHA-3 架构差异
| 特性 | SHA-2 | SHA-3 |
|---|---|---|
| 结构 | Merkle-Damgård | 海绵结构(Sponge) |
| 设计者 | NSA | Guido Bertoni 等(Keccak 团队) |
| 标准化年份 | 2001 | 2015(FIPS 202) |
| 长度扩展攻击 | 易受影响 | 天然免疫 |
| 硬件效率 | 中等 | 优秀(适合硬件实现) |
| 量子安全 | 256 位 → ~128 位有效安全 | 同上,但海绵结构提供额外防护 |
6.4 Java 实现:SHA 家族 + HMAC
java
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;
public class ShaExample {
// SHA-256 哈希
public static String sha256(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(input.getBytes("UTF-8"));
return HexFormat.of().formatHex(hash);
}
// SHA-512 哈希
public static String sha512(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] hash = md.digest(input.getBytes("UTF-8"));
return HexFormat.of().formatHex(hash);
}
// SHA3-256 哈希(Java 9+)
public static String sha3_256(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA3-256");
byte[] hash = md.digest(input.getBytes("UTF-8"));
return HexFormat.of().formatHex(hash);
}
// HMAC-SHA256(消息认证码)
public static String hmacSha256(String data, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
byte[] result = mac.doFinal(data.getBytes("UTF-8"));
return HexFormat.of().formatHex(result);
}
// 演示雪崩效应
public static void avalancheDemo() throws Exception {
String input1 = "Hello, World!";
String input2 = "Hello, World?"; // 仅改变一个字符
String hash1 = sha256(input1);
String hash2 = sha256(input2);
System.out.println("输入1: " + input1);
System.out.println("SHA-256: " + hash1);
System.out.println("输入2: " + input2);
System.out.println("SHA-256: " + hash2);
// 计算汉明距离(不同位数)
int diffBits = 0;
for (int i = 0; i < hash1.length(); i++) {
int xor = Integer.parseInt(hash1.substring(i, i+1), 16)
^ Integer.parseInt(hash2.substring(i, i+1), 16);
diffBits += Integer.bitCount(xor);
}
System.out.printf("差异位数: %d / 256 (%.1f%%)\n",
diffBits, diffBits * 100.0 / 256);
}
public static void main(String[] args) throws Exception {
String msg = "密码学是信息安全的基石";
System.out.println("=== 哈希算法对比 ===");
System.out.println("SHA-256: " + sha256(msg));
System.out.println("SHA-512: " + sha512(msg));
System.out.println("SHA3-256: " + sha3_256(msg));
System.out.println("\n=== HMAC-SHA256 ===");
byte[] hmacKey = "my-secret-hmac-key-12345".getBytes();
System.out.println("HMAC: " + hmacSha256(msg, hmacKey));
System.out.println("\n=== 雪崩效应演示 ===");
avalancheDemo();
}
}
6.5 Python 实现:SHA 全家族 + HMAC
python
import hashlib
import hmac
def hash_demo():
msg = "密码学是信息安全的基石".encode("utf-8")
print("=== SHA 家族哈希对比 ===")
print(f"SHA-256: {hashlib.sha256(msg).hexdigest()}")
print(f"SHA-384: {hashlib.sha384(msg).hexdigest()}")
print(f"SHA-512: {hashlib.sha512(msg).hexdigest()}")
print(f"SHA3-256: {hashlib.sha3_256(msg).hexdigest()}")
print(f"SHA3-512: {hashlib.sha3_512(msg).hexdigest()}")
print(f"BLAKE2b: {hashlib.blake2b(msg).hexdigest()}")
print(f"BLAKE2s: {hashlib.blake2s(msg).hexdigest()}")
# HMAC
key = b"my-secret-hmac-key-12345"
mac = hmac.new(key, msg, hashlib.sha256).hexdigest()
print(f"\nHMAC-SHA256: {mac}")
# HMAC 验证
mac2 = hmac.new(key, msg, hashlib.sha256).hexdigest()
print(f"HMAC 验证: {hmac.compare_digest(mac, mac2)}")
def avalanche_demo():
"""雪崩效应演示"""
print("\n=== 雪崩效应 ===")
h1 = hashlib.sha256(b"Hello, World!").digest()
h2 = hashlib.sha256(b"Hello, World?").digest()
diff = sum(bin(a ^ b).count('1') for a, b in zip(h1, h2))
print(f"输入仅差 1 个字符")
print(f"SHA-256 输出差异: {diff} / 256 位 ({diff*100/256:.1f}%)")
def file_hash(filepath: str):
"""文件哈希(流式处理,支持大文件)"""
sha256 = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256.update(chunk)
return sha256.hexdigest()
if __name__ == "__main__":
hash_demo()
avalanche_demo()
6.6 密码哈希(Password Hashing)
注意:SHA-256 等通用哈希不适合直接用于密码存储!应使用专用的密码哈希函数。
| 算法 | 推荐度 | 说明 |
|---|---|---|
| bcrypt | ✅ 推荐 | 经典选择,内置盐和工作因子 |
| scrypt | ✅ 推荐 | 内存密集型,抗 GPU/ASIC |
| Argon2id | ✅✅ 强烈推荐 | 2015 年密码哈希竞赛冠军,当前最佳选择 |
| PBKDF2 | ⚠️ 可用 | 迭代次数需足够大(≥600,000) |
Python Argon2 示例:
python
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3, # 迭代次数
memory_cost=65536, # 内存使用 64MB
parallelism=4, # 并行线程数
)
# 哈希密码
password_hash = ph.hash("MySecureP@ssword123")
print(f"Argon2id Hash: {password_hash}")
# 验证密码
try:
ph.verify(password_hash, "MySecureP@ssword123")
print("✅ 密码验证通过")
except Exception:
print("❌ 密码验证失败")
7. 传输层安全:TLS
7.1 概述
TLS(Transport Layer Security)是保护互联网通信安全的核心协议,用于 HTTPS、安全邮件、VPN 等场景。TLS 1.3(RFC 8446,2018年发布)是当前最新版本。
TLS 版本演进:
| 版本 | 年份 | 状态 | 说明 |
|---|---|---|---|
| SSL 2.0 | 1995 | ❌ 已废弃 | 存在严重安全漏洞 |
| SSL 3.0 | 1996 | ❌ 已废弃 | POODLE 攻击 |
| TLS 1.0 | 1999 | ❌ 已废弃 | BEAST 攻击 |
| TLS 1.1 | 2006 | ❌ 已废弃 | 安全性不足 |
| TLS 1.2 | 2008 | ⚠️ 仍可用 | 支持 AEAD,但配置复杂 |
| TLS 1.3 | 2018 | ✅✅ 推荐 | 简化握手、强制前向保密、移除不安全算法 |
7.2 TLS 1.3 握手流程
TLS 1.3 将握手从 2-RTT 缩减为 1-RTT,大幅降低延迟:
客户端 服务器
│ │
│── ClientHello ──────────────────────────────→│
│ · 支持的密码套件 │
│ · 支持的 TLS 版本(在 supported_versions 扩展中)│
│ · 密钥共享参数(key_share) │
│ · 客户端随机数 │
│ │
│←── ServerHello ──────────────────────────────│
│ · 选定的密码套件 │
│ · 服务器密钥共享参数 │
│ · 服务器随机数 │
│ │
│ ===== 此后所有通信均已加密 ===== │
│ │
│←── {EncryptedExtensions} ────────────────────│
│←── {Certificate} ───────────────────────────│
│←── {CertificateVerify} ─────────────────────│
│←── {Finished} ──────────────────────────────│
│ │
│── {Finished} ──────────────────────────────→│
│ │
│←========== 应用数据传输 ==========→│
7.3 TLS 1.3 vs TLS 1.2 关键区别
| 特性 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返 | 2-RTT | 1-RTT(支持 0-RTT 恢复) |
| 密钥交换 | RSA、DHE、ECDHE | 仅 (EC)DHE |
| 密码套件数量 | ~37 种 | 5 种 AEAD 套件 |
| 前向保密 | 可选 | 强制 |
| 静态 RSA 密钥交换 | 支持 | 已移除 |
| 压缩 | 支持 | 已移除(防 CRIME 攻击) |
| ServerHello 后 | 明文 | 加密 |
| 0-RTT | 不支持 | 支持(会话恢复) |
7.4 TLS 1.3 支持的密码套件
TLS_AES_128_GCM_SHA256 (0x1301)
TLS_AES_256_GCM_SHA384 (0x1302)
TLS_CHACHA20_POLY1305_SHA256 (0x1303)
TLS_AES_128_CCM_SHA256 (0x1304)
TLS_AES_128_CCM_8_SHA256 (0x1305)
7.5 Java 实现:TLS 服务器与客户端
java
import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
// ===== TLS 服务器 =====
public class TlsServer {
public static void main(String[] args) throws Exception {
// 加载密钥库(包含服务器证书和私钥)
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("server.p12"), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "password".toCharArray());
SSLContext ctx = SSLContext.getInstance("TLSv1.3");
ctx.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket(8443);
// 限制协议和密码套件
serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
serverSocket.setEnabledCipherSuites(new String[]{
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256"
});
System.out.println("TLS 1.3 服务器启动,监听端口 8443...");
while (true) {
SSLSocket client = (SSLSocket) serverSocket.accept();
SSLSession session = client.getSession();
System.out.println("客户端连接: " + client.getInetAddress());
System.out.println("协议: " + session.getProtocol());
System.out.println("密码套件: " + session.getCipherSuite());
// 读写数据
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true);
String line = in.readLine();
System.out.println("收到: " + line);
out.println("服务器回复: " + line);
client.close();
}
}
}
java
// ===== TLS 客户端 =====
public class TlsClient {
public static void main(String[] args) throws Exception {
SSLContext ctx = SSLContext.getInstance("TLSv1.3");
ctx.init(null, null, null); // 使用默认信任库
SSLSocketFactory sf = ctx.getSocketFactory();
SSLSocket socket = (SSLSocket) sf.createSocket("localhost", 8443);
socket.setEnabledProtocols(new String[]{"TLSv1.3"});
// 发送数据
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
out.println("Hello, TLS 1.3!");
System.out.println("服务器回复: " + in.readLine());
// 显示会话信息
SSLSession session = socket.getSession();
System.out.println("协议: " + session.getProtocol());
System.out.println("密码套件: " + session.getCipherSuite());
socket.close();
}
}
7.6 Python 实现:TLS 连接与证书验证
python
import ssl
import socket
import pprint
def inspect_tls_connection(hostname: str, port: int = 443):
"""检查目标站点的 TLS 配置"""
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(f"=== {hostname}:{port} TLS 信息 ===")
print(f"TLS 版本: {ssock.version()}")
print(f"密码套件: {ssock.cipher()}")
cert = ssock.getpeercert()
print(f"\n证书主题: {dict(x[0] for x in cert['subject'])}")
print(f"颁发者: {dict(x[0] for x in cert['issuer'])}")
print(f"有效期至: {cert['notAfter']}")
print(f"SAN:")
for san_type, san_value in cert.get('subjectAltName', []):
print(f" {san_type}: {san_value}")
def create_tls_server(certfile: str, keyfile: str, port: int = 8443):
"""创建 TLS 1.3 服务器"""
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_3
context.load_cert_chain(certfile, keyfile)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('0.0.0.0', port))
sock.listen(5)
with context.wrap_socket(sock, server_side=True) as ssock:
print(f"TLS 1.3 服务器监听端口 {port}")
conn, addr = ssock.accept()
print(f"连接来自: {addr}, 协议: {conn.version()}")
data = conn.recv(1024)
conn.send(b"Hello from TLS 1.3 server!")
conn.close()
if __name__ == "__main__":
# 检查知名网站的 TLS 配置
for site in ["www.google.com", "github.com", "www.cloudflare.com"]:
try:
inspect_tls_connection(site)
print()
except Exception as e:
print(f"{site}: {e}\n")
8. 公钥基础设施:PKI
8.1 概述
PKI(Public Key Infrastructure)是一套用于创建、管理、分发、使用、存储和撤销数字证书的体系。它为公钥加密提供信任框架,是 HTTPS、安全邮件、VPN、代码签名等技术的基础。
8.2 PKI 核心组件
| 组件 | 说明 |
|---|---|
| CA(证书颁发机构) | 签发、存储和签署数字证书的可信实体 |
| RA(注册机构) | 验证证书申请者的身份 |
| 数字证书 | 将公钥与实体身份绑定的电子凭证(X.509 标准) |
| 证书库 | 存储证书及其元数据的数据库 |
| CRL(证书吊销列表) | 列出在到期前被吊销的证书 |
| OCSP(在线证书状态协议) | 实时查询证书是否被吊销 |
8.3 证书信任链
Root CA(根证书颁发机构)
│ 自签名证书,离线存储
│ 内置于操作系统和浏览器的信任库中
│
├── Intermediate CA(中间 CA)
│ │ 由 Root CA 签发
│ │
│ ├── End Entity Certificate(终端实体证书)
│ │ 用于 www.example.com 的 TLS 证书
│ │
│ └── End Entity Certificate
│ 用于 mail.example.com 的 TLS 证书
│
└── Intermediate CA 2
│
└── End Entity Certificate
代码签名证书
验证过程:浏览器从终端实体证书开始,沿信任链逐级向上验证签名,直到到达已信任的根 CA。
8.4 X.509 证书结构
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 04:00:00:00:00:01:15:4b:5a:c3:94
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=BE, O=GlobalSign, CN=GlobalSign Root CA
Validity:
Not Before: Sep 1 00:00:00 2014 GMT
Not After : Jan 28 12:00:00 2028 GMT
Subject: C=US, ST=California, O=Example Inc, CN=www.example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:www.example.com, DNS:example.com
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 CRL Distribution Points:
URI:http://crl.globalsign.com/root.crl
Signature Algorithm: sha256WithRSAEncryption
[签名数据]
8.5 Java 实现:证书生成与验证
java
import java.security.*;
import java.security.cert.*;
import java.math.BigInteger;
import java.util.Date;
import java.io.*;
// 使用 Bouncy Castle 库生成自签名证书
// 依赖: org.bouncycastle:bcprov-jdk18on:1.78
// 依赖: org.bouncycastle:bcpkix-jdk18on:1.78
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.*;
import org.bouncycastle.cert.jcajce.*;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.jcajce.*;
public class PkiExample {
// 生成自签名 CA 证书
public static X509Certificate generateCACert(KeyPair caKeyPair)
throws Exception {
X500Name issuer = new X500Name("CN=My Root CA, O=MyOrg, C=CN");
BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis()
+ 365L * 24 * 60 * 60 * 1000 * 10); // 10 年
JcaX509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
issuer, serial, notBefore, notAfter, issuer,
caKeyPair.getPublic()
);
// 添加 CA 扩展
builder.addExtension(Extension.basicConstraints, true,
new BasicConstraints(true));
builder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign));
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA")
.build(caKeyPair.getPrivate());
return new JcaX509CertificateConverter()
.getCertificate(builder.build(signer));
}
// 使用 CA 签发终端实体证书
public static X509Certificate generateEndEntityCert(
KeyPair caKeyPair, X509Certificate caCert,
KeyPair entityKeyPair, String cn) throws Exception {
X500Name issuer = new X500Name(caCert.getSubjectX500Principal().getName());
X500Name subject = new X500Name("CN=" + cn + ", O=MyOrg, C=CN");
BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis()
+ 365L * 24 * 60 * 60 * 1000); // 1 年
JcaX509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
issuer, serial, notBefore, notAfter, subject,
entityKeyPair.getPublic()
);
// SAN 扩展
GeneralNames sans = new GeneralNames(new GeneralName[]{
new GeneralName(GeneralName.dNSName, cn),
new GeneralName(GeneralName.dNSName, "www." + cn)
});
builder.addExtension(Extension.subjectAlternativeName, false, sans);
builder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA")
.build(caKeyPair.getPrivate());
return new JcaX509CertificateConverter()
.getCertificate(builder.build(signer));
}
// 验证证书链
public static void verifyCertificateChain(
X509Certificate endEntity, X509Certificate caCert)
throws Exception {
// 1. 验证签名
endEntity.verify(caCert.getPublicKey());
System.out.println("✅ 签名验证通过");
// 2. 检查有效期
endEntity.checkValidity();
System.out.println("✅ 证书在有效期内");
// 3. 检查颁发者
if (endEntity.getIssuerX500Principal()
.equals(caCert.getSubjectX500Principal())) {
System.out.println("✅ 颁发者匹配");
}
}
public static void main(String[] args) throws Exception {
Security.addProvider(
new org.bouncycastle.jce.provider.BouncyCastleProvider());
// 生成 CA 密钥对和证书
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(3072);
KeyPair caKeyPair = kpg.generateKeyPair();
X509Certificate caCert = generateCACert(caKeyPair);
System.out.println("CA 证书: " + caCert.getSubjectX500Principal());
// 生成终端实体证书
KeyPair entityKP = kpg.generateKeyPair();
X509Certificate entityCert = generateEndEntityCert(
caKeyPair, caCert, entityKP, "example.com");
System.out.println("实体证书: "
+ entityCert.getSubjectX500Principal());
// 验证证书链
System.out.println("\n=== 证书链验证 ===");
verifyCertificateChain(entityCert, caCert);
}
}
8.6 Python 实现:证书操作
python
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime
def create_ca_certificate():
"""创建自签名 CA 根证书"""
# 生成 CA 密钥对
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
ca_name = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Organization"),
x509.NameAttribute(NameOID.COMMON_NAME, "My Root CA"),
])
ca_cert = (
x509.CertificateBuilder()
.subject_name(ca_name)
.issuer_name(ca_name) # 自签名
.public_key(ca_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
.not_valid_after(
datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(days=3650)
)
.add_extension(
x509.BasicConstraints(ca=True, path_length=None), critical=True
)
.add_extension(
x509.KeyUsage(
digital_signature=False, key_cert_sign=True,
crl_sign=True, content_commitment=False,
key_encipherment=False, data_encipherment=False,
key_agreement=False, encipher_only=False,
decipher_only=False,
),
critical=True,
)
.sign(ca_key, hashes.SHA256())
)
print(f"CA 证书已创建: {ca_cert.subject}")
return ca_key, ca_cert
def issue_server_certificate(ca_key, ca_cert, domain: str):
"""使用 CA 签发服务器证书"""
server_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
subject = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Example Inc"),
x509.NameAttribute(NameOID.COMMON_NAME, domain),
])
server_cert = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(ca_cert.subject)
.public_key(server_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.datetime.now(datetime.timezone.utc))
.not_valid_after(
datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(days=365)
)
.add_extension(
x509.SubjectAlternativeName([
x509.DNSName(domain),
x509.DNSName(f"www.{domain}"),
]),
critical=False,
)
.add_extension(
x509.KeyUsage(
digital_signature=True, key_encipherment=True,
key_cert_sign=False, crl_sign=False,
content_commitment=False, data_encipherment=False,
key_agreement=False, encipher_only=False,
decipher_only=False,
),
critical=True,
)
.add_extension(
x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]),
critical=False,
)
.sign(ca_key, hashes.SHA256())
)
print(f"服务器证书已签发: {server_cert.subject}")
return server_key, server_cert
def verify_certificate(cert, ca_cert):
"""验证证书(简化版,生产中使用完整路径验证)"""
print(f"\n=== 证书验证: {cert.subject} ===")
print(f"颁发者: {cert.issuer}")
print(f"有效期: {cert.not_valid_before_utc} ~ {cert.not_valid_after_utc}")
print(f"序列号: {cert.serial_number}")
# 验证签名
try:
ca_cert.public_key().verify(
cert.signature,
cert.tbs_certificate_bytes,
padding.PKCS1v15(),
cert.signature_hash_algorithm,
)
print("✅ CA 签名验证通过")
except Exception as e:
print(f"❌ 签名验证失败: {e}")
# 检查有效期
now = datetime.datetime.now(datetime.timezone.utc)
if cert.not_valid_before_utc <= now <= cert.not_valid_after_utc:
print("✅ 证书在有效期内")
else:
print("❌ 证书已过期或尚未生效")
# 显示 SAN
try:
san = cert.extensions.get_extension_for_class(
x509.SubjectAlternativeName)
print(f"SAN: {san.value.get_values_for_type(x509.DNSName)}")
except x509.ExtensionNotFound:
pass
def save_artifacts(ca_key, ca_cert, server_key, server_cert):
"""保存证书和密钥到文件"""
# CA 证书
with open("ca.crt", "wb") as f:
f.write(ca_cert.public_bytes(serialization.Encoding.PEM))
# CA 私钥
with open("ca.key", "wb") as f:
f.write(ca_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(b"ca-password"),
))
# 服务器证书
with open("server.crt", "wb") as f:
f.write(server_cert.public_bytes(serialization.Encoding.PEM))
# 服务器私钥
with open("server.key", "wb") as f:
f.write(server_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption(),
))
print("\n所有证书和密钥已保存到文件")
if __name__ == "__main__":
from cryptography.hazmat.primitives.asymmetric import padding
ca_key, ca_cert = create_ca_certificate()
server_key, server_cert = issue_server_certificate(
ca_key, ca_cert, "example.com")
verify_certificate(server_cert, ca_cert)
save_artifacts(ca_key, ca_cert, server_key, server_cert)
9. 数字签名
9.1 签名原理
数字签名提供三个关键保障:身份认证 (确认消息来自声称的发送方)、完整性 (消息未被篡改)和不可否认性(发送方无法否认发送行为)。
签名流程:
- 对原始数据计算哈希摘要
- 用私钥加密哈希摘要 → 得到数字签名
- 将原始数据 + 数字签名一起发送
验签流程:
- 用公钥解密数字签名 → 得到原始哈希摘要
- 对收到的数据重新计算哈希摘要
- 比较两个摘要是否一致
9.2 常用签名算法
| 算法 | 基础 | 密钥大小 | 速度 | 推荐度 |
|---|---|---|---|---|
| RSA-PSS | 大整数分解 | 3072+ 位 | 慢 | ✅ |
| ECDSA | 椭圆曲线 | 256+ 位 | 快 | ✅✅ |
| EdDSA (Ed25519) | 扭曲 Edwards 曲线 | 256 位 | 最快 | ✅✅✅ |
| ML-DSA (Dilithium) | 格密码 | ~2.5KB | 中等 | ✅ 后量子 |
9.3 完整 Java 签名工具类
java
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
public class DigitalSignatureUtil {
// ====== RSA-PSS 签名 ======
public static byte[] signRSA(byte[] data, PrivateKey key) throws Exception {
Signature sig = Signature.getInstance("RSASSA-PSS");
sig.setParameter(new PSSParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
sig.initSign(key);
sig.update(data);
return sig.sign();
}
public static boolean verifyRSA(byte[] data, byte[] signature,
PublicKey key) throws Exception {
Signature sig = Signature.getInstance("RSASSA-PSS");
sig.setParameter(new PSSParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1));
sig.initVerify(key);
sig.update(data);
return sig.verify(signature);
}
// ====== ECDSA 签名 ======
public static byte[] signECDSA(byte[] data, PrivateKey key)
throws Exception {
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(key);
sig.update(data);
return sig.sign();
}
public static boolean verifyECDSA(byte[] data, byte[] signature,
PublicKey key) throws Exception {
Signature sig = Signature.getInstance("SHA256withECDSA");
sig.initVerify(key);
sig.update(data);
return sig.verify(signature);
}
// ====== Ed25519 签名 (Java 15+) ======
public static byte[] signEd25519(byte[] data, PrivateKey key)
throws Exception {
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(key);
sig.update(data);
return sig.sign();
}
public static boolean verifyEd25519(byte[] data, byte[] signature,
PublicKey key) throws Exception {
Signature sig = Signature.getInstance("Ed25519");
sig.initVerify(key);
sig.update(data);
return sig.verify(signature);
}
public static void main(String[] args) throws Exception {
byte[] data = "重要合同文件 v3.2 final".getBytes("UTF-8");
// RSA-PSS
KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA");
rsaGen.initialize(3072);
KeyPair rsaKP = rsaGen.generateKeyPair();
byte[] rsaSig = signRSA(data, rsaKP.getPrivate());
System.out.println("RSA-PSS 签名长度: " + rsaSig.length + " 字节");
System.out.println("RSA-PSS 验证: " + verifyRSA(data, rsaSig,
rsaKP.getPublic()));
// ECDSA
KeyPairGenerator ecGen = KeyPairGenerator.getInstance("EC");
ecGen.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair ecKP = ecGen.generateKeyPair();
byte[] ecSig = signECDSA(data, ecKP.getPrivate());
System.out.println("\nECDSA 签名长度: " + ecSig.length + " 字节");
System.out.println("ECDSA 验证: " + verifyECDSA(data, ecSig,
ecKP.getPublic()));
// Ed25519 (Java 15+)
KeyPairGenerator edGen = KeyPairGenerator.getInstance("Ed25519");
KeyPair edKP = edGen.generateKeyPair();
byte[] edSig = signEd25519(data, edKP.getPrivate());
System.out.println("\nEd25519 签名长度: " + edSig.length + " 字节");
System.out.println("Ed25519 验证: " + verifyEd25519(data, edSig,
edKP.getPublic()));
}
}
10. 混合加密体系
10.1 为什么需要混合加密
- 对称加密(AES):速度快,但密钥分发困难
- 非对称加密(RSA/ECC):密钥分发方便,但速度慢且不适合大数据
混合加密结合两者优势:用非对称加密安全传输对称密钥,再用对称密钥加密实际数据。
10.2 Java 实现:RSA + AES 混合加密
java
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.util.Base64;
public class HybridEncryption {
// 加密:生成 AES 密钥 → AES 加密数据 → RSA 加密 AES 密钥
public static EncryptedPackage encrypt(byte[] plaintext,
PublicKey recipientPublicKey)
throws Exception {
// 1. 生成随机 AES-256 密钥
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
SecretKey aesKey = keyGen.generateKey();
// 2. AES-GCM 加密数据
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey,
new GCMParameterSpec(128, iv));
byte[] ciphertext = aesCipher.doFinal(plaintext);
// 3. RSA-OAEP 加密 AES 密钥
Cipher rsaCipher = Cipher.getInstance(
"RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, recipientPublicKey);
byte[] encryptedKey = rsaCipher.doFinal(aesKey.getEncoded());
return new EncryptedPackage(encryptedKey, iv, ciphertext);
}
// 解密:RSA 解密 AES 密钥 → AES 解密数据
public static byte[] decrypt(EncryptedPackage pkg,
PrivateKey recipientPrivateKey)
throws Exception {
// 1. RSA 解密 AES 密钥
Cipher rsaCipher = Cipher.getInstance(
"RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, recipientPrivateKey);
byte[] aesKeyBytes = rsaCipher.doFinal(pkg.encryptedKey);
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
// 2. AES-GCM 解密数据
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.DECRYPT_MODE, aesKey,
new GCMParameterSpec(128, pkg.iv));
return aesCipher.doFinal(pkg.ciphertext);
}
// 封装加密结果
static class EncryptedPackage {
byte[] encryptedKey; // RSA 加密的 AES 密钥
byte[] iv; // AES-GCM IV
byte[] ciphertext; // AES-GCM 密文(含认证标签)
EncryptedPackage(byte[] ek, byte[] iv, byte[] ct) {
this.encryptedKey = ek;
this.iv = iv;
this.ciphertext = ct;
}
}
public static void main(String[] args) throws Exception {
// 接收方生成 RSA 密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(3072);
KeyPair kp = kpg.generateKeyPair();
// 发送方加密
String message = "这是一份机密报告,包含大量数据...".repeat(100);
EncryptedPackage pkg = encrypt(
message.getBytes("UTF-8"), kp.getPublic());
System.out.println("原始大小: " + message.getBytes().length + " 字节");
System.out.println("加密密钥大小: " + pkg.encryptedKey.length + " 字节");
System.out.println("密文大小: " + pkg.ciphertext.length + " 字节");
// 接收方解密
byte[] decrypted = decrypt(pkg, kp.getPrivate());
System.out.println("解密成功: " + new String(decrypted, "UTF-8")
.equals(message));
}
}
10.3 Python 实现:ECC + AES 混合加密
python
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes, serialization
import os, base64
class HybridEncryption:
"""基于 ECIES 思想的 ECC + AES-GCM 混合加密"""
@staticmethod
def encrypt(plaintext: bytes, recipient_public_key) -> dict:
# 1. 生成临时 ECC 密钥对
ephemeral_key = ec.generate_private_key(ec.SECP256R1())
# 2. ECDH 密钥协商
shared_secret = ephemeral_key.exchange(
ec.ECDH(), recipient_public_key)
# 3. HKDF 派生 AES 密钥
aes_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"hybrid-encryption-v1",
).derive(shared_secret)
# 4. AES-GCM 加密
nonce = os.urandom(12)
aesgcm = AESGCM(aes_key)
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
# 5. 导出临时公钥(发送给接收方)
ephemeral_pub_bytes = ephemeral_key.public_key().public_bytes(
serialization.Encoding.X962,
serialization.PublicFormat.CompressedPoint,
)
return {
"ephemeral_public_key": ephemeral_pub_bytes,
"nonce": nonce,
"ciphertext": ciphertext,
}
@staticmethod
def decrypt(encrypted: dict, recipient_private_key) -> bytes:
# 1. 恢复临时公钥
ephemeral_pub = ec.EllipticCurvePublicKey.from_encoded_point(
ec.SECP256R1(), encrypted["ephemeral_public_key"])
# 2. ECDH 密钥协商
shared_secret = recipient_private_key.exchange(
ec.ECDH(), ephemeral_pub)
# 3. 派生相同的 AES 密钥
aes_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"hybrid-encryption-v1",
).derive(shared_secret)
# 4. AES-GCM 解密
aesgcm = AESGCM(aes_key)
return aesgcm.decrypt(
encrypted["nonce"], encrypted["ciphertext"], None)
# 使用示例
def hybrid_demo():
# 接收方生成密钥对
recipient_key = ec.generate_private_key(ec.SECP256R1())
recipient_pub = recipient_key.public_key()
# 发送方加密
message = "机密数据:用户 Alice 的银行账户信息..." * 50
encrypted = HybridEncryption.encrypt(
message.encode(), recipient_pub)
print(f"临时公钥大小: {len(encrypted['ephemeral_public_key'])} 字节")
print(f"密文大小: {len(encrypted['ciphertext'])} 字节")
# 接收方解密
decrypted = HybridEncryption.decrypt(encrypted, recipient_key)
print(f"解密成功: {decrypted.decode() == message}")
if __name__ == "__main__":
hybrid_demo()
11. 后量子密码学
11.1 量子威胁
量子计算机通过两个关键算法威胁现有密码体系:
- Shor 算法 :可在多项式时间内分解大整数和求解离散对数 → 彻底破解 RSA、DSA、ECDSA、ECDH、ElGamal
- Grover 算法 :将暴力搜索复杂度降低为平方根 → 将 AES-128 降至 64 位有效安全,SHA-256 碰撞安全从 128 位降至约 85 位
11.2 各算法的量子抗性
| 算法类型 | 算法 | 量子安全性 | 应对策略 |
|---|---|---|---|
| 对称加密 | AES-128 | ⚠️ 降至 64 位 | 升级到 AES-256 |
| 对称加密 | AES-256 | ✅ 降至 128 位,仍安全 | 继续使用 |
| 哈希 | SHA-256 | ✅ 基本安全 | 推荐升级到 SHA-384+ |
| 哈希 | SHA-512 | ✅✅ 安全 | 继续使用 |
| 非对称 | RSA | ❌ 被 Shor 算法破解 | 迁移到 PQC |
| 非对称 | ECDSA/ECDH | ❌ 被 Shor 算法破解 | 迁移到 PQC |
| 签名 | EdDSA | ❌ 被 Shor 算法破解 | 迁移到 PQC |
11.3 NIST 后量子密码标准(2024年发布)
| 标准 | 原名 | 用途 | 数学基础 |
|---|---|---|---|
| FIPS 203 (ML-KEM) | CRYSTALS-Kyber | 密钥封装(密钥交换) | 模格(Module Lattice) |
| FIPS 204 (ML-DSA) | CRYSTALS-Dilithium | 数字签名 | 模格 |
| FIPS 205 (SLH-DSA) | SPHINCS+ | 数字签名(无状态哈希) | 哈希函数 |
额外候选:FALCON(签名,基于 NTRU 格)、Classic McEliece(KEM,基于编码理论)。
11.4 CNSA 2.0 迁移时间表(美国 NSA)
| 用途 | 要求算法 | 迁移截止 |
|---|---|---|
| 软件/固件签名 | ML-DSA 或 LMS/XMSS | 2025 年起 |
| Web 浏览器/服务器 | ML-KEM + ML-DSA | 2025 年起 |
| 传统网络 | ML-KEM + ML-DSA | 2030 年 |
| 操作系统 | 全面 PQC 支持 | 2030 年 |
| 哈希算法 | SHA-384 或 SHA-512 | 立即 |
| 对称加密 | AES-256 | 立即 |
11.5 过渡策略:混合模式
当前推荐的迁移策略是"混合"模式------同时使用传统算法和后量子算法,确保即使某一方被攻破,另一方仍能提供安全保障。
混合密钥交换:X25519 + ML-KEM-768
混合数字签名:Ed25519 + ML-DSA-65
12. 密码学安全最佳实践
12.1 密钥管理
- 密钥生成:始终使用密码学安全的随机数生成器(CSPRNG)
- 密钥存储:使用 HSM(硬件安全模块)或密钥管理服务(KMS)
- 密钥轮换:定期更换密钥,建立轮换策略
- 密钥销毁:密钥不再需要时安全擦除
- 最小权限:仅授予必要的密钥访问权限
12.2 算法选择指南
| 用途 | 推荐算法 | 避免使用 |
|---|---|---|
| 数据加密 | AES-256-GCM | DES、3DES、RC4、AES-ECB |
| 密钥交换 | X25519、ECDH P-256 | 静态 RSA 密钥交换 |
| 数字签名 | Ed25519、ECDSA P-256 | RSA-1024、PKCS#1 v1.5 |
| 哈希 | SHA-256、SHA-3、BLAKE2 | MD5、SHA-1 |
| 密码哈希 | Argon2id、bcrypt | MD5、SHA-256 直接哈希 |
| TLS 协议 | TLS 1.3 | SSL、TLS 1.0/1.1 |
| 密钥派生 | HKDF、PBKDF2(≥600k 迭代) | 简单哈希 |
12.3 常见安全陷阱
- 自己发明密码算法:永远不要。使用经过审查的标准算法。
- 复用 Nonce/IV:AES-GCM 中重复使用 nonce 会导致灾难性安全问题。
- 不验证 MAC/签名:先验证再处理(Encrypt-then-MAC,或使用 AEAD)。
- 使用 ECB 模式:相同明文产生相同密文,泄露数据模式。
- 硬编码密钥:密钥不应出现在源代码中。
- 忽略时序攻击:密码比较应使用常量时间函数。
- 不验证证书:禁用证书验证等于没有加密。
13. 算法对比速查表
13.1 安全等级对照
| 安全位数 | AES | RSA | ECC | SHA-2 | SHA-3 |
|---|---|---|---|---|---|
| 80 | --- | 1024 | 160 | --- | --- |
| 112 | --- | 2048 | 224 | SHA-224 | SHA3-224 |
| 128 | AES-128 | 3072 | 256 | SHA-256 | SHA3-256 |
| 192 | AES-192 | 7680 | 384 | SHA-384 | SHA3-384 |
| 256 | AES-256 | 15360 | 521 | SHA-512 | SHA3-512 |
13.2 性能参考(典型硬件)
| 操作 | AES-256-GCM | RSA-3072 | ECC P-256 | Ed25519 |
|---|---|---|---|---|
| 加密/签名 | ~1 GB/s | ~5ms | ~2ms | ~0.5ms |
| 解密/验签 | ~1 GB/s | ~100ms | ~3ms | ~1ms |
| 密钥生成 | 即时 | ~600ms | ~10ms | ~0.1ms |
| 密钥大小 | 32 字节 | ~384 字节 | 32 字节 | 32 字节 |
13.3 应用场景推荐
| 场景 | 推荐方案 |
|---|---|
| HTTPS/Web 安全 | TLS 1.3 + ECDHE + AES-256-GCM |
| 文件加密 | AES-256-GCM(密钥通过 PBKDF2/Argon2 派生) |
| 数据库字段加密 | AES-256-GCM + 密钥管理服务 |
| API 认证 | HMAC-SHA256 或 Ed25519 签名 |
| 代码签名 | RSA-3072-PSS 或 ECDSA P-384 |
| 电子邮件加密 | S/MIME(RSA/ECC + AES) |
| 区块链 | secp256k1 + SHA-256(Bitcoin)/ Keccak-256(Ethereum) |
| IoT 设备 | ECC(资源受限)+ AES-128-CCM |
| 长期存档 | AES-256 + 考虑后量子迁移 |
14. 参考资源
官方标准与规范
- NIST FIPS 197 --- AES 标准
- NIST FIPS 186-5 --- 数字签名标准(DSS)
- NIST FIPS 202 --- SHA-3 标准
- RFC 8446 --- TLS 1.3
- RFC 5280 --- X.509 PKI 证书和 CRL 规范
- NIST SP 800-38D --- AES-GCM 推荐
- NIST SP 800-56A --- 密钥协商方案
- NIST PQC 项目 --- https://csrc.nist.gov/projects/post-quantum-cryptography
学习资源
- 《密码编码学与网络安全》(William Stallings)
- 《应用密码学》(Bruce Schneier)
- 《Serious Cryptography》(Jean-Philippe Aumasson)
- Practical Cryptography for Developers --- https://cryptobook.nakov.com
- The Illustrated TLS 1.3 Connection --- https://tls13.xargs.org
开发库
- Java: JCA/JCE (内置)、Bouncy Castle
- Python: cryptography、PyCryptodome、hashlib(内置)
- Go: crypto 标准库
- Rust: ring、RustCrypto
- C/C++: OpenSSL、libsodium、wolfSSL