ldap-users/sbin/sslca

288 lines
11 KiB
Python

#!/usr/bin/env python
from os import system, popen, makedirs, chmod
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)
chmod(config_file, 0700)
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()