Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a CSR with Python (crypto)

Tags:

python

I have a skeleton that -- works, but I'm kind of stuck on the following;

  1. I don't see a way with the crypto library to handle SAN (subjectAltName)? Hopefully I'm not wrong on the terminology, but if I have say -- one main hostname test.test.edu and then alternatively would like that host to also be pushu.edu, usually that can be a "subjectAltName".
  2. Is there any way to see the entire CSR? Like, where it shows the Subject, State, etc, etc? I just want to see it printed to screen, but I don't see a way to do this with crypto.

Any help would be greatly appreciated; code thus far -

#!/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')

EDIT:

I ended up fixing this awhile ago. Here's the code with the added extensions, or you can clone it from my 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
like image 477
Ethabelle Avatar asked Jun 04 '14 16:06

Ethabelle


People also ask

How do I get a 2048 bit CSR?

To generate a CSR, you will need to create a key pair for your server. These two items are a digital certificate key pair and cannot be separated. If you lose your public/private key file or your password and generate a new one, your SSL Certificate will no longer match.

How can I generate CSR for SSL online?

OpenSSL CSR Wizard. Our OpenSSL CSR Wizard is the fastest way to create your CSR for Apache (or any platform) using OpenSSL. Fill in the details, click Generate, then paste your customized OpenSSL CSR command in to your terminal. Note: After 2015, certificates for internal names will no longer be trusted.


1 Answers

For your first question about how to add a SAN to a CSR, look into using the add_extensions method on the X509req object (which is curiously not mentioned in the docs for the X509req class)

It would look something like this

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

Update : Thanks to Peter Gallagher for catching the missing type prefixes (e.g. DNS, IP) in my original answer.


like image 161
gene_wood Avatar answered Sep 27 '22 22:09

gene_wood