- 最直接的方式:使用
cryptography库 - 标准库方式:使用
ssl模块 - 系统级集成:编译 Python 时绑定特定版本的 OpenSSL
- 检查和验证 Python 程序使用的 OpenSSL 版本
最直接、最现代的方式:cryptography 库
对于绝大多数需要直接操作 OpenSSL 功能(如创建密钥、证书、加密/解密数据)的开发者来说,cryptography 库是最佳选择,它是一个高级的密码学库,其底层直接调用 OpenSSL,但提供了比 OpenSSL 原生 C API 更 Pythonic、更安全的接口。

为什么推荐 cryptography?
- Pythonic API:接口设计符合 Python 的习惯,易于使用。
- 安全性:它努力避免常见的加密错误(如使用不安全的填充、模式等)。
- 跨平台:预编译的二进制包在 Windows, macOS, 和 Linux 上都可用,并会自动链接到系统上安装的 OpenSSL。
- 功能全面:涵盖了现代密码学的方方面面,从非对称加密、哈希到密钥派生。
安装
pip install cryptography
示例:使用 RSA 加密和解密数据
这个例子展示了如何使用 cryptography 库进行非对称加密,这背后就是 OpenSSL 在工作。
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
# --- 1. 生成 RSA 密钥对 ---
# 私钥用于解密和签名,公钥用于加密和验证签名
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
print("RSA 密钥对已生成。")
# --- 2. 使用公钥加密数据 ---
message = b"这是一条需要保密的敏感信息。"
print(f"原始消息: {message.decode()}")
# 使用 OAEP 填充和 SHA-256 哈希,这是现代且安全的做法
ciphertext = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"加密后的密文: {ciphertext.hex()}")
# --- 3. 使用私钥解密数据 ---
# 在实际应用中,接收方会用自己的私钥来解密
decrypted_message = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"解密后的消息: {decrypted_message.decode()}")
# --- 4. 序列化和保存密钥 ---
# 将私钥和公钥保存到文件中,以便将来使用
# 保存私钥 (需要设置密码保护)
pem_private_key = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(b'my-secret-password')
)
with open("private_key.pem", "wb") as f:
f.write(pem_private_key)
print("私钥已保存到 private_key.pem")
# 保存公钥
pem_public_key = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open("public_key.pem", "wb") as f:
f.write(pem_public_key)
print("公钥已保存到 public_key.pem")
标准库方式:ssl 模块
Python 的标准库 ssl 模块是 with 语句与 OpenSSL 结合最紧密、最常见的应用场景,它主要用于创建安全的网络连接,即 TLS/SSL 加密通道。
with 语句在这里的作用是确保网络连接在代码块执行完毕后,无论是否发生异常,都能被正确关闭。
核心概念
ssl.wrap_socket(): 包装一个普通的套接字,使其变为安全的套接字。ssl.create_default_context(): 推荐使用此函数创建一个安全的 SSL 上下文,它会自动加载系统的信任根证书,并使用安全的默认设置(如禁用不安全的协议和弱密码套件)。- 服务器端 vs. 客户端:
- 服务器端:需要一个证书和对应的私钥来证明自己的身份。
- 客户端:需要验证服务器的证书是否可信。
示例:创建一个安全的 HTTPS 服务器
这是一个简单的 HTTPS 服务器,它监听端口 4443,并为任何连接提供加密服务。

