316 lines
7.6 KiB
Markdown
316 lines
7.6 KiB
Markdown
# RSA 加密与签名工具库技术文档
|
||
|
||
本项目提供一个基于 Python 的 RSA 加密、解密、签名和验证功能的轻量级工具模块,使用 `PyCryptodome` 库实现核心密码学操作。支持密钥读取、生成、数据加密/解密以及数字签名/验证等功能。
|
||
|
||
---
|
||
|
||
## 📦 模块依赖
|
||
|
||
```python
|
||
import codecs
|
||
from Crypto.PublicKey import RSA
|
||
from Crypto.Cipher import PKCS1_OAEP
|
||
from Crypto.Cipher import PKCS1_v1_5 as V1_5 # 注意:原代码拼写错误已修正
|
||
from Crypto.Signature import PKCS1_v1_5
|
||
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
|
||
from Crypto import Random
|
||
from base64 import b64encode, b64decode
|
||
```
|
||
|
||
> ⚠️ **注意**:需要安装 `pycryptodome` 包:
|
||
>
|
||
> ```bash
|
||
> pip install pycryptodome
|
||
> ```
|
||
|
||
---
|
||
|
||
## 🔧 全局变量
|
||
|
||
| 变量名 | 类型 | 默认值 | 说明 |
|
||
|--------|--------|--------------|------|
|
||
| `hash` | string | `"SHA-256"` | 当前使用的哈希算法名称,用于签名与验证(全局状态) |
|
||
|
||
> ❗ 警告:该变量为全局共享状态,在多线程或并发场景中可能导致不可预期行为,建议重构为参数传递方式。
|
||
|
||
---
|
||
|
||
## 📚 函数说明
|
||
|
||
### `readPublickey(fname) → RSA.RsaKey or None`
|
||
|
||
从文件读取公钥。
|
||
|
||
#### 参数:
|
||
- `fname` (str): 公钥文件路径(PEM 格式)
|
||
|
||
#### 返回值:
|
||
- 成功时返回 `RSA.RsaKey` 对象
|
||
- 失败时返回 `None`
|
||
|
||
#### 示例:
|
||
```python
|
||
pub_key = readPublickey('public.pem')
|
||
```
|
||
|
||
#### 文件格式要求:
|
||
```
|
||
-----BEGIN PUBLIC KEY-----
|
||
...Base64编码内容...
|
||
-----END PUBLIC KEY-----
|
||
```
|
||
|
||
---
|
||
|
||
### `readPrivatekey(fname, pwd) → RSA.RsaKey or None`
|
||
|
||
从文件读取带密码保护的私钥。
|
||
|
||
#### 参数:
|
||
- `fname` (str): 私钥文件路径(PEM 格式)
|
||
- `pwd` (str 或 bytes): 解密私钥的密码
|
||
|
||
#### 返回值:
|
||
- 成功时返回 `RSA.RsaKey` 对象
|
||
- 失败时返回 `None`
|
||
|
||
#### 示例:
|
||
```python
|
||
priv_key = readPrivatekey('private.pem', 'mysecretpassword')
|
||
```
|
||
|
||
#### 文件格式要求:
|
||
```
|
||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||
...Base64编码内容...
|
||
-----END ENCRYPTED PRIVATE KEY-----
|
||
```
|
||
|
||
> ✅ 支持 AES 加密的 PEM 私钥(如 OpenSSL 生成)
|
||
|
||
---
|
||
|
||
### `newkeys(keysize) → (public_key, private_key)`
|
||
|
||
生成新的 RSA 密钥对。
|
||
|
||
#### 参数:
|
||
- `keysize` (int): 密钥长度(推荐 2048 或 4096)
|
||
|
||
#### 返回值:
|
||
- 元组 `(public_key: RsaKey, private_key: RsaKey)`
|
||
|
||
#### 示例:
|
||
```python
|
||
pub, priv = newkeys(2048)
|
||
```
|
||
|
||
> 💡 使用安全随机数生成器 (`Random.new().read`) 确保密钥安全性。
|
||
|
||
---
|
||
|
||
### `importKey(externKey) → RSA.RsaKey`
|
||
|
||
从字符串导入密钥(支持公钥或私钥)。
|
||
|
||
#### 参数:
|
||
- `externKey` (str 或 bytes): PEM 编码的密钥字符串
|
||
|
||
#### 返回值:
|
||
- `RSA.RsaKey` 对象
|
||
|
||
#### 示例:
|
||
```python
|
||
key_str = open("public.pem").read()
|
||
key = importKey(key_str)
|
||
```
|
||
|
||
---
|
||
|
||
### `getpublickey(priv_key) → RSA.RsaKey`
|
||
|
||
从私钥对象提取对应的公钥。
|
||
|
||
#### 参数:
|
||
- `priv_key` (RSA.RsaKey): 私钥对象
|
||
|
||
#### 返回值:
|
||
- 对应的公钥对象 (`RsaKey`)
|
||
|
||
#### 示例:
|
||
```python
|
||
pub_key = getpublickey(priv_key)
|
||
```
|
||
|
||
---
|
||
|
||
### `encrypt(message, pub_key) → bytes`
|
||
|
||
使用公钥加密消息(采用 PKCS#1 OAEP 填充,推荐用于新系统)。
|
||
|
||
#### 参数:
|
||
- `message` (bytes): 明文数据(必须是字节串)
|
||
- `pub_key` (RSA.RsaKey): 公钥对象
|
||
|
||
#### 返回值:
|
||
- 加密后的密文字节串 (`bytes`)
|
||
|
||
#### 示例:
|
||
```python
|
||
ciphertext = encrypt(b"Hello World", pub_key)
|
||
```
|
||
|
||
> 🔐 安全提示:OAEP 是抗适应性选择密文攻击的安全填充模式。
|
||
|
||
---
|
||
|
||
### `decrypt(ciphertext, priv_key) → bytes`
|
||
|
||
尝试使用私钥解密数据。优先使用 OAEP,失败后自动降级到 v1.5 填充。
|
||
|
||
#### 参数:
|
||
- `ciphertext` (bytes): 密文数据
|
||
- `priv_key` (RSA.RsaKey): 私钥对象
|
||
|
||
#### 返回值:
|
||
- 解密后的明文字节串 (`bytes`)
|
||
- 若两种模式均失败,则抛出异常并打印错误信息
|
||
|
||
#### 异常处理逻辑:
|
||
1. 首先尝试 `PKCS1_OAEP`
|
||
2. 失败则尝试 `PKCS1_v1_5`(兼容旧系统)
|
||
3. 打印异常详情(调试用)
|
||
|
||
> ⚠️ 自动降级可能带来安全隐患,请确保你知道为何使用 v1.5。
|
||
|
||
---
|
||
|
||
### `sign(message, priv_key, hashAlg="SHA-256") → bytes`
|
||
|
||
对消息进行数字签名(使用 PKCS#1 v1.5 签名方案)。
|
||
|
||
#### 参数:
|
||
- `message` (bytes): 待签名的消息
|
||
- `priv_key` (RSA.RsaKey): 私钥
|
||
- `hashAlg` (str): 哈希算法,可选值:
|
||
- `"SHA-512"`
|
||
- `"SHA-384"`
|
||
- `"SHA-256"`(默认)
|
||
- `"SHA-1"`
|
||
- `"MD5"`
|
||
|
||
#### 返回值:
|
||
- 签名结果(原始字节流)
|
||
|
||
#### 内部逻辑:
|
||
根据 `hashAlg` 设置全局 `hash` 变量,并选择相应哈希函数计算摘要后签名。
|
||
|
||
#### 示例:
|
||
```python
|
||
signature = sign(b"important data", priv_key, "SHA-256")
|
||
```
|
||
|
||
> ❌ 不推荐使用 SHA-1 或 MD5,仅保留向后兼容。
|
||
|
||
---
|
||
|
||
### `verify(message, signature, pub_key) → bool`
|
||
|
||
验证数字签名的有效性。
|
||
|
||
#### 参数:
|
||
- `message` (bytes): 原始消息
|
||
- `signature` (bytes): 签名数据
|
||
- `pub_key` (RSA.RsaKey): 公钥对象
|
||
|
||
#### 返回值:
|
||
- `True`:签名有效
|
||
- `False`:签名无效或验证失败
|
||
|
||
#### 注意事项:
|
||
- 使用与 `sign()` 相同的全局 `hash` 变量决定哈希算法
|
||
- 必须保证签名时和验证时使用相同的哈希算法
|
||
|
||
#### 示例:
|
||
```python
|
||
is_valid = verify(b"important data", signature, pub_key)
|
||
if is_valid:
|
||
print("✅ 签名验证通过")
|
||
else:
|
||
print("❌ 签名无效")
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 主程序示例(测试用途)
|
||
|
||
```python
|
||
if __name__ == '__main__':
|
||
cipher = """WaMlLEYnhBk+kTDyN/4OJmQf4ccNdk6USgtKpb7eHsYsotq4iyXi3N5hB1E/PqrPSmca1AMDLUcumwIrLeGLT9it3eTBQgl1YQAsmPxa6lF/rDOZoLbwD5sJ6ab/0/fuM4GbotqN5/d0MeuOSELoo8cFWw+7XpRxn9EMYnw5SzsjDQRWxXjZptoaGa/8pBBkDmgLqINif9EWV+8899xqTd0e9w1Gqb7wbt/elRNVBpgsSuSZb+dtBlvNUjuTms8BETSRai5vhXetK26Ms8hrayiy38n7wwEKE8fZ9iFzLtwa6xbhD5KudWbKJFFOZAfpzWttGMwWlISbGQigcW4+Bg=="""
|
||
|
||
key = readPrivatekey('d:/dev/mecp/conf/RSA.private.key', 'ymq123')
|
||
t = decrypt(b64decode(cipher), key) # 注意:cipher 是 Base64 字符串,需先解码
|
||
print('t=', t)
|
||
```
|
||
|
||
> 🔍 **Bug修复建议**:原文中的 `cipher` 是 Base64 字符串,但直接传给 `decrypt()` 会出错。应先用 `b64decode()` 转换为字节。
|
||
|
||
✅ 正确调用方式:
|
||
```python
|
||
t = decrypt(b64decode(cipher), key)
|
||
```
|
||
|
||
---
|
||
|
||
## 🛠️ 使用建议与注意事项
|
||
|
||
| 项目 | 建议 |
|
||
|------|------|
|
||
| 🔐 填充模式 | 推荐使用 `OAEP` 进行加密;`v1.5` 仅用于兼容老系统 |
|
||
| 📏 密钥长度 | 至少 2048 位,推荐 4096 以增强长期安全性 |
|
||
| 🧼 全局变量 `hash` | 存在并发风险,建议改为函数参数传递 |
|
||
| 🧑💻 错误处理 | `decrypt()` 中的 `print(e)` 应替换为日志记录 |
|
||
| 🔄 签名一致性 | `sign` 和 `verify` 必须使用相同哈希算法 |
|
||
| 🧩 Base64 编码 | 输入输出建议封装编解码层以便外部使用字符串传输 |
|
||
|
||
---
|
||
|
||
## 📎 示例:完整加解密流程
|
||
|
||
```python
|
||
# 生成密钥对
|
||
pub, priv = newkeys(2048)
|
||
|
||
# 加密
|
||
msg = b"Secret message"
|
||
ciphertext = encrypt(msg, pub)
|
||
|
||
# 解密
|
||
plaintext = decrypt(ciphertext, priv)
|
||
print(plaintext) # b'Secret message'
|
||
|
||
# 签名 & 验证
|
||
sig = sign(msg, priv, "SHA-256")
|
||
valid = verify(msg, sig, pub)
|
||
print(valid) # True
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 参考资料
|
||
|
||
- [PyCryptodome 官方文档](https://pycryptodome.readthedocs.io/)
|
||
- RFC 3447 – PKCS #1 v2.1
|
||
- NIST FIPS 180-4 – 安全哈希标准
|
||
|
||
---
|
||
|
||
## 📝 版本信息
|
||
|
||
- 创建日期:2025年4月5日
|
||
- 作者:AI 助手
|
||
- 许可:MIT(假设)
|
||
|
||
---
|
||
|
||
> ✅ 提示:将此 `.py` 文件保存为 `rsa_utils.py`,可通过 `import rsa_utils` 在其他模块中复用。 |