RSA算法实战:从数学原理到Python代码实现(附常见攻击防御指南)
RSA算法实战:从数学原理到Python代码实现(附常见攻击防御指南)
如果你在开发一个需要用户登录的Web应用,或者设计一套需要安全传输数据的系统,那么你迟早会遇到一个名字:RSA。这个诞生于1977年的算法,至今仍然是互联网安全的基石之一。它保护着我们的HTTPS连接、数字签名、软件更新,甚至加密货币交易。但很多开发者对它既熟悉又陌生——知道它很重要,却总觉得那些数学公式和密钥对生成过程像是一团迷雾。
这篇文章就是为你准备的。我们不打算只停留在“公钥加密,私钥解密”的口号式理解上,而是要亲手用Python代码,一步步实现RSA的完整流程。更重要的是,我们会深入那些教科书里常常一笔带过,但在实战中却至关重要的细节:为什么参数的选择如此关键?哪些常见的实现错误会让看似坚固的RSA变得不堪一击?攻击者究竟是如何利用这些漏洞的?理解了这些,你才能真正在项目中安全地使用RSA,而不是盲目地调用一个加密库。
1. 核心数学原理:为什么RSA是可靠的?
在写第一行代码之前,我们必须先搞清楚RSA背后的数学逻辑。放心,我们不会陷入过于抽象的纯数学推导,而是聚焦于那些直接影响算法实现和安全的几个核心概念。
1.1 模运算:一切的基础
RSA的整个加解密过程都发生在“模运算”的世界里。你可以把它想象成一个时钟。在模12的时钟上,15点就是3点,因为15除以12余3。我们写作 15 ≡ 3 (mod 12)。在RSA中,我们处理的是一个巨大的“时钟”,其模数是一个大整数N。
模运算有几个关键性质,它们保证了RSA的可逆性:
(a * b) mod N = [(a mod N) * (b mod N)] mod Na^e mod N可以通过重复的平方乘算法高效计算,即使e很大。
一个直观的例子:计算 7^13 mod 10。直接计算7的13次方是个天文数字,但利用模运算性质:
7^1 mod 10 = 7
7^2 mod 10 = 49 mod 10 = 9
7^4 mod 10 = (7^2)^2 mod 10 = 9^2 mod 10 = 81 mod 10 = 1
7^8 mod 10 = (7^4)^2 mod 10 = 1^2 mod 10 = 1
那么 7^13 = 7^8 * 7^4 * 7^1 ≡ 1 * 1 * 7 ≡ 7 (mod 10)
这个“平方-乘”算法是高效实现RSA幂运算的核心。
1.2 欧拉定理与密钥对的生成
RSA的安全性建立在“大整数分解难题”上,而其正确性则依赖于欧拉定理。对于任意与N互质的整数a,有: a^φ(N) ≡ 1 (mod N) 其中φ(N)是欧拉函数,表示小于N且与N互质的正整数的个数。如果N是两个质数p和q的乘积,那么φ(N) = (p-1)*(q-1)。
注意:这里的“互质”是关键。如果明文m与N不互质(即m是p或q的倍数),欧拉定理不能直接应用,但RSA仍然成立——这需要额外的证明,也是很多教科书省略的部分。在实际使用中,由于p和q极大,随机选到与N不互质的m的概率微乎其微,但理论完备性很重要。
密钥生成的核心步骤,就是找到两个特殊的指数e和d,使得: e * d ≡ 1 (mod φ(N)) 这意味着 e * d = k * φ(N) + 1 对于某个整数k成立。
那么加解密过程 (m^e)^d mod N 就变成了 m^(e*d) mod N = m^(k*φ(N)+1) mod N。根据欧拉定理,如果m与N互质,m^(φ(N)) ≡ 1 mod N,所以上式等于 m mod N。即使m与N不互质,通过中国剩余定理也能证明等式成立。这就是RSA能够正确解密的数学保证。
2. 手把手实现RSA密钥生成与加解密
理论足够清晰了,现在让我们打开Python环境,从零开始实现它。我们将使用Python内置的random、math库,以及处理大整数的能力。
2.1 生成大质数:Miller-Rabin素性测试
RSA的第一步是生成两个大质数p和q。如何判断一个上百位的大数是质数?确定性算法太慢,我们使用概率性的Miller-Rabin素性测试,它速度快且出错概率极低。
import random
import math
def is_prime(n, k=5):
"""使用Miller-Rabin算法进行素性测试"""
if n < 2: return False
for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]:
if n % p == 0:
return n == p
# 将n-1写成 d * 2^s 的形式
s, d = 0, n - 1
while d % 2 == 0:
s += 1
d //= 2
# 进行k轮测试
for _ in range(k):
a = random.randrange(2, n - 1)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(s - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def generate_large_prime(bit_length=512):
"""生成指定位数的大质数"""
while True:
# 确保是奇数,且高位为1以保证位数
candidate = random.getrandbits(bit_length) | (1 << (bit_length - 1)) | 1
if is_prime(candidate):
return candidate
这里k=5意味着进行5轮独立测试,每轮通过的概率至少3/4,5轮都误判为质数的概率小于(1/4)^5 ≈ 0.00098,对于实际应用已经足够安全。
2.2 完整的密钥生成流程
有了质数生成函数,我们现在可以生成完整的RSA密钥对:
def extended_gcd(a, b):
"""扩展欧几里得算法,返回(g, x, y)使得 a*x + b*y = g = gcd(a, b)"""
if a == 0:
return b, 0, 1
g, x1, y1 = extended_gcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return g, x, y
def mod_inverse(e, phi):
"""计算e关于模phi的模逆元d,即 e*d ≡ 1 (mod phi)"""
g, x, _ = extended_gcd(e, phi)
if g != 1:
raise ValueError("e和φ(n)必须互质")
return x % phi
def generate_rsa_keys(bit_length=1024):
"""生成RSA公钥和私钥"""
print("正在生成大质数p...")
p = generate_large_prime(bit_length // 2)
print("正在生成大质数q...")
q = generate_large_prime(bit_length // 2)
n = p * q
phi = (p - 1) * (q - 1)
# 选择公钥指数e,通常用65537(2^16+1)
e = 65537
while math.gcd(e, phi) != 1:
# 如果65537与φ(n)不互质(极罕见),尝试其他值
e = 65539
# 计算私钥指数d
d = mod_inverse(e, phi)
# 返回公钥(n, e)和私钥(n, d)
public_key = (n, e)
private_key = (n, d)
print(f"密钥生成完成!")
print(f"n的长度: {n.bit_length()} 位")
print(f"p: {p}")
print(f"q: {q}")
print(f"φ(n): {phi}")
print(f"公钥e: {e}")
print(f"私钥d: {d}")
return public_key, private_key, p, q
为什么常用e=65537? 这是一个精心选择的值:
- 二进制表示只有两个1(10000000000000001),使得平方乘运算非常高效
- 足够大,避免了低加密指数攻击
- 与大多数φ(n)互质
- 不是费马数,避免了某些特殊攻击
2.3 加解密与数字签名实现
现在实现核心的加解密函数。注意,RSA本身只能加密比N小的整数,所以实际应用中需要对长数据进行分块,并配合填充方案(如OAEP)。
def rsa_encrypt(message_int, public_key):
"""RSA加密:c = m^e mod n"""
n, e = public_key
if message_int >= n:
raise ValueError("明文必须小于n")
return pow(message_int, e, n)
def rsa_decrypt(cipher_int, private_key):
"""RSA解密:m = c^d mod n"""
n, d = private_key
return pow(cipher_int, d, n)
def rsa_sign(message_int, private_key):
"""RSA签名:s = m^d mod n"""
return rsa_decrypt(message_int, private_key) # 数学上相同
def rsa_verify(signature_int, message_int, public_key):
"""RSA验证:验证 m ≡ s^e mod n"""
n, e = public_key
return message_int == pow(signature_int, e, n)
# 演示完整流程
def demo_basic_rsa():
print("=== RSA完整流程演示 ===")
# 1. 生成密钥
public_key, private_key, p, q = generate_rsa_keys(bit_length=256) # 测试用256位,实际应用至少2048位
n, e = public_key
_, d = private_key
# 2. 准备明文(转换为整数)
message = "Hello RSA!"
message_bytes = message.encode('utf-8')
message_int = int.from_bytes(message_bytes, 'big')
print(f"\n原始消息: '{message}'")
print(f"消息整数: {message_int}")
print(f"消息长度: {len(message_bytes)} 字节")
# 3. 加密(确保m < n)
if message_int >= n:
print("警告:消息太长,需要分块加密")
# 实际应用中这里应该分块
return
cipher_int = rsa_encrypt(message_int, public_key)
print(f"\n加密后的密文: {cipher_int}")
# 4. 解密
decrypted_int = rsa_decrypt(cipher_int, private_key)
decrypted_bytes = decrypted_int.to_bytes((decrypted_int.bit_length() + 7) // 8, 'big')
decrypted_message = decrypted_bytes.decode('utf-8', errors='ignore').rstrip('\x00')
print(f"解密后的整数: {decrypted_int}")
print(f"解密后的消息: '{decrypted_message}'")
# 5. 签名与验证
print(f"\n=== 数字签名演示 ===")
signature = rsa_sign(message_int, private_key)
print(f"消息的签名: {signature}")
is_valid = rsa_verify(signature, message_int, public_key)
print(f"签名验证结果: {'有效' if is_valid else '无效'}")
# 尝试篡改后验证
tampered_message_int = message_int + 1
is_valid_tampered = rsa_verify(signature, tampered_message_int, public_key)
print(f"篡改后验证结果: {'有效(不应该!)' if is_valid_tampered else '无效(正确)'}")
if __name__ == "__main__":
demo_basic_rsa()
运行这段代码,你会看到RSA从密钥生成到加解密、签名的完整过程。但请注意,这个实现是教学用的简化版本,实际生产环境需要考虑更多因素:
| 生产环境考虑 | 教学版本缺失 | 风险 |
|---|---|---|
| 填充方案 | 无填充(教科书式RSA) | 确定性加密,易受选择明文攻击 |
| 密钥存储 | 内存中明文存储 | 私钥泄露风险 |
| 错误处理 | 简单异常抛出 | 可能通过错误信息泄露密钥信息 |
| 侧信道防护 | 无 | 可能通过时间、功耗分析恢复密钥 |
3. 常见攻击原理与防御实战
理解了RSA的基本实现后,我们来看看攻击者会从哪些角度突破它。只有知道攻击如何发生,才能更好地防御。
3.1 低加密指数攻击与广播攻击
攻击场景:当加密指数e很小时(比如e=3),如果明文m也比较小,使得 m^e < n,那么加密操作 c = m^e mod n 实际上就是 c = m^e(因为没超过模数n)。攻击者可以直接对密文c开e次方恢复明文。
def low_exponent_attack(ciphertext, e):
"""低加密指数攻击演示"""
import gmpy2
# 尝试直接开e次方
m, is_exact = gmpy2.iroot(ciphertext, e)
if is_exact:
return int(m)
return None
# 防御措施:总是使用适当的填充(如OAEP)
# 填充后,即使m很小,填充后的消息也会很大,确保 m^e > n
更隐蔽的是低加密指数广播攻击:如果同一个明文m用相同的低指数e加密,但不同的模数n1, n2, n3...发送给多个接收者,攻击者可以利用中国剩余定理恢复明文。
def chinese_remainder_theorem(n, a):
"""中国剩余定理求解同余方程组 x ≡ a_i (mod n_i)"""
from functools import reduce
def egcd(a, b):
if b == 0:
return 1, 0, a
x, y, g = egcd(b, a % b)
return y, x - (a // b) * y, g
total = 0
prod = reduce(lambda x, y: x * y, n)
for n_i, a_i in zip(n, a):
p = prod // n_i
_, inv, _ = egcd(p, n_i)
total += a_i * inv * p
return total % prod
def broadcast_attack(ciphertexts, moduli, e=3):
"""低加密指数广播攻击演示"""
# ciphertexts[i] = m^e mod moduli[i]
# 使用中国剩余定理计算 m^e mod (n1*n2*n3...)
m_e = chinese_remainder_theorem(moduli, ciphertexts)
# 现在 m^e < n1*n2*n3*...,可以直接开e次方
return low_exponent_attack(m_e, e)
防御策略:
- 永远不要使用e=3,至少使用e=65537
- 使用随机填充:PKCS#1 v1.5或OAEP填充确保每次加密的输入都不同且足够大
- 避免重复加密:同一消息用不同密钥加密时,确保填充随机化
3.2 共模攻击
攻击场景:两个用户使用相同的模数n但不同的指数e1、e2,并且加密了同一个明文m。如果e1和e2互质,攻击者可以恢复明文。
数学原理:存在整数s、t使得 e1*s + e2*t = 1(贝祖等式)。那么:
c1^s * c2^t ≡ (m^e1)^s * (m^e2)^t ≡ m^(e1*s + e2*t) ≡ m^1 ≡ m (mod n)
def common_modulus_attack(c1, c2, e1, e2, n):
"""共模攻击演示"""
import gmpy2
# 使用扩展欧几里得算法求s, t
g, s, t = extended_gcd(e1, e2)
if g != 1:
raise ValueError("e1和e2必须互质")
# 确保s和t为正数
if s < 0:
c1 = gmpy2.invert(c1, n)
s = -s
if t < 0:
c2 = gmpy2.invert(c2, n)
t = -t
# 计算 m = c1^s * c2^t mod n
part1 = pow(c1, s, n)
part2 = pow(c2, t, n)
m = (part1 * part2) % n
return m
防御策略:绝对不要共享模数n!每个用户必须生成自己独立的p和q。这是RSA使用中的铁律。
3.3 维纳攻击(小私钥d攻击)
攻击场景:当私钥d很小时(具体来说,d < n^0.25/3),攻击者可以从公钥(n, e)中恢复出私钥d。攻击基于连分数展开。
def wiener_attack(e, n):
"""维纳攻击:当d很小时从(e, n)恢复d"""
import math
# 将e/n展开为连分数
def continued_fraction(x, y):
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf
# 计算连分数的收敛子
def convergents(cf):
convergents = []
for i in range(1, len(cf) + 1):
num, den = 1, 0
for term in cf[:i][::-1]:
num, den = den + term * num, num
convergents.append((num, den))
return convergents
cf = continued_fraction(e, n)
convergents_list = convergents(cf)
for k, d in convergents_list:
if k == 0:
continue
# 检查是否满足 ed ≡ 1 (mod φ(n))
phi = (e * d - 1) // k
# 从φ(n)和n求解p, q: n = p*q, φ(n) = (p-1)(q-1)
# 解二次方程: x^2 - (n - φ(n) + 1)x + n = 0
b = n - phi + 1
discriminant = b * b - 4 * n
if discriminant >= 0:
sqrt_disc = int(math.isqrt(discriminant))
if sqrt_disc * sqrt_disc == discriminant:
p = (b + sqrt_disc) // 2
q = (b - sqrt_disc) // 2
if p * q == n:
return d, p, q
return None
# 防御:确保d足够大,通常要求d > n^0.292(更安全的界限)
# 实际中,使用标准密钥生成方法(如选择e=65537)自然会产生足够大的d
维纳攻击的实战意义:这个攻击告诉我们,不能为了解密速度快而故意选择小的d。有些实现可能会尝试使用小的d来加速解密操作,但这是极其危险的。
3.4 计时攻击与防御
这是一种侧信道攻击,不直接攻击数学结构,而是通过测量解密操作的时间来推断私钥信息。
# 有漏洞的实现(易受计时攻击)
def vulnerable_decrypt(c, d, n):
"""有漏洞的解密实现:执行时间与d的比特相关"""
result = 1
base = c
# 平方乘算法的朴素实现
while d > 0:
if d & 1: # 如果当前位是1
result = (result * base) % n # 这个乘法耗时
base = (base * base) % n # 平方操作
d >>= 1
return result
# 防御版本:恒定时间实现
def constant_time_decrypt(c, d, n):
"""抗计时攻击的解密实现"""
# 使用蒙哥马利幂模或 blinding 技术
# 这里展示一个简单的盲化技术
import random
# 盲化因子
r = random.randrange(2, n - 1)
r_inv = pow(r, -1, n) # Python 3.8+ 支持模逆
# 盲化密文
c_blinded = (c * pow(r, 65537, n)) % n # 假设e=65537
# 解密盲化后的密文
m_blinded = pow(c_blinded, d, n)
# 去除盲化
m = (m_blinded * r_inv) % n
return m
防御侧信道攻击的完整策略:
| 攻击类型 | 防御措施 |
|---|---|
| 计时攻击 | 恒定时间算法、操作盲化 |
| 功耗分析 | 随机化指数、掩码技术 |
| 故障攻击 | 冗余计算、结果验证 |
| 缓存攻击 | 恒定内存访问模式 |
4. 生产环境最佳实践与常见陷阱
在真实项目中直接使用我们上面实现的“教科书RSA”是危险的。以下是必须注意的关键点。
4.1 填充方案:为什么PKCS#1 v1.5不够安全
教科书RSA是确定性的:同样的明文总是产生同样的密文。这会导致许多攻击:
- 选择明文攻击:攻击者可以猜测明文并验证猜测
- 语义安全性缺失:不能隐藏明文的模式
OAEP填充是目前推荐的标准:
def rsa_oaep_encrypt(message, public_key, label=b''):
"""RSA-OAEP加密(简化示意,实际应使用密码学库)"""
import hashlib
import os
n, e = public_key
k = (n.bit_length() + 7) // 8 # 模数n的字节长度
h_len = 32 # SHA-256输出长度
m_len = len(message)
if m_len > k - 2 * h_len - 2:
raise ValueError("消息太长")
# 1. 生成随机种子
seed = os.urandom(h_len)
# 2. 使用MGF1生成数据掩码
def mgf1(seed, length):
# 掩码生成函数(简化)
counter = 0
output = b''
while len(output) < length:
C = seed + counter.to_bytes(4, 'big')
output += hashlib.sha256(C).digest()
counter += 1
return output[:length]
# 3. 构造数据块DB
lhash = hashlib.sha256(label).digest()
ps = b'\x00' * (k - m_len - 2 * h_len - 2)
DB = lhash + ps + b'\x01' + message
# 4. 应用掩码
db_mask = mgf1(seed, k - h_len - 1)
masked_db = bytes(x ^ y for x, y in zip(DB, db_mask))
# 5. 生成种子掩码
seed_mask = mgf1(masked_db, h_len)
masked_seed = bytes(x ^ y for x, y in zip(seed, seed_mask))
# 6. 编码后的消息
em = b'\x00' + masked_seed + masked_db
# 7. RSA加密
m = int.from_bytes(em, 'big')
c = pow(m, e, n)
return c.to_bytes((c.bit_length() + 7) // 8, 'big')
重要对比:
| 特性 | 教科书RSA | PKCS#1 v1.5 | OAEP |
|---|---|---|---|
| 确定性加密 | 是 | 部分随机化 | 完全随机化 |
| 抵抗选择明文攻击 | 否 | 较弱 | 强 |
| 抵抗适应性选择密文攻击 | 否 | 否 | 是(在随机预言模型下) |
| 标准推荐 | 绝不使用 | 遗留系统,逐步淘汰 | 新系统必须使用 |
4.2 密钥生成的安全参数
选择正确的密钥长度和参数至关重要:
def secure_key_generation_parameters():
"""安全密钥生成参数指南"""
return {
"密钥长度": {
"2023年最低要求": "2048位",
"新系统推荐": "3072位",
"长期安全(10年以上)": "4096位",
"绝对不安全的长度": "1024位及以下"
},
"质数p和q": {
"长度": "应为密钥长度的一半",
"差值": "|p-q|应足够大,避免费马分解",
"强质数": "推荐但不强制,(p-1)和(q-1)应有大质因子",
"生成算法": "使用安全的随机数生成器"
},
"公钥指数e": {
"标准值": "65537 (0x10001)",
"其他可接受值": "3(不推荐)、17、19等",
"要求": "必须与φ(n)互质"
},
"私钥指数d": {
"最小长度": "应大于n^0.292(防御维纳攻击)",
"生成方式": "从e和φ(n)计算,不应人为选择"
}
}
4.3 实际部署中的常见错误
我在安全审计中经常看到这些错误:
- 硬编码密钥或弱随机数:
# 错误示例
random.seed(12345) # 可预测的随机数
p = generate_large_prime() # 如果随机数种子固定,p总是相同
# 正确做法
import secrets
random.seed(secrets.token_bytes(32)) # 使用密码学安全的随机数
- 错误的重用:
# 危险:同一密钥既用于加密又用于签名
def rsa_encrypt_and_sign(data, key):
encrypted = rsa_encrypt(data, key) # 使用私钥加密?不!
signature = rsa_sign(data, key) # 使用私钥签名
return encrypted, signature
# 正确:加密用公钥,签名用私钥
def rsa_encrypt_and_sign_proper(data, public_key, private_key):
encrypted = rsa_encrypt(data, public_key) # 公钥加密
signature = rsa_sign(data, private_key) # 私钥签名
return encrypted, signature
- 缺乏完整性保护:
# 攻击者可以篡改密文,解密得到乱码但系统可能不检测
ciphertext = receive_from_network()
plaintext = rsa_decrypt(ciphertext, private_key)
process(plaintext) # 如果密文被篡改,这里可能崩溃或产生错误输出
# 正确:加密时包含完整性校验
def encrypt_with_integrity(data, public_key):
# 添加HMAC或使用认证加密模式
hmac_key = secrets.token_bytes(32)
encrypted_data = rsa_encrypt(data + hmac(data, hmac_key), public_key)
encrypted_key = rsa_encrypt(hmac_key, public_key)
return encrypted_data, encrypted_key
4.4 性能优化与大型系统考虑
当需要在大型系统中使用RSA时,性能成为关键考虑:
class OptimizedRSA:
"""优化版RSA实现,使用中国剩余定理加速解密"""
def __init__(self, p, q, e=65537):
self.p = p
self.q = q
self.n = p * q
self.e = e
# 预计算CRT参数
self.dp = pow(e, -1, p - 1) # d mod (p-1)
self.dq = pow(e, -1, q - 1) # d mod (q-1)
self.qinv = pow(q, -1, p) # q的模p逆元
def decrypt_crt(self, c):
"""使用CRT加速解密,比直接计算 c^d mod n 快4倍"""
# 分别在模p和模q下解密
mp = pow(c % self.p, self.dp, self.p)
mq = pow(c % self.q, self.dq, self.q)
# 使用CRT组合结果
h = (self.qinv * (mp - mq)) % self.p
m = mq + h * self.q
return m
def encrypt(self, m):
"""加密(通常不需要优化,客户端操作)"""
if m >= self.n:
raise ValueError("明文过大")
return pow(m, self.e, self.n)
# 性能对比
def performance_comparison():
import time
# 生成2048位密钥
p = generate_large_prime(1024)
q = generate_large_prime(1024)
n = p * q
e = 65537
d = pow(e, -1, (p-1)*(q-1))
rsa_crt = OptimizedRSA(p, q, e)
# 测试消息
m = 123456789
c = pow(m, e, n)
# 标准解密
start = time.time()
for _ in range(100):
pow(c, d, n)
std_time = time.time() - start
# CRT解密
start = time.time()
for _ in range(100):
rsa_crt.decrypt_crt(c)
crt_time = time.time() - start
print(f"标准解密时间: {std_time:.4f}秒")
print(f"CRT解密时间: {crt_time:.4f}秒")
print(f"加速比: {std_time/crt_time:.2f}倍")
实际部署建议:
-
密钥类型选择:
- 短期会话密钥:考虑使用ECDSA(更短密钥,更快运算)
- 长期身份证书:RSA 3072位或以上
- 后量子安全:开始规划迁移到抗量子算法
-
混合加密系统:
def hybrid_encrypt(data, rsa_public_key): # 1. 生成随机的AES密钥 aes_key = secrets.token_bytes(32) # AES-256 # 2. 用AES加密数据 cipher = AES.new(aes_key, AES.MODE_GCM) ciphertext, tag = cipher.encrypt_and_digest(data) # 3. 用RSA加密AES密钥 encrypted_key = rsa_encrypt( aes_key, rsa_public_key, padding='OAEP' # 必须使用填充! ) return { 'encrypted_key': encrypted_key, 'ciphertext': ciphertext, 'nonce': cipher.nonce, 'tag': tag } -
密钥轮换策略:
- 加密密钥:定期轮换(如每90天)
- 签名密钥:生命周期更长,但需要明确的吊销机制
- 根证书:可能数年不换,但必须有应急计划
RSA算法虽然已有四十多年历史,但它仍然是现代密码学的基石。理解其原理、实现细节和潜在陷阱,对于构建安全系统至关重要。真正的安全不在于使用最复杂的算法,而在于正确使用经过时间检验的算法。每次实现密码学功能时,问问自己:我是否真的需要自己实现?使用成熟的库(如Python的cryptography)通常是更安全的选择。但当理解背后的原理成为必须时,希望这篇文章能成为你探索之路上的可靠指南。
更多推荐


所有评论(0)