杰瑞科技汇

Python如何用OpenSSL生成证书?

  1. 使用 Python 的 cryptography:这是最推荐、最现代、最安全的方式,你可以用它来生成自签名证书、创建证书签名请求、验证证书等。cryptography 库是对 OpenSSL 功能的高级封装,提供了更友好的 Pythonic API。
  2. 使用 openssl 命令行工具:通过 Python 的 subprocess 模块来调用系统自带的 openssl 命令,这种方式比较直接,但需要你熟悉 OpenSSL 命令行参数,并且处理起来稍显繁琐。

下面我将重点介绍第一种方式(cryptography),因为它更符合 Python 开发的习惯,并会简要提及第二种方式。

Python如何用OpenSSL生成证书?-图1
(图片来源网络,侵删)

使用 Python cryptography

你需要安装这个库:

pip install cryptography

生成自签名证书 (Self-Signed Certificate)

自签名证书通常用于本地开发、测试环境或内部服务,因为它不受受信任的证书颁发机构(CA)的信任。

目标:创建一个包含私钥和公钥的 PEM 格式文件 (server.keyserver.crt)。

代码示例

Python如何用OpenSSL生成证书?-图2
(图片来源网络,侵删)
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta
# 1. 生成私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
# 2. 创建证书构建器
builder = x509.CertificateBuilder()
# 3. 设置证书的各种属性
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Beijing"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, "Beijing"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Dev Company"),
    x509.NameAttribute(NameOID.COMMON_NAME, "localhost"),
])
builder = builder.subject_name(subject) \
             .issuer_name(issuer) \
             .public_key(private_key.public_key()) \
             .serial_number(x509.random_serial_number()) \
             .not_valid_before(datetime.utcnow()) \
             .not_valid_after(datetime.utcnow() + timedelta(days=365)) # 证书有效期1年
# 4. 添加扩展(非常重要,用于指定证书用途)
builder = builder.add_extension(
    x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
builder = builder.add_extension(
    x509.KeyUsage(
        digital_signature=True,
        content_commitment=False,
        key_encipherment=True,
        data_encipherment=False,
        key_agreement=False,
        key_cert_sign=False,
        crl_sign=False,
        encipher_only=False,
        decipher_only=False
    ),
    critical=True,
)
builder = builder.add_extension(
    x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]),
    critical=False,
)
# 5. 签名证书(用自己的私钥签名,因为是自签名)
certificate = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(), backend=default_backend()
)
# 6. 将私钥和证书保存到文件
# 保存私钥
with open("server.key", "wb") as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()
    ))
# 保存证书
with open("server.crt", "wb") as f:
    f.write(certificate.public_bytes(serialization.Encoding.PEM))
print("自签名证书生成成功: server.key 和 server.crt")

创建证书签名请求 (CSR)

CSR 是向证书颁发机构(CA)申请证书时提交的请求,包含了你的公钥和一些身份信息。

目标:创建一个 server.csr 文件。

代码示例

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
# 1. 生成私钥(这个私钥将用于后续接收CA签发的证书)
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
# 2. 创建CSR构建器
builder = x509.CertificateSigningRequestBuilder()
# 3. 设置CSR的主体信息
subject = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, "CN"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Beijing"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, "Beijing"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Prod Company"),
    x509.NameAttribute(NameOID.COMMON_NAME, "my-website.com"),
])
builder = builder.subject_name(subject)
# 4. 添加可选的扩展
builder = builder.add_extension(
    x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
# 5. 使用私钥对CSR进行签名
csr = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(), backend=default_backend()
)
# 6. 将CSR保存到文件
with open("server.csr", "wb") as f:
    f.write(csr.public_bytes(serialization.Encoding.PEM))
# 同时也保存好私钥,因为你后续需要用它来解密或签名
with open("server.key", "wb") as f:
    f.write(private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()
    ))
print("CSR生成成功: server.csr 和 server.key")

使用CA签名CSR(颁发证书)

如果你有一个自己的CA(证书颁发机构),你可以用它来签发你之前生成的CSR,这模拟了真实世界的证书申请流程。

Python如何用OpenSSL生成证书?-图3
(图片来源网络,侵删)

前提:你需要一个CA的私钥和证书,我们可以用上面的方法先生成一个CA。

