Automatically make CSR and key from site

Photo by RoonZ nl on Unsplash

This is to help with manual renewing of certificates.

So you have a many live sites and you need to renew the certificates.

git clone https://github.com/bradymd/makecsr.git
cd makecsr
pip install -r requirments.txt 
./makecsr -u   https://example.com:443

This will leave three files example.key, example.csr – the csr can be used to get a commercial certificate.

The program will also leave you with a single openssl command to help you create the key and certificate. (With alternative names a example.cnf will be provided.)

See https://github.com/bradymd/makecsr

$ ./makecsr -u aardvark.herts.ac.uk
[-] You have requested to look at the  current certificate https://aardvark.herts.ac.uk:443
                The existing certificate has Altnames
[-] You can generate the certificate request (CSR) and a new key to replace this like this:
                openssl req -new -nodes -newkey rsa:4096 -keyout aardvark.key -out aardvark.csr -config aardvark.cnf  -subj "/C=GB/L=Hatfield/O=University of Hertfordshire/CN=www.aardvark.herts.ac.uk"
[-] Generating locally:
                aardvark.key  and aardvark.csr
[-] The openssl.cnf file: (generated aardvark.cnf)

#----cut-and-paste-to-a-file openssl.cnf
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[dn]

[ req_ext ]
subjectAltName = @alt_names

[alt_names]

DNS.1 = www.aardvark.herts.ac.uk
DNS.2 = aardvark.herts.ac.uk 

The code is below for perusal (I’m not great coder, just a persistent novice). Use the git source – below is just in the spirit of learning.

#!/usr/bin/env python
helpmessage="""NAME:
  makecsr
SYNOPSIS:
  Generating CSR (Certificate Signing Requsts) from live sites.
USAGE:
  makecsr -u url
  makecsr -h
EXAMPLE:
  makesr -u example.com
  makecsr -u https://example.com:44321
  makecsr -u https://example.com
FUNCTION: It will examine a current live certificate, and construct openssl comamnds
and generate a key, csr, and cnf file if required.
AUTHOR: m.brady@herts.ac.uk"""

import socket
import sys
import subprocess
import shutil
import os
import getopt
import re
import csv
from datetime import datetime, date,timedelta
from operator import itemgetter
from colorama import Fore, Back, Style

url=""
help=""
argv = sys.argv[1:]
if (len(argv)) == 1:
    print(helpmessage)
    sys.exit()
try:
    options, args = getopt.getopt(argv, "u:h", ["url=", "help"])
except:
    print("Error Message ")
 
for name, value in options:
    if name in ['-u', '--url']:
        url = value
    elif name in ['-h', '--help']:
        print(helpmessage)
        sys.exit()
    else:
        print("No such option")


# Allowing for people to put in a url as in https://example.com:443
url=url.replace("https://","")
if re.findall(":",url):
  p=url.split(":")
  port = p[len(p)-1]
  url=p[0]
else:
  port="443"


# Check that the site exists
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
  result = sock.connect_ex((url,int(port)))
except socket.gaierror:
  print('Cannot connect')
  sys.exit()
sock.close()


# See if it has Altnames in the live certificate
highlight=Fore.RED + "[-] " + Style.RESET_ALL
print( highlight + "You have requested to look at the  current certificate https://" + url + ":" + port)
cmd1="openssl s_client -connect " + url + ":" + port 
cmd2="openssl x509 -noout -text"
p1 = subprocess.Popen(cmd1.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
results = str(subprocess.check_output(cmd2.split(), stderr=subprocess.STDOUT, stdin=p1.stdout))
names = re.findall("DNS:.*uk",results)
names = re.sub("DNS:","",names[0])
altnames = names.split(", ")

# Compose the -subj part of the long openssl command for CSR generation
matchObj=re.search( r'(.*Subject:)(.*)(Subject Public Key Info:)',results)
names = matchObj.group(2)
names=names.replace("\\n","")
names=re.sub(",", "/", names)
names=re.sub(" = ", "=", names)
names=re.sub("/ ", "/", names)
names=re.sub(" ","",names,count=1)
names=names.rstrip(" ")
names="/" + names

# Use a meaningful name for files created - not "www"
fname=url.split(".")
if fname[0] != "www":
    fname=fname[0]
else:
    fname=fname[1]

# When no altnames its simpler - basically no openssl.cnf required
if len(altnames) == 1:
  print(highlight + "You can generate the certificate request (CSR) and a new key to replace this like this:")
  cmd="openssl req -new -nodes -newkey rsa:4096 -keyout " + fname + ".key -out " + fname + ".csr -subj " 
  print("\t\t"  + cmd +  "\"" + names + "\"" )
  c=cmd.split()
  c.append(names)
  print(highlight + "Generating locally:")
  p2 = subprocess.Popen(c, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
  cmd3="/usr/bin/cat -"
  results = str(subprocess.check_output(cmd3.split(), stderr=subprocess.STDOUT, stdin=p2.stdout))
  print (highlight + "\t\t" + fname + ".key  and " + fname + ".csr" )

# If there are altnames its slightly more involved
# Generate a snippet to go in the openssl.cnf file
if len(altnames) != 1:
  print("\t\tThe existing certificate has Altnames")
  count=1
  snippet=""
  for alt in altnames:
    snippet=snippet + "\nDNS." + str(count) + " = " + alt 
    count=count+1
  opensslcnf="""
#----cut-and-paste-to-a-file openssl.cnf
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[dn]

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
""" + snippet  + """ 
"""
  # Write it to a file
  myText = open(fname + ".cnf",'w')
  myText.write(opensslcnf)
  myText.close()

  #  Compose the command ready to generate key and CSR - show the command to the user
  cmd1="openssl req -new -nodes -newkey rsa:4096 -keyout " + fname + ".key -out " + fname + ".csr -config " + fname + ".cnf  -subj "
  print(highlight + "You can generate the certificate request (CSR) and a new key to replace this like this:")
  print("\t\t" + cmd1 + "\"" +  names + "\"")
  c=cmd1.split()
  c.append(names)
  print(highlight + "Generating locally:")
  p2 = subprocess.Popen(c, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
  cmd3="/usr/bin/cat -"
  results = str(subprocess.check_output(cmd3.split(), stderr=subprocess.STDOUT, stdin=p2.stdout))
  print ("\t\t" + fname + ".key  and " + fname + ".csr" )
  print(highlight + "The openssl.cnf file: (generated " + fname + ".cnf)")
  print(opensslcnf)