import http.server
import ssl
# --- 1. 准备证书和私钥 ---
# 在实际生产环境中,你应该从受信任的证书颁发机构(CA)获取证书。
# 这里我们使用 OpenSSL 命令行工具自签名一个证书用于测试。
# (在终端运行):
# openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes
# 这会生成 cert.pem (证书) 和 key.pem (私钥) 文件。
# --- 2. 设置服务器 ---
# 切换到包含 cert.pem 和 key.pem 的目录
# 或者修改下面的路径为你文件的路径
PORT = 4443
Handler = http.server.SimpleHTTPRequestHandler
# 创建一个默认的 SSL 上下文
# load_default_certs=True 会加载系统信任的 CA 证书
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
# 加载服务器证书和私钥
# 请确保 'cert.pem' 和 'key.pem' 文件在同一目录下,或提供完整路径
try:
context.load_cert_chain(certfile='cert.pem', keyfile='key.pem')
except FileNotFoundError:
print("错误: 请确保 'cert.pem' 和 'key.pem' 文件存在。")
print("你可以使用 OpenSSL 生成自签名证书: ")
print("openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes")
exit()
# --- 3. 启动服务器 ---
# 使用 with 语句,确保服务器套接字在退出时被正确关闭
with http.server.HTTPServer(('', PORT), Handler) as httpd:
# 使用 SSLContext 包装服务器套接字
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
print(f"服务器启动在 https://localhost:{PORT}")
print("按 Ctrl+C 停止服务器")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\n服务器正在关闭...")
httpd.server_close()
print("服务器已关闭。")
如何运行这个例子
- 将上面的代码保存为
https_server.py。 - 在终端中,使用 OpenSSL 生成证书和私钥:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes
在生成过程中,它会询问一些信息,你可以直接按回车使用默认值。
- 确保生成的
cert.pem和key.pem与https_server.py在同一目录下。 - 运行 Python 脚本:
python https_server.py
- 打开你的浏览器,访问
https://localhost:4443,你会看到一个浏览器安全警告,因为这是一个自签名证书,点击“高级”并继续访问即可看到当前目录的文件列表。
系统级集成:编译 Python 时绑定特定版本的 OpenSSL
在某些特殊情况下,你可能需要 Python 绑定到一个特定版本的 OpenSSL,而不是系统默认的版本,这通常发生在以下场景:
- 你需要使用某个特定 OpenSSL 版本的新特性。
- 你需要修复一个旧版 Python 的 OpenSSL 安全漏洞,但系统无法升级 OpenSSL。
- 你正在构建一个需要精确控制依赖的 Docker 镜像。
这个过程比较复杂,通常涉及从源代码编译 Python。
基本步骤:

- 下载并编译 OpenSSL:从 OpenSSL 官网下载源码,编译并安装到自定义目录(
/usr/local/openssl)。 - 下载并编译 Python:从 Python 官网下载源码。
- 配置编译选项:在运行
./configure时,使用--with-openssl参数指向你自定义编译的 OpenSSL 路径。./configure --with-openssl=/path/to/your/custom/openssl make sudo make install
注意:对于绝大多数开发者来说,这是不必要的,直接使用操作系统包管理器或 pyenv 等工具管理 Python 版本会更简单。
检查 Python 程序使用的 OpenSSL 版本
你可以通过以下几种方式检查你的 Python 解释器正在使用哪个版本的 OpenSSL。
使用 ssl 模块 (最简单)
import ssl print(ssl.OPENSSL_VERSION) # 输出示例: OpenSSL 1.1.1k FIPS 25 Mar 2025
使用 ctypes 模块 (更底层)
这种方法可以获取到 OpenSSL 的版本号信息,而不仅仅是版本字符串。
import ssl
import ctypes
import ctypes.util
# 加载 OpenSSL 的共享库
lib = ctypes.CDLL(ctypes.util.find_library("ssl"))
# 获取版本号函数
# 注意:函数名在不同版本中可能略有不同 (OpenSSL_version vs. OpenSSL_version_num)
# OpenSSL_version_num 更可靠
version_num = lib.OpenSSL_version_num()
major = (version_num >> 28) & 0xF
minor = (version_num >> 20) & 0xFF
fix = (version_num >> 12) & 0xFF
patch = (version_num >> 4) & 0xFF
status = version_num & 0xF
print(f"OpenSSL Version Number: {version_num:#x}")
print(f"Major: {major}, Minor: {minor}, Fix: {fix}, Patch: {patch}, Status: {status}")
# 格式化输出版本号
print(f"Formatted Version: {major}.{minor}.{fix}")
| 场景 | 推荐工具 | 与 with 和 OpenSSL 的关系 |
|---|---|---|
| 编写安全网络客户端/服务器 | ssl (标准库) |
核心应用。with 语句用于安全地管理加密套接字的整个生命周期。 |
| 实现自定义加密/解密逻辑 | cryptography (第三方库) |
底层依赖。cryptography 提供高级 API,其内部调用 OpenSSL 来执行实际的密码学运算。 |
| 需要精确控制 OpenSSL 版本 | 从源码编译 Python | 环境配置,在构建 Python 解释器时指定 OpenSSL 的路径,影响整个 Python 环境的行为。 |
| 检查 OpenSSL 版本 | ssl.OPENSSL_VERSION |
信息获取,用于调试和确认运行环境。 |
对于绝大多数 Python 你只需要记住:
- 要做 网络通信安全,用
ssl模块,并用with语句管理连接。 - 要做 底层数据加密,用
cryptography库,它安全又好用。
