Python 第三方模块之 cryptography - 加密解密

1、背景

cryptography 库是一个强大的 Python 加密库,提供了对加密算法和协议的高层和低层访问。它是用来实现数据加密、签名、密钥管理等功能的。以下是一些常见用法的详解。

2、安装

1
pip install cryptography

3、对称加密

对称加密是指加密和解密使用相同的密钥。Fernet 是 cryptography 库中提供的对称加密类。

示例:使用 Fernet 进行加密和解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from cryptography.fernet import Fernet

# 生成密钥
key = Fernet.generate_key()
cipher_suite = Fernet(key)

# 加密数据
plain_text = b"Hello, World!"
cipher_text = cipher_suite.encrypt(plain_text)
print(f"Cipher Text: {cipher_text}")

# 解密数据
decrypted_text = cipher_suite.decrypt(cipher_text)
print(f"Decrypted Text: {decrypted_text.decode('utf-8')}")

"""
Cipher Text: b'gAAAAABm1SuaILy-UxWXz4hpBxVmrFlrZ1pPGJPTLZNMqqp38qwcHjTX16BbWasLUvhkCCL485jSfCTZ0HQWUptYniUX6EAwQA=='
Decrypted Text: Hello, World!
"""

4、非对称加密

非对称加密使用一对密钥:公钥和私钥。cryptography 库中提供了 RSA 算法的实现。

示例:使用 RSA 进行加密和解密

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
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes

# 生成 RSA 密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)

public_key = private_key.public_key()

# 加密数据
message = b"Hello, RSA!"
cipher_text = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"Cipher Text: {cipher_text}")

# 解密数据
decrypted_text = private_key.decrypt(
cipher_text,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"Decrypted Text: {decrypted_text.decode('utf-8')}")

"""
Cipher Text: b';2\xdf\xc80%\x19\xf4\x95\xbc<\xfa\xb7\xe7\'\x94\xed!\xd0\x17\xdf\xb7c\xe3Q\xd6caEU\xc6 g(r\xa1-\xa1*y\xf9\xac\x1d"\xa7F\xd8J[w6Ly7\x06m5\xd2\x1d\xa9\x87A_\xb6\n\x078\xfb\xcc\x80\xf8\xad#\x86\x88\x97\xd8j\x17s\xf4\xc0\t\xc2\xb24i\x1c\x1c\xc1\x96\r\xac2\xd9\xaf\xfal\xa2n\xac\xb7\xa4C\xb6\xac\x1a,\xec\x94\xfa\x9b\xf2\xca\x030v#\x00\xdc\xd6\xa7\xf8:o\x1eK\xc3L\xf4\x88B\ru\xa1\x8b\xc0//@\x0c\x84\x047\xa7\xa1\x1f\x82\xe2\xdd\x0f\tT\x9df\xb5\x98n\xe2\xb8\x0e\x8c\xcb\x95T\x93SjX\xaf;97\xad\x19\x9el\x8d\xa3\xacr\xe0tT\x8c\x18\xa2!\x15\xd8\xf3\x8e\x88{\x01\x96[\xfa\xff\xc70\xef\xfe\xa5C\x01b?\xe7\xa9\xbdLw\xa8\x05^?\xbe\x94p\xa4#j\xeb\xef\xfb\xac\x92\xca|}`\xce\xe6\x7f&\xd2h\x19\x19K\x91\xde\xc5\xce\xdc\x91\xb7|\xaf\x9ef\x8d\xc8\xb7\xaf'
Decrypted Text: Hello, RSA!
"""

5、数字签名

数字签名可以用来验证消息的完整性和来源。可以使用非对称加密的私钥进行签名,使用公钥进行验证。

示例:使用 RSA 进行数字签名

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding

"""
代码解释:
生成密钥对:

使用 RSA 算法生成一个私钥和相应的公钥。这里的公钥和私钥都是 2048 位的。
签名消息:

用私钥对一条消息进行签名,使用 PSS 填充和 SHA-256 哈希算法。
验证签名:

使用公钥验证签名是否有效。若有效,则证明消息在签名前未被篡改。
"""
# 生成密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)

# 获取公钥
public_key = private_key.public_key()

# 保存私钥
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)

# 打印私钥
print("Private Key:")
print(private_pem.decode())

# 签名消息
message = b"Hello, this is a secret message."
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)

# 打印签名
print("Signature:")
print(signature.hex())

