From 0b05ab06e5bdb610f0ea1a6a3b8a7dac675d5747 Mon Sep 17 00:00:00 2001 From: aewens Date: Mon, 31 Dec 2018 14:48:02 -0600 Subject: [PATCH] Updated license file, added ssl cert tool --- LICENSE | 2 +- bin/sslca | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 bin/sslca diff --git a/LICENSE b/LICENSE index 547d63d..6b6375d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) . 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. diff --git a/bin/sslca b/bin/sslca new file mode 100644 index 0000000..0797087 --- /dev/null +++ b/bin/sslca @@ -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() \ No newline at end of file