代码示例

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta
# --- 假设这是你的CA的私钥和证书 ---
# 在实际应用中,这些文件应该被安全地存储
with open("ca.key", "rb") as f:
    ca_private_key = serialization.load_pem_private_key(
        f.read(),
        password=None, # 如果私钥有密码,在这里提供
        backend=default_backend()
    )
with open("ca.crt", "rb") as f:
    ca_cert = x509.load_pem_x509_certificate(f.read(), default_backend())
# --- 加载你之前生成的CSR ---
with open("server.csr", "rb") as f:
    csr = x509.load_pem_x509_csr(f.read(), default_backend())
# --- 使用CA来签发证书 ---
builder = x509.CertificateBuilder()
builder = builder.subject_name(csr.subject) \
             .issuer_name(ca_cert.subject) \
             .public_key(csr.public_key()) \
             .serial_number(x509.random_serial_number()) \
             .not_valid_before(datetime.utcnow()) \
             .not_valid_after(datetime.utcnow() + timedelta(days=365)) # 1年有效期
# 添加与CSR中相同的扩展
for extension in csr.extensions:
    builder = builder.add_extension(extension.value, critical=extension.critical)
# 使用CA的私钥进行签名
server_cert = builder.sign(
    private_key=ca_private_key, algorithm=hashes.SHA256(), backend=default_backend()
)
# --- 保存签发后的服务器证书 ---
with open("server_signed.crt", "wb") as f:
    f.write(server_cert.public_bytes(serialization.Encoding.PEM))
print("CA证书签发成功: server_signed.crt")

使用 openssl 命令行工具

如果你更喜欢直接使用 OpenSSL 命令,可以通过 Python 的 subprocess 模块来执行。

生成自签名证书

import subprocess
# 生成私钥
subprocess.run(
    ["openssl", "genrsa", "-out", "server_cli.key", "2048"],
    check=True
)
# 生成自签名证书
subprocess.run([
    "openssl", "req", "-new", "-x509", "-key", "server_cli.key",
    "-out", "server_cli.crt", "-days", "365",
    "-subj", "/C=CN/ST=Beijing/L=Beijing/O=My Dev Company/CN=localhost"
], check=True)
print("OpenSSL命令生成自签名证书成功: server_cli.key 和 server_cli.crt")

创建 CSR

import subprocess
# 生成私钥
subprocess.run(
    ["openssl", "genrsa", "-out", "server_cli.key", "2048"],
    check=True
)
# 创建CSR
subprocess.run([
    "openssl", "req", "-new", "-key", "server_cli.key",
    "-out", "server_cli.csr",
    "-subj", "/C=CN/ST=Beijing/L=Beijing/O=My Prod Company/CN=my-website.com"
], check=True)
print("OpenSSL命令生成CSR成功: server_cli.csr 和 server_cli.key")

Python 应用中使用证书

生成了证书后,最常见的是在 Web 服务器(如 Flask, FastAPI, Django)或 gRPC 服务器中启用 HTTPS。

示例:在 Flask 中使用自签名证书

from flask import Flask
import ssl
app = Flask(__name__)
# 添加一个路由
@app.route('/')
def hello():
    return "Hello, Secure World!"
if __name__ == '__main__':
    # 加载我们之前生成的证书和私钥
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain('server.crt', 'server.key')
    # 启动应用,并启用 SSL
    # 注意:浏览器会警告这是一个不安全的连接,因为证书是自签名的
    app.run(host='0.0.0.0', port=443, ssl_context=context)

总结与建议

特性 cryptography openssl 命令行
易用性 ,Pythonic API,逻辑清晰。 ,需要记忆命令和参数,处理文本输出。
集成度 ,无缝集成到 Python 应用逻辑中。 中等,通过 subprocess 调用,有进程间通信开销。
灵活性 ,可以精细地控制证书的每一个字节和扩展。 ,OpenSSL 功能极其强大,命令行参数也很多。
安全性 ,避免了对 shell 命令的依赖,减少了注入风险。 中等,需要小心处理命令参数,防止命令注入。
推荐场景 Python 应用开发、自动化脚本、需要证书逻辑集成 一次性任务、快速测试、熟悉 OpenSSL 的用户

强烈建议:在 Python 项目中,优先使用 cryptography 库来处理所有与证书相关的任务,它更安全、更易于维护,并且能更好地融入你的 Python 代码库。

分享:
扫描分享到社交APP
上一篇
下一篇