密钥管理入门:对称密钥、非对称密钥、哈希函数与数字签名
聊密钥管理之前,我得先说说一个基本认知:密码学不是用来藏东西的,而是用来管关系的。你想想看,密钥就是你和系统之间的信任凭证。没有密钥,权限架构就是个空壳。
我个人习惯把密钥管理比作一把钥匙和一把锁的关系。但实际工程中,远不止这么简单。今天我们从四个核心概念入手,把地基打牢。
对称密钥:一把钥匙开一把锁
对称加密,说白了就是加密和解密用同一个密钥。就像你家的门钥匙,用它锁门,也用它开门。
我在项目中遇到过这种情况:用户上传文件到系统,我们用的是 AES-256 对称加密。为什么选它?因为文件动不动几百 MB,用非对称加密会慢到你怀疑人生。
from cryptography.fernet import Fernet
# 生成密钥(对称密钥)
key = Fernet.generate_key()
cipher = Fernet(key)
# 加密
plaintext = b"这是一份机密RBAC配置文档"
ciphertext = cipher.encrypt(plaintext)
# 解密
decrypted = cipher.decrypt(ciphertext)
print(decrypted.decode())
但有一个坑,我踩过不止一次:对称密钥的分发。你想想看,如果加密方和解密方用的同一个密钥,那这个密钥怎么安全地传给对方?网络传输?万一被截获,加密就形同虚设。所以对称密钥的弱点不在算法,而在管理。
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥数量 | 1个(共享密钥) | 2个(公钥+私钥) |
| 速度 | 快(硬件加速常见) | 慢(数学运算复杂) |
| 典型算法 | AES、DES、3DES | RSA、ECC、DSA |
| 适用场景 | 大数据量加密、存储加密 | 密钥交换、数字签名 |
非对称密钥:公钥在手,天下我有
非对称加密解决了对称加密最头疼的问题——密钥分发。它用了数学上的一对关系:公钥和私钥。公钥可以公开,私钥必须保密。你用公钥加密,只有对应的私钥能解。
我刚开始学的时候,总觉得这很神奇。后来在做一个 RBAC 系统的跨服务认证时,用 RSA 做密钥交换,才真正体会到它的价值。
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# 生成RSA密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
# 导出公钥(给别人用)
pub_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
print(pub_pem.decode())
不过非对称加密有个代价:慢。它不直接加密大文件,而是用来加密对称密钥,对称密钥再去加密文件。这叫混合加密,目前 HTTPS 就是这么干的。
哈希函数:不可逆的单行道
哈希函数不是加密,它是摘要。输入任意长度的数据,输出固定长度的哈希值。这个过程不可逆,你无法从哈希值反推出原始数据。
我记有一次做用户密码存储,有人问为什么不用对称加密存密码?我说:你存了加密的密码,就代表你随时能解密它。那万一数据库泄露了,黑客拿到了密钥,所有用户的密码就全裸奔了。
哈希函数就不一样了:只存哈希值,不存原始密码。用户登录时,系统对输入的密码做同样的哈希,跟数据库里的哈希对比。一致就放行,不一致就拒绝。
import hashlib
password = b"MySecurePassword123"
hash_obj = hashlib.sha256(password)
digest = hash_obj.hexdigest()
print(digest)
# 输出: e9c7b4b8...(64位十六进制字符串)
常见哈希算法及其输出长度:
| 算法 | 输出长度 | 安全性 |
|---|---|---|
| MD5 | 128 bits | 已淘汰(碰撞攻击) |
| SHA-1 | 160 bits | 已淘汰(碰撞攻击) |
| SHA-256 | 256 bits | 安全(推荐) |
| SHA-3 | 可变 | 更安全(新标准) |
数字签名:防抵赖的电子印章
数字签名是非对称密钥的一个核心应用。你拿私钥对一段数据签名,别人用你的公钥验证签名。签名的作用就是:证明数据是你签的,且数据没有被篡改。
我在 RBAC 权限管理系统中用数字签名来签发 access 。每次用户登录成功后,服务器用私钥对用户的身份信息和权限范围签名,生成一个 JWT 。客户端持有这个 调用 API,服务端用公钥验证签名是否合法。
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
# 签名
signature = private_key.sign(
b"用户ID=1001,角色=admin,时间戳=1700000000",
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
# 验证签名
try:
public_key.verify(
signature,
b"用户ID=1001,角色=admin,时间戳=1700000000",
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
print("签名验证成功,数据完整且来源可信")
except Exception as e:
print("签名验证失败,数据可能被篡改")
综合实战:密钥管理的全貌
讲到这里,你可能觉得四个概念各管各的。其实在实际工程中,它们是混合使用的。比如你在 RBAC 系统中做 API 鉴权:
- 用户登录:密码用加盐哈希存储,防止泄露。
- 生成 :服务端用 RSA 私钥签发 JWT(数字签名)。
- 传输 :用 HTTPS 的 TLS 加密,TLS 底层就是混合加密(非对称交换密钥 + 对称加密数据)。
- 验证 :服务端用 RSA 公钥验证签名。
- 存储敏感数据:用 AES 对称加密,密钥用 KMS(密钥管理服务)保护。
下面这张图,是我梳理的密钥管理知识结构,你一看就明白了:
看到这张图,你会发现四个概念各有分工,又相互支撑。对称加密负责效率,非对称加密负责安全分发,哈希负责完整性校验,数字签名负责可信身份。四者结合,构成了现代 RBAC 系统的密钥安全底座。
我建议你动手做一个小实验:用 Python 生成一对 RSA 密钥,然后用公钥加密一个 AES 密钥,再用对称密钥加密一段文字。最后用私钥解密。这个过程你亲手跑一遍,会比看十篇文章都管用。