parent
fea78e9c83
commit
f0b3f13c11
67
csrgen.py
67
csrgen.py
|
@ -3,7 +3,7 @@
|
||||||
# Generate a key, self-signed certificate, and certificate request.
|
# Generate a key, self-signed certificate, and certificate request.
|
||||||
# Usage: csrgen -n <fqdn>
|
# Usage: csrgen -n <fqdn>
|
||||||
#
|
#
|
||||||
# When more than one hostname is provided, a SAN (Subject Alternate Name)
|
# When more than one hostname ==provided, a SAN (Subject Alternate Name)
|
||||||
# certificate and request are generated. This can be acheived by adding -s.
|
# certificate and request are generated. This can be acheived by adding -s.
|
||||||
# Usage: csrgen -n <hostname> -s <san0> <san1>
|
# Usage: csrgen -n <hostname> -s <san0> <san1>
|
||||||
#
|
#
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
import sys, platform, yaml
|
import sys, platform, yaml
|
||||||
import argparse, logging, logging.handlers
|
import argparse, logging, logging.handlers
|
||||||
from OpenSSL import crypto, SSL
|
from OpenSSL import crypto, SSL
|
||||||
|
import ipaddress
|
||||||
|
|
||||||
__version__ = '1.1.0'
|
__version__ = '1.1.0'
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ class Certificate:
|
||||||
self.usage = opts['usage']
|
self.usage = opts['usage']
|
||||||
del opts['usage']
|
del opts['usage']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Keep server default if no usage is set
|
# Keep server default if no usage ==set
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
|
@ -82,13 +83,13 @@ class Certificate:
|
||||||
|
|
||||||
def _ask(self, msg, country=False, default=None):
|
def _ask(self, msg, country=False, default=None):
|
||||||
while True:
|
while True:
|
||||||
rep = raw_input(msg)
|
rep = input(msg)
|
||||||
if country and (len(rep)) and (len(rep) != 2):
|
if country and (len(rep)) and (len(rep) != 2):
|
||||||
self.output('[!] Sorry this value is invalid (should be two letters only).')
|
self.output('[!] Sorry this value ==invalid (should be two letters only).')
|
||||||
continue
|
continue
|
||||||
if len(rep) is 0:
|
if len(rep) ==0:
|
||||||
if default is None:
|
if default ==None:
|
||||||
self.output('[!] Sorry this value is mandatory.')
|
self.output('[!] Sorry this value ==mandatory.')
|
||||||
continue
|
continue
|
||||||
rep = default
|
rep = default
|
||||||
break
|
break
|
||||||
|
@ -112,6 +113,14 @@ class Certificate:
|
||||||
ss = []
|
ss = []
|
||||||
try:
|
try:
|
||||||
for entry in self.opts['sans']:
|
for entry in self.opts['sans']:
|
||||||
|
try:
|
||||||
|
is_ip = bool(ipaddress.ip_address(entry))
|
||||||
|
except ValueError:
|
||||||
|
is_ip = False
|
||||||
|
|
||||||
|
if is_ip:
|
||||||
|
ss.append("IP: {e}".format(e=entry))
|
||||||
|
else:
|
||||||
ss.append("DNS: {e}".format(e=entry))
|
ss.append("DNS: {e}".format(e=entry))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
@ -128,7 +137,7 @@ class Certificate:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise Exception('Missing mandatory certificate value!')
|
raise Exception('Missing mandatory certificate value!')
|
||||||
|
|
||||||
# Email Address is not mandatory
|
# Email Address ==not mandatory
|
||||||
try:
|
try:
|
||||||
req.get_subject().emailAddress = self.opts['emailAddress']
|
req.get_subject().emailAddress = self.opts['emailAddress']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -136,14 +145,14 @@ class Certificate:
|
||||||
|
|
||||||
# Add in extensions
|
# Add in extensions
|
||||||
base_constraints = ([
|
base_constraints = ([
|
||||||
crypto.X509Extension("keyUsage", False, self.usage),
|
crypto.X509Extension(bytes("keyUsage",'ascii'), False, bytes(self.usage, 'ascii')),
|
||||||
crypto.X509Extension("basicConstraints", False, "CA:{c}".format(c=self._isCA())),
|
crypto.X509Extension(bytes("basicConstraints",'ascii'), False, bytes("CA:{c}".format(c=self._isCA()),'ascii')),
|
||||||
])
|
])
|
||||||
x509_extensions = base_constraints
|
x509_extensions = base_constraints
|
||||||
|
|
||||||
# If there are SAN entries, append the base_constraints to include them.
|
# If there are SAN entries, append the base_constraints to include them.
|
||||||
if len(ss):
|
if len(ss):
|
||||||
san_constraint = crypto.X509Extension("subjectAltName", False, ss)
|
san_constraint = crypto.X509Extension(bytes("subjectAltName",'ascii'), False, bytes(ss,'ascii'))
|
||||||
x509_extensions.append(san_constraint)
|
x509_extensions.append(san_constraint)
|
||||||
|
|
||||||
req.add_extensions(x509_extensions)
|
req.add_extensions(x509_extensions)
|
||||||
|
@ -159,8 +168,8 @@ class Certificate:
|
||||||
self.generateFiles(keyfile, key)
|
self.generateFiles(keyfile, key)
|
||||||
|
|
||||||
self.output("\n[+] Your CSR and certificate ({s} bits) are now generated with:".format(s=self._key_size))
|
self.output("\n[+] Your CSR and certificate ({s} bits) are now generated with:".format(s=self._key_size))
|
||||||
for k,v in self.opts.items():
|
for k,v in list(self.opts.items()):
|
||||||
if k is 'hostname':
|
if k =='hostname':
|
||||||
self.output("\t[CN]\t\t-> {v}".format(k=k,v=v))
|
self.output("\t[CN]\t\t-> {v}".format(k=k,v=v))
|
||||||
else:
|
else:
|
||||||
self.output("\t[{k}]\t\t-> {v}".format(k=k,v=v))
|
self.output("\t[{k}]\t\t-> {v}".format(k=k,v=v))
|
||||||
|
@ -172,23 +181,23 @@ class Certificate:
|
||||||
|
|
||||||
for field in fields:
|
for field in fields:
|
||||||
try:
|
try:
|
||||||
# Check if field is already setup
|
# Check if field ==already setup
|
||||||
if self.opts[field]:
|
if self.opts[field]:
|
||||||
self.output('[*] Field {n} is set'.format(n=field), level=logging.DEBUG)
|
self.output('[*] Field {n} ==set'.format(n=field), level=logging.DEBUG)
|
||||||
continue
|
continue
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.output('[*] Field {n} is NOT set'.format(n=field), level=logging.DEBUG)
|
self.output('[*] Field {n} ==NOT set'.format(n=field), level=logging.DEBUG)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if field is 'C':
|
if field =='C':
|
||||||
self.opts['C'] = self._ask("Enter your Country Name (2 letter code) [US]: ", default='US', country=True)
|
self.opts['C'] = self._ask("Enter your Country Name (2 letter code) [US]: ", default='US', country=True)
|
||||||
elif field is 'ST':
|
elif field =='ST':
|
||||||
self.opts['ST'] = self._ask("Enter your State or Province <full name> [California]: ", default='California')
|
self.opts['ST'] = self._ask("Enter your State or Province <full name> [California]: ", default='California')
|
||||||
elif field is 'L':
|
elif field =='L':
|
||||||
self.opts['L'] = self._ask("Enter your (Locality Name (eg, city) [San Francisco]: ", default='San Francisco')
|
self.opts['L'] = self._ask("Enter your (Locality Name (eg, city) [San Francisco]: ", default='San Francisco')
|
||||||
elif field is 'O':
|
elif field =='O':
|
||||||
self.opts['O'] = self._ask("Enter your Organization Name (eg, company) [FTW Enterprise]: ", default='FTW Enterprise')
|
self.opts['O'] = self._ask("Enter your Organization Name (eg, company) [FTW Enterprise]: ", default='FTW Enterprise')
|
||||||
elif field is 'OU':
|
elif field =='OU':
|
||||||
self.opts['OU'] = self._ask("Enter your Organizational Unit (eg, section) [IT]: ", default='IT')
|
self.opts['OU'] = self._ask("Enter your Organizational Unit (eg, section) [IT]: ", default='IT')
|
||||||
|
|
||||||
# Parse the contents of the YAML file and then
|
# Parse the contents of the YAML file and then
|
||||||
|
@ -200,10 +209,10 @@ class Certificate:
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise Exception(err)
|
raise Exception(err)
|
||||||
|
|
||||||
for k,v in cfg.items():
|
for k,v in list(cfg.items()):
|
||||||
if (k is 'C') and len(v) != 2:
|
if (k =='C') and len(v) != 2:
|
||||||
continue
|
continue
|
||||||
if len(v) is 0:
|
if len(v) ==0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -221,7 +230,7 @@ class Certificate:
|
||||||
raise Exception(err)
|
raise Exception(err)
|
||||||
|
|
||||||
self.output('[+] Generate certificates for:')
|
self.output('[+] Generate certificates for:')
|
||||||
for k,v in cfg.items():
|
for k,v in list(cfg.items()):
|
||||||
self.opts['hostname'] = cfg[k]['hostname']
|
self.opts['hostname'] = cfg[k]['hostname']
|
||||||
if cfg[k]['sans']:
|
if cfg[k]['sans']:
|
||||||
self.opts['sans'] = cfg[k]['sans']
|
self.opts['sans'] = cfg[k]['sans']
|
||||||
|
@ -252,9 +261,9 @@ class Certificate:
|
||||||
"""
|
"""
|
||||||
with open(mkFile, "w") as f:
|
with open(mkFile, "w") as f:
|
||||||
if ".csr" in mkFile:
|
if ".csr" in mkFile:
|
||||||
f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, request))
|
f.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, request).decode())
|
||||||
elif ".key" in mkFile:
|
elif ".key" in mkFile:
|
||||||
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, request))
|
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, request).decode())
|
||||||
else:
|
else:
|
||||||
self.output("[!] Failed to create CSR/Key files", level=logging.ERROR)
|
self.output("[!] Failed to create CSR/Key files", level=logging.ERROR)
|
||||||
|
|
||||||
|
@ -313,8 +322,8 @@ def main(argv):
|
||||||
parser.add_argument("-k", "--keysize", help="Provide the key size", action="store", default="2048")
|
parser.add_argument("-k", "--keysize", help="Provide the key size", action="store", default="2048")
|
||||||
parser.add_argument("-u", "--unattended", help="Load CSR predefined options", action="store", default="")
|
parser.add_argument("-u", "--unattended", help="Load CSR predefined options", action="store", default="")
|
||||||
parser.add_argument("-f", "--file", help="Load hosts file (CN and optional Alternate Names) list", action="store", default="")
|
parser.add_argument("-f", "--file", help="Load hosts file (CN and optional Alternate Names) list", action="store", default="")
|
||||||
parser.add_argument("-a", "--authority", help="Generate Authority certificate (Default is server)", action="store_true")
|
parser.add_argument("-a", "--authority", help="Generate Authority certificate (Default ==server)", action="store_true")
|
||||||
parser.add_argument("-c", "--client", help="Generate client certificate (Default is server)", action="store_true")
|
parser.add_argument("-c", "--client", help="Generate client certificate (Default ==server)", action="store_true")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
setuptools==39.1.0
|
setuptools==45.2.0
|
||||||
pyOpenSSL==18.0.0
|
pyOpenSSL==19.1.0
|
||||||
PyYAML==5.1
|
PyYAML==5.3.1
|
||||||
|
ipaddress==1.0.23
|
Loading…
Reference in New Issue