You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在cryptography中添加自定义extension适配pyHanko证书注册问题

问题描述

使用cryptography库生成X509证书的代码可正常运行,但在pyHanko中创建PDF签名的SimpleSigner对象时失败。原因是将证书注册到CertificateRegistry时需要issuer_serial,但不清楚如何为证书添加该"扩展"以适配pyHanko。

证书与私钥创建代码

# Generate RSA private key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)

# Generate a self-signed certificate
subject = issuer = x509.Name([
    x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"DE"),  # Country
    x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"State"),  # State
    x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Company"),  # Organization
    x509.NameAttribute(x509.NameOID.COMMON_NAME, user['name']),  # User's common name
    x509.NameAttribute(x509.NameOID.EMAIL_ADDRESS, user['email'])  # User's email
])

cert_builder = (
    x509.CertificateBuilder()
    .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))
    .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
    .sign(private_key, algorithm=hashes.SHA256(), backend=default_backend())
)

# Serialize private key to PEM format
private_key_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

# Serialize certificate to PEM format
cert_pem = cert_builder.public_bytes(encoding=serialization.Encoding.PEM)

# Generate a random Fernet key for encryption
fernet_key = Fernet.generate_key()

# Encrypt the private key using Fernet encryption
f = Fernet(fernet_key)
encrypted_private_key = f.encrypt(private_key_pem)

# Encode encrypted private key and fernet key to base64
encrypted_private_key_base64 = base64.b64encode(encrypted_private_key).decode('utf-8')
fernet_key_base64 = base64.b64encode(fernet_key).decode('utf-8')

pyHanko中失败的代码

注册代码:

cert_store = CertificateRegistry()  # Create an instance of CertificateRegistry
  
cert_store.register(cert_pem)  # Register the certificate

创建signer对象代码:

cms_signer = signers.SimpleSigner(
    private_key,
    cert,
    cert_registry=cert_store,
)
解决方案

首先明确:issuer_serial不是X509证书的自定义扩展,而是证书固有属性的组合——发行者名称(issuer) + 证书序列号(serialNumber),pyHanko可以直接从证书对象中提取这两个值,不需要手动添加扩展。问题出在证书注册和对象传递的方式上,修正步骤如下:

  1. 解析证书对象:将PEM格式的证书字节解析为cryptography的x509.Certificate对象,而不是直接传递PEM字符串给CertificateRegistry。
  2. 正确注册证书:使用解析后的证书对象调用register方法,pyHanko会自动提取issuer_serial。
  3. 确保参数类型正确:创建SimpleSigner时,cert参数必须是解析后的证书对象,而非PEM字节或字符串。

修改后的完整代码片段

from cryptography.x509 import load_pem_x509_certificate
from pyhanko_certvalidator import CertificateRegistry
from pyhanko.sign import signers

# 解析证书对象(从之前生成的cert_pem)
cert = load_pem_x509_certificate(cert_pem, backend=default_backend())

# 注册证书到CertificateRegistry
cert_store = CertificateRegistry()
cert_store.register(cert)  # 传入解析后的证书对象,而非PEM字节

# 创建SimpleSigner对象
cms_signer = signers.SimpleSigner(
    private_key=private_key,
    cert=cert,
    cert_registry=cert_store
)

额外说明

  • 自签名证书的issuer和subject相同,pyHanko依然能正确识别issuer_serial(发行者名称就是证书自身的主体名称,加上随机生成的序列号)。
  • 如果仍有问题,检查证书是否包含完整的issuer和serialNumber字段——你的证书创建代码中已经通过.issuer_name(issuer)和.serial_number(x509.random_serial_number())设置了这两个字段,所以无需额外修改证书生成逻辑。

内容的提问来源于stack exchange,提问作者KROLN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 11:48:21