# 验证签名
try:
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
print("Signature is valid.")
except Exception as e:
print("Signature is invalid:", str(e))
"""
Private Key:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAq3zlAQlVy/4/S+St69LJubb/OUfZLdI1L+ERXfwZOfro0sWU
DXhkx7ZDAyZPG72eIcYjl4yOlUM51Ih+1ukFDwC+iKPIT8Dc2nBCP5gpTTNOdb+Y
U0LLaCzqZa5QBivl6Su3y8G2k9pACiENGPWjw7ph/Ev+0UFxk/Zu7xV85XZPUQZE
xdSHrSAvLI7vcjQPC2PURNf7NwYRw0QFe/7/xah4omu1YLO6BllJDahxcnp/MgYd
/do/i0R34s4IX0mhK2E0napaBI5DsAmIt81juO3RJVXpxbe2+RIrrD53UJVSt5qg
R+y1xjT2gCTn8pIDkx3yJHX6Iftq0hPFvh3gGwIDAQABAoIBAA8/L/yFxXSOeuHh
skWaJ1L/6QpBOpcp2OO/Dt8R0nZcD3Dp8y4NaxHHQTGu/sVDIyOdQmiE19/VKyJG
g9cnaZ/ksOFk2nhIzU9jQBNNEThVcZ+OH4TLlq/WSn2Bit45mfE/cKceO9WXx9jU
CR4+fMcis6KNvoQBXbqfjvZqgoEY0bTTmhisEslpG+utKxhjKgVlFlFTb7RxTci7
OTyOpstpfvx1ZgQz+ck6rR6MhonUvHObvEpxHLIBADp+ND+8K29mMSl0/YkGJRLO
Y4NuBn2JP+x3KkFjGdRr27z5tqODLyNXNEj9o1WuiH20ssrSvuHc4PDd8r2lGAsG
xB9WpzUCgYEA7bE3Wh5R1S1RzhGQjJGyXcsEQuzoSyCvpbiMKOlggU2f4RLu1wiH
lVF8Hy7WQntlEs2Mty6hUCu2jwThX3emdXisO7/LMAsYXYPrjLwbPVEmpmgHP1Ng
7TdiskHZuc+IaEXdnLQo2cfwnwyjTGYA4G0G40dht4pDELUPAYino6UCgYEAuLJE
7bP844JShS9rXm/nwbPQcPPsKDXAIE/EvkKW9zpURiMMI/22F2ZiNFMDuSkgu0MV
PgfBhZwu0UY005Us8x1Q7iH8WgByELBIFsbTfhxlw/tCM1wDCgKTdXvB1TJqSPYZ
NJIIdNPxE+uhRvsxWcbbhyN7ZFwUKO428MxFKL8CgYA5xovH0freMGfkCQzzvi52
8fQVOHS2XjmTtKoM/70/cNjd4rY/eolKWEYtZjsaadgFnAUKOcFoNVfop60IgZLq
lux2kYYYJXpEsN4+lTKh0unsJPkuAVxcDMxmu+7qZXGotuLJRllvplPstSu0VE7+
PkIgWKd975OhqgD9xHPLKQKBgA2W4Ed1FMhQkG3IPWgnZ6/y4QVOjTOwMMCv3dP5
5k2udwDqFzyWsE0sdVFQQlffVM8ycl+sBmskQLWZKs3ejf4dTBUZXw3oNnxP3B4A
5xLCWCKBXC+XN4oHAn6EapCbMwLHSLd7fy/JYFWM4sLsPIdUgFaDH9MUi08vp88z
2GrVAoGBAKzyYoz2z71y4hC5ZXyMSefuGHRoODOgu8DYJVviJybyJ5rtLfUi1eu7
sLhhfA4oE/XiXLeGDDc4DuQ5KbrbR6X6nWI1oAT8ptwk5IoYHPfRsccaVtIwYVQr
2W0tup110R110LdwfhIsHQJONnbOu6wMuNtmJbT4ZXezXH7pa14V
-----END RSA PRIVATE KEY-----

Signature:
9cfa80431114b49b0c1c1847af7d87e055889824bb6b4a06fd0b46d85d3200c083284382a1ee73e7f20a467986d858ae9f1888039fdef6b550beaf46ffe8fe181a214089727dc5f8907d580d404df75ff18ab5bb95ab1b2a6a84247771532fd75c3252cbb1a41dbaf28d02ef84b07fdd80284354d848f191a1bd3ffd571010e50a03d070c2db25c54196cb1aa9a8d3ef579d5522dfc591528a781ef27345986c52ef2bd75cd7444ee9de4ed74b2b8ad2234c1b4aea3097897daf24f3dd144bf2d4f00b7b46e06cdc2342def8eff4f782dedc20de4b0aa5c1ead7527627815186596e0e28f23c83364c56383e13217e0497ea843607b6b923464d98e0c70d98f7
Signature is valid.
"""

