Updated license file, added ssl cert tool
This commit is contained in:
parent
5a34bacaf5
commit
0b05ab06e5
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) <year> <owner> . All rights reserved.
|
||||
Copyright (c) 2018-2019 Austin Ewens. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from os import system, popen, makedirs
|
||||
from os.path import exists, dirname, isfile, realpath
|
||||
from sys import exit
|
||||
from errno import EEXIST
|
||||
from string import digits, ascii_uppercase as upper
|
||||
from random import SystemRandom
|
||||
from argparse import ArgumentParser
|
||||
from ConfigParser import ConfigParser
|
||||
from getpass import getpass
|
||||
|
||||
class SSLCA:
|
||||
_path = "%s/etc" % dirname(dirname(realpath(__file__)))
|
||||
ver = "0.1.0"
|
||||
|
||||
def __init__(self):
|
||||
self.config = ConfigParser()
|
||||
|
||||
if not exists(self._path):
|
||||
try:
|
||||
makedirs(self._path)
|
||||
except OSError as e:
|
||||
if e.errno != EEXIST:
|
||||
raise
|
||||
|
||||
config_file = "%s/sslca.conf" % self._path
|
||||
if not exists(config_file):
|
||||
print("Config file missing, answer prompts to generate:\n")
|
||||
country = raw_input("Country Name (2 letter code): ")
|
||||
state = raw_input("State or Province Name (full name): ")
|
||||
city = raw_input("Locality Name (eg, city): ")
|
||||
org_name = raw_input("Organization Name (eg, company): ")
|
||||
org_unit = raw_input("Organization Unit Name (eg, section): ")
|
||||
common_name = raw_input(
|
||||
"Common Name (eg, your name of your server's hostname): ")
|
||||
email = raw_input("Email Address: ")
|
||||
path = self._is_path(raw_input("Absolute path for certificates: "))
|
||||
bits = self._is_number(raw_input("Bits used by certificates: "))
|
||||
days = self._is_number(raw_input(
|
||||
"Days certifcates will be valid: "))
|
||||
self.config.add_section("ssl")
|
||||
self.config.add_section("defaults")
|
||||
self.config.set("ssl", "country", country)
|
||||
self.config.set("ssl", "state", state)
|
||||
self.config.set("ssl", "city", city)
|
||||
self.config.set("ssl", "org_name", org_name)
|
||||
self.config.set("ssl", "org_unit", org_unit)
|
||||
self.config.set("ssl", "common_name", common_name)
|
||||
self.config.set("ssl", "email", email)
|
||||
self.config.set("defaults", "path", path)
|
||||
self.config.set("defaults", "bits", bits)
|
||||
self.config.set("defaults", "days", days)
|
||||
with open(config_file, "w+b") as cfgfile:
|
||||
self.config.write(cfgfile)
|
||||
print("\nConfig file generated at: %s" % config_file)
|
||||
|
||||
self.config.read(config_file)
|
||||
|
||||
country = self.config.get("ssl", "country")
|
||||
state = self.config.get("ssl", "state")
|
||||
city = self.config.get("ssl", "city")
|
||||
org_name = self.config.get("ssl", "org_name")
|
||||
org_unit = self.config.get("ssl", "org_unit")
|
||||
common_name = self.config.get("ssl", "common_name")
|
||||
email = self.config.get("ssl", "email")
|
||||
self.subj = "/C=%s/ST=%s/L=%s/O=%s/OU=%s/emailAddress=%s" % (
|
||||
country, state, city, org_name, org_unit, email)
|
||||
self.subj_ca = "%s/CN=%s" % (self.subj, common_name)
|
||||
|
||||
self.bits = self.config.get("defaults", "bits")
|
||||
self.path = self.config.get("defaults", "path")
|
||||
self.days = self.config.get("defaults", "days")
|
||||
|
||||
def _is_number(self, value):
|
||||
if not value.isdigit() or "." in value:
|
||||
return None
|
||||
return int(value)
|
||||
|
||||
def _is_path(self, value):
|
||||
if not exists(value):
|
||||
return None
|
||||
return value
|
||||
|
||||
def _exec_cmd(self, command):
|
||||
system(command)
|
||||
|
||||
def _exec_cmd_get(self, command):
|
||||
return popen(command).read()
|
||||
|
||||
def _offer_randpass(self, n=13):
|
||||
chars = (SystemRandom().choice(upper + digits) for _ in range(n))
|
||||
return "".join(chars)
|
||||
|
||||
def _make_cert(self, *args, **kwargs):
|
||||
name = kwargs.get("name", None)
|
||||
path = kwargs.get("path", self.path)
|
||||
bits = kwargs.get("bits", self.bits)
|
||||
subj = kwargs.get("subj", self.subj)
|
||||
aesb = kwargs.get("aesb", "")
|
||||
pswd = kwargs.get("pswd", False)
|
||||
path = path[:-1] if path[-1] == "/" else path
|
||||
if name is None:
|
||||
print("Error: You must specify the name of the certificate")
|
||||
print("For help with commands, use --help")
|
||||
sys.exit(1)
|
||||
|
||||
loc = "%s/%s" % (path, name)
|
||||
pout = "-passout pass:%s" % pswd if pswd else ""
|
||||
pin = "-passin pass:%s" % pswd if pswd else ""
|
||||
|
||||
cmd1 = "openssl genrsa %s %s -out %s.key %s" % (aesb, pout, loc, bits)
|
||||
cmd2 = ("openssl req -new -key %s.key -out %s.csr -subj '%s' %s" %
|
||||
(loc, loc, subj, pin))
|
||||
|
||||
self._exec_cmd(cmd1)
|
||||
self._exec_cmd(cmd2)
|
||||
|
||||
def usage(self):
|
||||
description = "Automates CA certificate generation and signing"
|
||||
parser = ArgumentParser(prog="sslca", description=description)
|
||||
parser.add_argument("-v", "--version", dest="version",
|
||||
action="version", version="%(prog)s " + self.ver)
|
||||
subparsers = parser.add_subparsers(title="sub-commands", dest="subs")
|
||||
subparsers.required = False
|
||||
self.usage_ca(subparsers)
|
||||
self.usage_cert(subparsers)
|
||||
self.usage_sign(subparsers)
|
||||
return parser
|
||||
|
||||
def usage_ca(self, subparsers):
|
||||
ca_parser = subparsers.add_parser("ca", help="create CA certificates")
|
||||
ca_parser.add_argument("-n", "--name", dest="ca_name", required=True,
|
||||
help="specify name of CA files")
|
||||
ca_parser.add_argument("-p", "--path", dest="ca_path",
|
||||
default=self.path, help="specify path for CA files")
|
||||
ca_parser.add_argument("-b", "--bits", dest="ca_bits",
|
||||
default=self.bits, help="specify the number of bits used")
|
||||
ca_parser.add_argument("-d", "--days", dest="ca_days",
|
||||
default=self.days, help="specify the number of days valid")
|
||||
ca_parser.add_argument("-P", "--pswd", dest="ca_pswd", default=None,
|
||||
help="non-interactively specify passphrase for signing key")
|
||||
ca_parser.add_argument("-a", "--auto", dest="ca_auto",
|
||||
action="store_true",
|
||||
help="automate key passphrase using random password")
|
||||
|
||||
def usage_cert(self, subparsers):
|
||||
cert_parser = subparsers.add_parser("cert",
|
||||
help="generate normal SSL certificates")
|
||||
cert_parser.add_argument("-n", "--name", dest="name", required=True,
|
||||
help="specify name of certificate files")
|
||||
cert_parser.add_argument("-p", "--path", dest="path",
|
||||
default=self.path, help="specify name of certicicate files")
|
||||
cert_parser.add_argument("-b", "--bits", dest="bits",
|
||||
default=self.bits, help="specify the number of bits used")
|
||||
cert_parser.add_argument("-H", "--host", dest="host", required=True,
|
||||
help="specify the host for certificate files")
|
||||
|
||||
def usage_sign(self, subparsers):
|
||||
sign_parser = subparsers.add_parser("sign",
|
||||
help="sign certificate with CA")
|
||||
sign_parser.add_argument("-a", "--ca", dest="ca", required=True,
|
||||
help="specify name of CA used for signing")
|
||||
sign_parser.add_argument("-c", "--cert", dest="cert", required=True,
|
||||
help="specify name of certificate to be signed")
|
||||
sign_parser.add_argument("-p", "--path", dest="path", default=self.path,
|
||||
help="specify path for signed certificate files")
|
||||
sign_parser.add_argument("-b", "--bits", dest="bits", default=self.bits,
|
||||
help="specify the number of bits used")
|
||||
sign_parser.add_argument("-d", "--days", dest="days", default=self.days,
|
||||
help="specify the number of days valid")
|
||||
sign_parser.add_argument("-P", "--pswd", dest="pswd", default=None,
|
||||
help="non-interactively specify passphrase for signing key")
|
||||
|
||||
def get_arguments(self, parser):
|
||||
return parser.parse_args()
|
||||
|
||||
def get_help(self, parser):
|
||||
return parser.print_help
|
||||
|
||||
def inject(self, arguments, helper):
|
||||
command = arguments.subs
|
||||
if command == "ca":
|
||||
return self.gen_ca_cert(arguments)
|
||||
elif command == "cert":
|
||||
return self.gen_cert(arguments)
|
||||
elif command == "sign":
|
||||
return self.sign_cert(arguments)
|
||||
helper()
|
||||
|
||||
def gen_cert(self, arguments):
|
||||
name = arguments.name
|
||||
host = arguments.host
|
||||
bits = arguments.bits
|
||||
path = arguments.path
|
||||
path = path[:-1] if path[-1] == "/" else path
|
||||
loc = "%s/%s" % (path, name)
|
||||
subj = "%s/CN=%s" % (self.subj, host)
|
||||
kwargs = dict()
|
||||
kwargs["name"] = name
|
||||
kwargs["path"] = path
|
||||
kwargs["bits"] = bits
|
||||
kwargs["subj"] = subj
|
||||
self._make_cert(**kwargs)
|
||||
print("\n\nCreated certificate pair: %s.key, %s.csr" % (loc, loc))
|
||||
|
||||
def gen_ca_cert(self, arguments):
|
||||
name = arguments.ca_name
|
||||
path = arguments.ca_path
|
||||
days = arguments.ca_days
|
||||
bits = arguments.ca_bits
|
||||
auto = arguments.ca_auto
|
||||
pswd = arguments.ca_pswd
|
||||
subj = self.subj_ca
|
||||
path = path[:-1] if path[-1] == "/" else path
|
||||
|
||||
rand = self._offer_randpass()
|
||||
pswd = None
|
||||
kwargs = dict()
|
||||
if not (auto or pswd):
|
||||
print("Suggested Random Password: %s\n" % rand)
|
||||
|
||||
def request_passphrase():
|
||||
passphrase1 = getpass("Enter passphrase: ")
|
||||
passphrase2 = getpass("Repeat passphrase: ")
|
||||
|
||||
if passphrase1 == passphrase2:
|
||||
return passphrase1
|
||||
else:
|
||||
return request_passphrase()
|
||||
|
||||
pswd = request_passphrase()
|
||||
else:
|
||||
pswd = pswd if pswd else rand
|
||||
|
||||
kwargs["name"] = name
|
||||
kwargs["path"] = path
|
||||
kwargs["bits"] = bits
|
||||
kwargs["subj"] = subj
|
||||
kwargs["pswd"] = pswd
|
||||
kwargs["aesb"] = "-aes256"
|
||||
|
||||
self._make_cert(**kwargs)
|
||||
|
||||
loc = "%s/%s" % (path, name)
|
||||
pin = "-passin pass:%s" % pswd if auto or pswd else ""
|
||||
cmd = "openssl x509 -req -days %s -in %s.csr" % (days, loc)
|
||||
cmd = "%s -signkey %s.key -out %s.crt %s" % (cmd, loc, loc, pin)
|
||||
self._exec_cmd(cmd)
|
||||
|
||||
print("\n\nCreated CA certificate: %s.crt" % loc)
|
||||
if auto:
|
||||
print("Random Pasword Used (SAVE THIS!!!): %s" % pswd)
|
||||
|
||||
def sign_cert(self, arguments):
|
||||
ca = arguments.ca
|
||||
cert = arguments.cert
|
||||
path = arguments.path
|
||||
bits = arguments.bits
|
||||
days = arguments.days
|
||||
pswd = arguments.pswd
|
||||
path = path[:-1] if path[-1] == "/" else path
|
||||
|
||||
loc = "%s/%s" % (path, cert)
|
||||
ca_loc = "%s/%s" % (path, ca)
|
||||
pin = "-passin pass:%s" % pswd if pswd else ""
|
||||
cmd = "openssl x509 -req -days %s -in %s.csr" % (days, loc)
|
||||
cmd = "%s -CA %s.crt -CAkey %s.key" % (cmd, ca_loc, ca_loc)
|
||||
cmd = "%s -CAcreateserial -out %s.crt %s" % (cmd, loc, pin)
|
||||
self._exec_cmd(cmd)
|
||||
|
||||
if isfile("%s/%s.crt" % (path, cert)):
|
||||
print("\n\nSigned certificate: %s.crt" % loc)
|
||||
else:
|
||||
print("Error: Something went wrong, check that files exist")
|
||||
exit(1)
|
||||
|
||||
## Entry point
|
||||
def main():
|
||||
sslca = SSLCA()
|
||||
parser = sslca.usage()
|
||||
arguments = sslca.get_arguments(parser)
|
||||
helper = sslca.get_help(parser)
|
||||
sslca.inject(arguments, helper)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue