使用Python生成CSR(加密)

17 投票
2 回答
22502 浏览
提问于 2025-04-18 08:36

我有一个基本的框架,运行得还不错,但在以下几个方面遇到了一些困难:

  1. 我在加密库中找不到处理SAN(主题备用名称)的方法。希望我没有搞错术语。如果我有一个主要的主机名,比如 test.test.edu,然后我还想让这个主机名也能用 pushu.edu,通常这可以通过“主题备用名称”来实现。
  2. 有没有办法查看整个证书签名请求(CSR)?比如,它显示的主题、州等信息。我只是想把这些信息打印到屏幕上,但在加密库中找不到方法。

任何帮助都将非常感谢;到目前为止的代码如下 -

#!/usr/bin/env python

from OpenSSL import crypto, SSL
import subprocess, os, sys

# Create 'usage' portion
# Something, blah blah, use script like this, blah blah.

# Variable
TYPE_RSA = crypto.TYPE_RSA

# Generate pkey
def generateKey(type, bits):

    keyfile = 'incommon.key'
    key = crypto.PKey()
    key.generate_key(type, bits)
    if os.path.exists(keyfile):
        print "Certificate file exists, aborting."
        print " ", keyfile
        sys.exit(1)
    else:
        f = open(keyfile, "w")
        f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
        f.close()
    return key

# Generate CSR
def generateCSR(nodename):

    csrfile = 'incommon.csr'
    req = crypto.X509Req()
    # Return an X509Name object representing the subject of the certificate.
    req.get_subject().CN = nodename
    #req.get_subject().countryName = 'xxx'
    #req.get_subject().stateOrProvinceName = 'xxx'
    #req.get_subject().localityName = 'xxx'
    #req.get_subject().organizationName = 'xxx'
    #req.get_subject().organizationalUnitName = 'xxx'
    # Set the public key of the certificate to pkey.
    req.set_pubkey(key)
    # Sign the certificate, using the key pkey and the message digest algorithm identified by the string digest.
    req.sign(key, "sha1")
    # Dump the certificate request req into a buffer string encoded with the type type.
    if os.path.exists(csrfile):
        print "Certificate file exists, aborting."
        print " ", csrfile
        sys.exit(1)
    else:
        f = open('incommon.csr', "w")
        f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
        f.close()

#Call key & CSR functions
key = generateKey(TYPE_RSA,2048)
# Needs to take input from user.
generateCSR('test.test.edu')

编辑:

我在一段时间前解决了这个问题。这里是添加了扩展的代码,或者你可以从我的Github上克隆它: https://github.com/cjcotton/python-csr;

# Generate Certificate Signing Request (CSR)
def generateCSR(nodename, sans = []):

    C  = raw_input('Enter your country: ')
    ST = raw_input("Enter your state: ")
    L  = raw_input("Enter your location: ")
    O  = raw_input("Enter your organization: ")
    OU = raw_input("Enter your organizational unit: ")

    # Allows you to permanently set values required for CSR
    # To use, comment raw_input and uncomment this section.
    # C  = 'US'
    # ST = 'New York'
    # L  = 'Location'
    # O  = 'Organization'
    # OU = 'Organizational Unit'

    csrfile = 'host.csr'
    keyfile = 'host.key'
    TYPE_RSA = crypto.TYPE_RSA
    # Appends SAN to have 'DNS:'
    ss = []
    for i in sans:
        ss.append("DNS: %s" % i)
    ss = ", ".join(ss)

    req = crypto.X509Req()
    req.get_subject().CN = nodename
    req.get_subject().countryName = C
    req.get_subject().stateOrProvinceName = ST
    req.get_subject().localityName = L
    req.get_subject().organizationName = O
    req.get_subject().organizationalUnitName = OU
    # Add in extensions
    base_constraints = ([
        crypto.X509Extension("keyUsage", False, "Digital Signature, Non Repudiation, Key Encipherment"),
        crypto.X509Extension("basicConstraints", False, "CA:FALSE"),
    ])
    x509_extensions = base_constraints
    # If there are SAN entries, append the base_constraints to include them.
    if ss:
        san_constraint = crypto.X509Extension("subjectAltName", False, ss)
        x509_extensions.append(san_constraint)
    req.add_extensions(x509_extensions)
    # Utilizes generateKey function to kick off key generation.
    key = generateKey(TYPE_RSA, 2048)
    req.set_pubkey(key)
    req.sign(key, "sha1")
    generateFiles(csrfile, req)
    generateFiles(keyfile, key)
    return req

2 个回答

3

Gene的回答差不多,但每个主题备用名称(Subject Alternative Name)前面必须加上一个类型,比如DNS、IP、电子邮件等等。如果你想用DNS,下面的代码就可以用了:

aliases = ['test1.example.com', 'test2.example.com']
if aliases:
    subject_alt_names = []
    for alias in aliases:
        subject_alt_names.append("DNS:{}".format(alias))
    req.add_extensions([crypto.X509Extension("subjectAltName", False, ",".join(subject_alt_names))])
9

关于你第一个问题,如何在证书签名请求(CSR)中添加一个SAN(主题备用名称),你可以看看在X509req对象上使用的add_extensions方法(有趣的是,这个方法在X509req类的文档中并没有提到)。

它的用法大概是这样的:

altnames = ', '.join(['DNS:foo.example.com',
                      'DNS:bar.example.com', 
                      'IP:203.0.113.12'])
req.add_extensions([OpenSSL.crypto.X509Extension("subjectAltName", 
                                                 False, 
                                                 altnames)])

更新:感谢Peter Gallagher指出我原回答中缺少的类型前缀(例如DNSIP)。


撰写回答