RSA签名验签
# RSA签名验签
非对称加密双璧——公钥加密/验签、私钥解密/签名。API 签名验证、SSH 密钥管理、软件许可。
# 一、RSA 核心概念
非对称 = 两把钥匙
私钥 (Private Key) —— 保密,用来签名和解密
公钥 (Public Key) —— 公开,用来验签和加密
签名流程:
发送方用「私钥」签名 → 接收方用「公钥」验签
→ 证明"内容来自私钥持有者 + 未被篡改"
RSA 密钥长度:2048(最低)/ 3072 / 4096 位
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 二、Python — cryptography 库
pip install cryptography
1
# 2.1 密钥生成
#!/usr/bin/env python3
"""生成 RSA 密钥对并保存为 PEM 文件"""
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# ---- 生成密钥 ----
private_key = rsa.generate_private_key(
public_exponent=65537, # 标准公钥指数
key_size=2048, # 2048 位(>=2048)
)
# ---- 导出私钥(PEM 格式)----
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption() # 可加密
)
with open('private.pem', 'wb') as f:
f.write(private_pem)
print("✅ 私钥: private.pem")
# ---- 导出公钥 ----
public_key = private_key.public_key()
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public.pem', 'wb') as f:
f.write(public_pem)
print("✅ 公钥: public.pem")
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
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
# 2.2 签名与验签
#!/usr/bin/env python3
"""RSA 签名 + 验签——完整流程"""
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64
# ---- 加载密钥 ----
def load_private_key(path='private.pem'):
with open(path, 'rb') as f:
return serialization.load_pem_private_key(f.read(), password=None)
def load_public_key(path='public.pem'):
with open(path, 'rb') as f:
return serialization.load_pem_public_key(f.read())
# ---- 签名 ----
def sign(message: bytes, private_key) -> str:
"""用私钥签名,返回 Base64 签名"""
signature = private_key.sign(
message,
padding.PSS( # PSS = 推荐的填充方案
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return base64.b64encode(signature).decode()
# ---- 验签 ----
def verify(message: bytes, signature_b64: str, public_key) -> bool:
"""用公钥验证签名"""
try:
signature = base64.b64decode(signature_b64)
public_key.verify(
signature,
message,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
return True
except Exception:
return False
# ---- 演示 ----
if __name__ == '__main__':
priv = load_private_key()
pub = load_public_key()
msg = b'{"order_id":"12345","amount":99.99}'
sig = sign(msg, priv)
print(f"签名: {sig[:60]}...")
ok = verify(msg, sig, pub)
print(f"验签通过: {ok}") # True
# 篡改检测
tampered = b'{"order_id":"12345","amount":0.01}'
print(f"篡改检测: {verify(tampered, sig, pub)}") # False
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
57
58
59
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
57
58
59
# 2.3 RSA 加密/解密(小数据)
#!/usr/bin/env python3
"""RSA 加密——只适合小数据(<密钥长度-填充),通常配合 AES 使用"""
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
import base64
def rsa_encrypt(plaintext: bytes, public_key) -> str:
"""公钥加密——用 OAEP 填充"""
ct = public_key.encrypt(
plaintext,
padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
)
return base64.b64encode(ct).decode()
def rsa_decrypt(ciphertext_b64: str, private_key) -> bytes:
"""私钥解密"""
ct = base64.b64decode(ciphertext_b64)
return private_key.decrypt(
ct,
padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
)
# 典型用途:RSA 加密 AES 密钥(密钥交换),
# 大数据用 AES 加密
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
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
# 三、Shell — OpenSSL RSA
#!/bin/bash
# ===== 生成 RSA 私钥 =====
openssl genrsa -out private.pem 2048
# ===== 提取公钥 =====
openssl rsa -in private.pem -pubout -out public.pem
# ===== 签名 =====
echo -n "message to sign" | openssl dgst -sha256 -sign private.pem \
| base64 > signature.txt
# ===== 验签 =====
openssl dgst -sha256 -verify public.pem -signature \
<(base64 -d signature.txt) <<< "message to sign"
# 输出:Verified OK ← 签名有效
# ===== RSA 加密/解密 =====
# 公钥加密(OpenSSL 3.x 用 pkeyutl)
openssl pkeyutl -encrypt -pubin -inkey public.pem \
-in secret.txt -out secret.txt.enc
# 私钥解密
openssl pkeyutl -decrypt -inkey private.pem \
-in secret.txt.enc -out secret_decrypted.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 四、实战:API 请求签名工具
#!/usr/bin/env python3
"""
为 HTTP API 请求添加 RSA 签名——防篡改 + 身份认证
客户端:用私钥签名请求体
服务端:用公钥验证签名
"""
import json, base64, hashlib
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
# ---- 加载密钥 ----
def load_key(path, is_private=False):
with open(path, 'rb') as f:
data = f.read()
if is_private:
return serialization.load_pem_private_key(data, password=None)
return serialization.load_pem_public_key(data)
# ---- 客户端:生成带签名的请求 ----
def sign_request(body: dict, private_key_path: str) -> dict:
"""把请求体序列化→签名→附加到 headers"""
payload = json.dumps(body, separators=(',', ':')).encode()
priv = load_key(private_key_path, is_private=True)
signature = priv.sign(
payload,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
return {
'body': body,
'headers': {
'X-Signature': base64.b64encode(signature).decode(),
'Content-Type': 'application/json'
}
}
# ---- 服务端:验证签名 ----
def verify_request(body: dict, signature_b64: str, public_key_path: str) -> bool:
"""验证请求签名"""
payload = json.dumps(body, separators=(',', ':')).encode()
pub = load_key(public_key_path)
try:
pub.verify(
base64.b64decode(signature_b64),
payload,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH),
hashes.SHA256()
)
return True
except Exception:
return False
# ---- 演示 ----
if __name__ == '__main__':
req = sign_request(
{"order_id": "12345", "amount": 99.99},
'private.pem'
)
print(f"签名请求: {json.dumps(req, indent=2)}")
ok = verify_request(req['body'], req['headers']['X-Signature'], 'public.pem')
print(f"服务端验签: {'✅ 通过' if ok else '❌ 失败'}")
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
57
58
59
60
61
62
63
64
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
57
58
59
60
61
62
63
64
上次更新: 2026/06/17, 12:47:39