6、密钥序列化与反序列化

下面是一个示例,演示如何生成密钥并进行序列化和反序列化:

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
from cryptography.fernet import Fernet
"""
代码说明:
生成密钥:

Fernet.generate_key() 生成一个新的密钥。
序列化:

使用 key.decode('utf-8') 将密钥从字节转换为字符串,方便存储(例如保存到文件或数据库)。
反序列化:

使用 serialized_key.encode('utf-8') 将字符串转换回字节,以便恢复原始密钥。
加密与解密:

使用序列化后的密钥创建 Fernet 实例,可以对消息进行加密和解密。
"""
# 生成密钥
key = Fernet.generate_key()
fernet = Fernet(key)

# 序列化:将密钥编码为字符串以存储
serialized_key = key.decode('utf-8')
print(f"Serialized Key: {serialized_key}")

# 反序列化:将字符串解码为字节
deserialized_key = serialized_key.encode('utf-8')

# 使用反序列化的密钥进行加密和解密
fernet = Fernet(deserialized_key)

# 加密示例
message = b"Hello, World!"
encrypted_message = fernet.encrypt(message)
print(f"Encrypted Message: {encrypted_message}")

# 解密示例
decrypted_message = fernet.decrypt(encrypted_message)
print(f"Decrypted Message: {decrypted_message.decode('utf-8')}")
"""
Serialized Key: BQqk2MbwlcWt6Bt39topcwIao2Gk2M_R3doVN37EHDA=
Encrypted Message: b'gAAAAABm1S2Uwc4-4urM6bSfC3S7Az05xrJscPlP0WJlocDnXz8L6rddP3uCUVuQKsw507H6bnVsHcyj9OS1qDGVTawT9OwzQw=='
Decrypted Message: Hello, World!
"""

7、通信数据加密

下面是一个使用 cryptography 库和 Python 的 socket 模块来实现简单的加密通信的示例。

服务端代码(server.py):

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
import socket
from cryptography.fernet import Fernet

# 生成密钥
key = Fernet.generate_key()
cipher_suite = Fernet(key)

# 保存密钥到文件中(实际应用中要安全存储)
with open("secret.key", "wb") as key_file:
key_file.write(key)

# 创建 socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 65432))
server_socket.listen()

print("等待连接...")
conn, addr = server_socket.accept()

with conn:
print('连接来自', addr)
while True:
# 接收数据
encrypted_data = conn.recv(1024)
if not encrypted_data:
break

# 解密数据
decrypted_data = cipher_suite.decrypt(encrypted_data)
print("接收到:", decrypted_data.decode())

# 关闭 socket
server_socket.close()

客户端代码(client.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import socket
from cryptography.fernet import Fernet

# 读取密钥
with open("secret.key", "rb") as key_file:
key = key_file.read()

cipher_suite = Fernet(key)

# 创建 socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 65432))

# 发送数据
message = "你好,服务器!"
encrypted_message = cipher_suite.encrypt(message.encode())
client_socket.sendall(encrypted_message)

# 关闭 socket
client_socket.close()

注意事项

密钥管理:在实际应用中,密钥的存储和管理是至关重要的。不要将密钥硬编码在代码中。
错误处理:示例代码中缺乏错误处理,实际使用时需要考虑到网络异常等情况。
加密方式:这里使用的是对称加密,适合小规模数据,若涉及到用户身份验证或更复杂的场景,可以考虑使用 SSL/TLS 等。
通过上述代码示例,你可以了解到如何使用 cryptography 库以及 Python 的 socket 模块来实现基本的网络通信加密。


Python 第三方模块之 cryptography - 加密解密
https://flepeng.github.io/021-Python-33-Python-第三方模块-35-加解密-Python-第三方模块之-cryptography-加密解密/
作者
Lepeng
发布于
2016年8月3日
许可协议