使用 Python 实现 RSA 加密
昨晚快十二点,我正准备关电脑,有同事在群里丢了一句: “哥,Python 里能不能自己写个 RSA,加密个小东西,不想装一堆库。” 我一看这问题,困意没了,顺手就给他撸了个 demo。今天把思路完整说一遍,你照着抄一份就能跑起来。
先把 RSA 这事说人话
别一上来就一堆公式,先搞清楚它到底干嘛的。
RSA 其实就两件事:
生成一对钥匙:
公钥:给谁都行( n, e)私钥:自己藏好( n, d)加密 / 解密:
加密: cipher = m^e mod n解密: m = cipher^d mod n
m 是明文(先要变成数字),cipher 是密文,mod n 就是取模。
你可以粗暴理解成: “我造了一个巨大的锁(n),配两把钥匙,一个只能上锁(e),一个只能开锁(d),从数学上保证你拿上锁的那把反推不开另一把。”
钥匙到底怎么造出来?
理论版步骤是这样的,别慌,后面有完整 Python 代码:
随机挑两个足够大的素数 p和q算它们的乘积 n = p * q(这个就是那把“巨锁”)算欧拉函数 φ(n) = (p-1)*(q-1)选一个整数 e,要求1 < e < φ(n)且和φ(n)互质(一般直接用 65537)算出 d,满足:e * d ≡ 1 (mod φ(n)),也就是d是e在φ(n)里的“乘法逆元”
最后:
公钥: (n, e)私钥: (n, d)
核心难点其实就俩: 一个是“找素数”,一个是“求乘法逆元”。Python 都能帮你搞定。
先写一版“教学用”的 RSA(别拿去生产)
这版代码故意写简单一点,好看懂,但强度肯定不够,只适合你本地玩玩原理。
先搞几个数学小工具:判断素数、求最大公因数、扩展欧几里得、求逆元。
import random
from math import gcddefis_probable_prime(n: int) -> bool:
"""特别简陋的素数判断,只适合小一点的数,教学用"""
if n < 2:
returnFalse
if n in (2, 3):
returnTrue
if n % 2 == 0:
returnFalse
i = 3
while i * i <= n:
if n % i == 0:
returnFalse
i += 2
returnTrue
defgenerate_prime(bits: int) -> int:
"""生成一个 bits 位的素数(非常粗暴版本)"""
whileTrue:
# 生成一个 bits 位的随机奇数
n = random.getrandbits(bits) | 1
# 确保最高位是 1,保证位数够
n |= (1 << (bits - 1))
if is_probable_prime(n):
return n
defextended_gcd(a: int, b: int):
"""扩展欧几里得算法,返回 (g, x, y) 使得 a*x + b*y = g = gcd(a, b)"""
if b == 0:
return a, 1, 0
g, x1, y1 = extended_gcd(b, a % b)
x = y1
y = x1 - (a // b) * y1
return g, x, y
defmod_inverse(a: int, m: int) -> int:
"""求 a 在模 m 下的乘法逆元:a * x ≡ 1 (mod m)"""
g, x, _ = extended_gcd(a, m)
if g != 1:
raise ValueError("逆元不存在")
return x % m
有了这几个之后,生成 RSA 密钥就简单了:
defgenerate_keypair(bits: int = 512):
"""生成一对 RSA 密钥(教学用,bits 小而且没做严格素数测试)"""
# 1. 生成两个素数 p, q
p = generate_prime(bits // 2)
q = generate_prime(bits // 2)
while p == q:
q = generate_prime(bits // 2) n = p * q
phi = (p - 1) * (q - 1)
# 2. 选择 e,一般直接用 65537,如果不互质就往上加
e = 65537
if gcd(e, phi) != 1:
e = 3
while gcd(e, phi) != 1:
e += 2
# 3. 计算 d
d = mod_inverse(e, phi)
public_key = (n, e)
private_key = (n, d)
return public_key, private_key
再来把“字符串 ↔ 整数”封一下,因为 RSA 算法本质只认整数:
defbytes_to_int(b: bytes) -> int:
return int.from_bytes(b, byteorder="big")defint_to_bytes(n: int) -> bytes:
# 自动按需要的字节数转换
length = (n.bit_length() + 7) // 8
return n.to_bytes(length, byteorder="big")
加密 / 解密函数就很优雅了,用 Python 自带的 pow:
defrsa_encrypt(message: bytes, public_key):
n, e = public_key
m = bytes_to_int(message)if m >= n:
raise ValueError("明文太长了,换更大的 key 或者做分块 / 混合加密")
c = pow(m, e, n)
return c
defrsa_decrypt(cipher: int, private_key):
n, d = private_key
m = pow(cipher, d, n)
return int_to_bytes(m)
随便跑个小 demo:
if __name__ == "__main__":
public_key, private_key = generate_keypair(512) # 位数越大越安全,也越慢 msg = "hello, RSA with Python!"
plaintext = msg.encode("utf-8")
cipher = rsa_encrypt(plaintext, public_key)
print("密文(整数):", cipher)
decrypted = rsa_decrypt(cipher, private_key)
print("解密后:", decrypted.decode("utf-8"))
这套跑通之后,你脑子里对 RSA 的流程基本就立住了:
先造钥匙( generate_keypair)字符串转 bytes,再转 int pow(m, e, n)加密pow(c, d, n)解密
现实世界里怎么用?别自己造轮子
上面那版有几个致命问题:
素数测试太简陋 没有任何填充(padding),理论上能被各种数学攻击玩坏 密钥长度也不够大
实际项目里,一般都是用现成轮子,比如 PyCryptodome。大概长这样:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEPdefgenerate_keys_prod(bits: int = 2048):
key = RSA.generate(bits)
private_key_pem = key.export_key()
public_key_pem = key.publickey().export_key()
return public_key_pem, private_key_pem
defencrypt_with_lib(plaintext: bytes, public_key_pem: bytes) -> bytes:
pub_key = RSA.import_key(public_key_pem)
cipher = PKCS1_OAEP.new(pub_key)
return cipher.encrypt(plaintext)
defdecrypt_with_lib(ciphertext: bytes, private_key_pem: bytes) -> bytes:
pri_key = RSA.import_key(private_key_pem)
cipher = PKCS1_OAEP.new(pri_key)
return cipher.decrypt(ciphertext)
if __name__ == "__main__":
pub_pem, pri_pem = generate_keys_prod(2048)
msg = b"real world rsa with padding"
c = encrypt_with_lib(msg, pub_pem)
p = decrypt_with_lib(c, pri_pem)
print(p.decode("utf-8"))
你会发现,用库的时候,其实你根本不用管什么 p q φ(n),也不用手写逆元,所有危险的细节人家都封好了。
这时候自己实现 RSA 的意义就成了两件事:
学会它的工作原理,面试聊到能张嘴说清楚 偶尔跑个 tiny demo,或者在一些不那么严肃的环境里用一下(例如课堂作业、CTF 题目之类)
差不多就这样,你要是现在从头把上面的代码打一遍、跑通一遍,RSA 的整个链路就算过脑子了: “怎么选素数 → 怎么算 d → 用 pow 幂模运算 → 字节和整数之间怎么互转 → 实战里交给现成库”。
要是你哪天面试被问“用 Python 实现一下 RSA”,你直接说: “我能纯 Python 写一版教学实现,也能用 PyCryptodome 给你来一套带 OAEP 填充的工程化版本”,气场就不一样了。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB。