title: Generating a CSR with Python (crypto)
body: <p>I have a skeleton that -- works, but I'm kind of stuck on the following;</p>
  
  <ol>
  <li>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".</li>
  <li>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.</li>
  </ol>
  
  <p>Any help would be greatly appreciated; code thus far -</p>
  
  <pre><code>#!/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 &amp; CSR functions
  key = generateKey(TYPE_RSA,2048)
  # Needs to take input from user.
  generateCSR('test.test.edu')
  </code></pre>
  
  <p>EDIT:</p>
  
  <p>I ended up fixing this awhile ago. Here's the code with the added extensions, or you can clone it from my Github: <a href="https://github.com/cjcotton/python-csr" rel="noreferrer">https://github.com/cjcotton/python-csr</a>;</p>
  
  <pre><code># 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
  </code></pre>