一个困扰四年的python公钥解密的问题

在这篇文章中,介绍了如何使用飞天信诚的加密狗设备进行加密解密。
但是文章的最后,有一个遗憾,就是需要借助官方给的php代码来进行解密,自己尝试了几次用python实现,但是总是失败,今天终于发现了解决之道。

需要安装一个pycryptodome。

1
pip3 install pycryptodome -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

最后的代码:

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
50
51
52
53
54
55
56
def check_rsa_code(encrypted_str: str) -> str:
"""
使用公钥解密(等效于 PHP 的 openssl_public_decrypt)
适配 PKCS#1 v1.5 两种 padding 格式 (0x01/0x02)

:param encrypted_str: URL 编码 + Base64 编码的密文
:return: 解密后的明文字符串
"""
# === 1. RSA 公钥 ===
public_key_pem = """-----BEGIN PUBLIC KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxx
-----END PUBLIC KEY-----"""

# === 2. 处理输入:urldecode + base64 decode ===
# PHP 里是 rawurldecode(urlencode(urldecode(...))),等效这里直接 unquote 一次
encrypted_str = unquote(encrypted_str)
encrypted_bytes = base64.b64decode(encrypted_str)

# === 3. 导入 RSA 公钥,并获取模数 (n) 和指数 (e) ===
rsa_key = RSA.import_key(public_key_pem)
n = rsa_key.n
e = rsa_key.e

# === 4. 转换为整数,执行 RSA 运算 (cipher^e mod n) ===
cipher_int = int.from_bytes(encrypted_bytes, byteorder="big")
plain_int = pow(cipher_int, e, n)

# === 5. 转回字节数组,长度按密钥大小补齐 ===
k = (rsa_key.size_in_bits() + 7) // 8
plain_bytes = plain_int.to_bytes(k, byteorder="big")

# === 6. 处理 PKCS#1 v1.5 Padding ===
# 格式有两种:
# - 00 01 FF FF ... FF 00 <data>
# - 00 02 xx xx ... xx 00 <data>
if len(plain_bytes) > 2 and plain_bytes[0] == 0x00:
if plain_bytes[1] == 0x01: # 类型 1:FF 填充
sep_idx = plain_bytes.find(b"\x00", 2)
if sep_idx > 0:
plain_bytes = plain_bytes[sep_idx+1:]
elif plain_bytes[1] == 0x02: # 类型 2:随机字节填充
sep_idx = plain_bytes.find(b"\x00", 2)
if sep_idx > 0:
plain_bytes = plain_bytes[sep_idx+1:]
else:
# 如果不是标准 padding,就去掉前导 0
plain_bytes = plain_bytes.lstrip(b"\x00")
else:
plain_bytes = plain_bytes.lstrip(b"\x00")

# === 7. 返回解码结果(明文是 UTF-8 字符串,如 "3238095759") ===
return plain_bytes.decode("utf-8")

这样就解决了这一问题,完全不再依赖PHP了,真好!
困扰了四年的问题,就这样解决了~

Notice: 正常情况下,这里会有一个基于utteranc.es的留言系统,如果看不到,可能要想想办法才能看到。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2012 - 2025 tiaobug.com All Rights Reserved.

鲁ICP备2024124237号-1