Python: create a CSR with a private key

Python: create a CSR with a private key

In this article we're going to see how to create a CSR (Certificate Signing Request) and an RSA private key with Python.

In this article we're going to see how to create a CSR (Certificate Signing Request) and an RSA private key with Python.

In order to accomplish our task, we need to gather the following information:

  1. Country code (e.g. US)
  2. State or province name (e.g. Virginia)
  3. Locality name (e.g. Richmond)
  4. Organization name (e.g. My Company)
  5. Common name (the domain name, e.g. example.com)

We can then write a function that creates the CSR together with the private key and returns them as human-readable strings.


from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization

def create_csr(attrs):
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048
    )

    country = attrs.get('country','')
    state = attrs.get('state','')
    locality = attrs.get('locality','')
    organization = attrs.get('organization','')
    common_name = attrs.get('common_name', '')
    dns_name1 = common_name
    dns_name2 = f'www.{common_name}'

    csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, country),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
        x509.NameAttribute(NameOID.LOCALITY_NAME, locality),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
        x509.NameAttribute(NameOID.COMMON_NAME, common_name)
    ])).add_extension(
        x509.SubjectAlternativeName([
            x509.DNSName(dns_name1),
            x509.DNSName(dns_name2)
        ]),
        critical=False,
    ).sign(key, hashes.SHA256())

    return {'key': key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()).decode('utf-8'), 'csr': csr.public_bytes(
            encoding=serialization.Encoding.PEM).decode('utf-8')}

In order to make sure that this will always work, check if the OpenSSL library is installed on your machine and is properly recognized by Python.