From 7594756236939806215e5f9c13d7037316bf59b8 Mon Sep 17 00:00:00 2001 From: Taylor Brazelton Date: Wed, 10 Jan 2018 13:19:42 -0500 Subject: [PATCH 1/5] Added file that will be the refactored code. --- main.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..17eeabc --- /dev/null +++ b/main.py @@ -0,0 +1,25 @@ + +from collections import namedtuple + +CertAttributes = namedtuple('CertAttributes', [ + 'country', + 'state', + 'city', + 'org', + 'org_name', + 'common' +]) + + +class Cert(): + """Cert class helps in the generation of SSL certificates.""" + + def __init__(self, attributes=None): + + # Accept the attributes that define the certs. + if attributes: + self.attributes = attributes + + + def gen_self_signed_cert(): + """Generates a self signed cert.""" \ No newline at end of file From 5fe3858f8ce12790cfeaca5a6b7b7ae46c72dbbe Mon Sep 17 00:00:00 2001 From: Taylor Brazelton Date: Wed, 10 Jan 2018 13:46:16 -0500 Subject: [PATCH 2/5] Getting closer to feature complete. --- main.py | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 17eeabc..cb6edf6 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,16 @@ - +import tempfile +import logging from collections import namedtuple +from datetime import datetime, timedelta + +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import rsa + +LOGGER = logging.getLogger(__name__) +LOGGER.addHandler(logging.NullHandler()) CertAttributes = namedtuple('CertAttributes', [ 'country', @@ -20,6 +31,127 @@ def __init__(self, attributes=None): if attributes: self.attributes = attributes + def _private_key(): + """Generates a private key.""" + return rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=default_backend() + ) + + def _certificate(self, certattrs, ca_cert=False): + """Generate a certificate.""" + if not certattrs: + certattrs = self.attributes + + key = self._private_key() + + # Create certificate and sign it. + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, certattrs.country), + x509.NameAttribute( + NameOID.STATE_OR_PROVINCE_NAME, certattrs.state), + x509.NameAttribute(NameOID.LOCALITY_NAME, certattrs.city), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, certattrs.org), + x509.NameAttribute( + NameOID.ORGANIZATIONAL_UNIT_NAME, certattrs.org_name), + x509.NameAttribute(NameOID.COMMON_NAME, certattrs.common), + ]) + cert = x509.CertificateBuilder()\ + .subject_name(subject)\ + .issuer_name(issuer)\ + .public_key(key.public_key())\ + .serial_number(x509.random_serial_number())\ + .not_valid_before(datetime.utcnow())\ + .not_valid_after(datetime.utcnow() + timedelta(days=3650))\ + .add_extension( + x509.SubjectAlternativeName([x509.DNSName(certattrs.common)]), + critical=False + ) + + if ca_cert: + cert = cert.add_extension( + x509.BasicConstraints( + ca=True, + path_length=1 + ), + critical=True + ) + cert = cert.sign(key, hashes.SHA256(), default_backend()) + + return key, cert + + def gen_self_signed_cert(self): + """Generates a self signed cert.""" + + return self._certificate(self.attributes) + + def gen_ca_cert(self): + """ + Generate a key + certificate authority certificate. + + Creates a CA cert for the proxy to use when bumping SSL. This certificate + is also used to sign an SSL server certificate for the proxy web interface. + This leverages the trust that must be in place for the ca certificate. + """ + return self._certificate(self.attributes, ca_cert=True) + + def gen_csr(self): + """ + Generates a CSR that is based on the passed in key. + + Returns a private key and a CSR. + """ + certattrs = self.attributes + + key = self.private_key() + + # Generate a CSR. + csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ + # Pass in the callers attributes. + x509.NameAttribute( + NameOID.COUNTRY_NAME, certattrs.country), + x509.NameAttribute( + NameOID.STATE_OR_PROVINCE_NAME, certattrs.state), + x509.NameAttribute(NameOID.LOCALITY_NAME, certattrs.city), + x509.NameAttribute( + NameOID.ORGANIZATION_NAME, certattrs.org), + x509.NameAttribute( + NameOID.ORGANIZATIONAL_UNIT_NAME, certattrs.org_name), + x509.NameAttribute(NameOID.COMMON_NAME, certattrs.common), + ])).add_extension( + x509.SubjectAlternativeName([ + x509.DNSName(certattrs.common), + ]), + critical=False, + ) + + # Sign CSR with our private key and return it. + return key, csr.sign(key, hashes.SHA256(), default_backend()) + + # DISK SAVING FUNCTIONS + + def save_key(fobj, key, format='key'): + """Save a key file.""" + LOGGER.debug('Writing PEM encoded key to %s', fobj.name) + fobj.write( + key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + ) + ) + fobj.write(b'\n') + + def save_cert(fobj, cert, format='pem'): + """Save a cert.""" + LOGGER.debug('Writing PEM encoded certificate to %s', fobj.name) + fobj.write(cert.public_bytes(serialization.Encoding.PEM)) + fobj.write(b'\n') + + def save_csr(fobj, csr, format='pem'): + """Save a csr.""" + LOGGER.debug('Writing PEM encoded CSR to %s', fobj.name) + fobj.write(csr.public_bytes(serialization.Encoding.PEM)) + fobj.write(b'\n') - def gen_self_signed_cert(): - """Generates a self signed cert.""" \ No newline at end of file From 41ce6dfb39775d03888d7f24efba7df0c53ce615 Mon Sep 17 00:00:00 2001 From: Taylor Brazelton Date: Mon, 15 Jan 2018 11:19:51 -0500 Subject: [PATCH 3/5] Refactored the Name object. --- main.py | 55 ++++++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/main.py b/main.py index cb6edf6..777705e 100644 --- a/main.py +++ b/main.py @@ -12,14 +12,27 @@ LOGGER = logging.getLogger(__name__) LOGGER.addHandler(logging.NullHandler()) -CertAttributes = namedtuple('CertAttributes', [ + +class CertAttributes(namedtuple('CertAttributes', [ 'country', 'state', 'city', 'org', 'org_name', 'common' -]) +])): + def to_x509(self): + """Convert this namedtuple to an x509 named object.""" + return x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, self.country), + x509.NameAttribute( + NameOID.STATE_OR_PROVINCE_NAME, self.state), + x509.NameAttribute(NameOID.LOCALITY_NAME, self.city), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, self.org), + x509.NameAttribute( + NameOID.ORGANIZATIONAL_UNIT_NAME, self.org_name), + x509.NameAttribute(NameOID.COMMON_NAME, self.common), + ]) class Cert(): @@ -47,16 +60,7 @@ def _certificate(self, certattrs, ca_cert=False): key = self._private_key() # Create certificate and sign it. - subject = issuer = x509.Name([ - x509.NameAttribute(NameOID.COUNTRY_NAME, certattrs.country), - x509.NameAttribute( - NameOID.STATE_OR_PROVINCE_NAME, certattrs.state), - x509.NameAttribute(NameOID.LOCALITY_NAME, certattrs.city), - x509.NameAttribute(NameOID.ORGANIZATION_NAME, certattrs.org), - x509.NameAttribute( - NameOID.ORGANIZATIONAL_UNIT_NAME, certattrs.org_name), - x509.NameAttribute(NameOID.COMMON_NAME, certattrs.common), - ]) + subject = issuer = certattrs.to_x509() cert = x509.CertificateBuilder()\ .subject_name(subject)\ .issuer_name(issuer)\ @@ -107,24 +111,14 @@ def gen_csr(self): key = self.private_key() # Generate a CSR. - csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ - # Pass in the callers attributes. - x509.NameAttribute( - NameOID.COUNTRY_NAME, certattrs.country), - x509.NameAttribute( - NameOID.STATE_OR_PROVINCE_NAME, certattrs.state), - x509.NameAttribute(NameOID.LOCALITY_NAME, certattrs.city), - x509.NameAttribute( - NameOID.ORGANIZATION_NAME, certattrs.org), - x509.NameAttribute( - NameOID.ORGANIZATIONAL_UNIT_NAME, certattrs.org_name), - x509.NameAttribute(NameOID.COMMON_NAME, certattrs.common), - ])).add_extension( - x509.SubjectAlternativeName([ - x509.DNSName(certattrs.common), - ]), - critical=False, - ) + csr = x509.CertificateSigningRequestBuilder().subject_name( + certattrs.to_x509() + ).add_extension( + x509.SubjectAlternativeName([ + x509.DNSName(certattrs.common), + ]), + critical=False, + ) # Sign CSR with our private key and return it. return key, csr.sign(key, hashes.SHA256(), default_backend()) @@ -154,4 +148,3 @@ def save_csr(fobj, csr, format='pem'): LOGGER.debug('Writing PEM encoded CSR to %s', fobj.name) fobj.write(csr.public_bytes(serialization.Encoding.PEM)) fobj.write(b'\n') - From f07cd8dd3a0d5a05c467a5c3dd8c7315f08ce66b Mon Sep 17 00:00:00 2001 From: Taylor Brazelton Date: Mon, 15 Jan 2018 11:27:45 -0500 Subject: [PATCH 4/5] Added checks to the save functions. --- main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.py b/main.py index 777705e..57366bc 100644 --- a/main.py +++ b/main.py @@ -127,6 +127,9 @@ def gen_csr(self): def save_key(fobj, key, format='key'): """Save a key file.""" + if format != 'key': + raise NotImplementedError( + '%s format unsupported, use .key' % format) LOGGER.debug('Writing PEM encoded key to %s', fobj.name) fobj.write( key.private_bytes( @@ -139,12 +142,18 @@ def save_key(fobj, key, format='key'): def save_cert(fobj, cert, format='pem'): """Save a cert.""" + if format != 'pem': + raise NotImplementedError( + '%s format unsupported, use .pem' % format) LOGGER.debug('Writing PEM encoded certificate to %s', fobj.name) fobj.write(cert.public_bytes(serialization.Encoding.PEM)) fobj.write(b'\n') def save_csr(fobj, csr, format='pem'): """Save a csr.""" + if format != 'pem': + raise NotImplementedError( + '%s format unsupported, use .pem' % format) LOGGER.debug('Writing PEM encoded CSR to %s', fobj.name) fobj.write(csr.public_bytes(serialization.Encoding.PEM)) fobj.write(b'\n') From cf454995d50d374f818f9afd58341032c0d27044 Mon Sep 17 00:00:00 2001 From: Taylor Brazelton Date: Mon, 15 Jan 2018 11:30:18 -0500 Subject: [PATCH 5/5] Give user a choice as to self sign. --- main.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 57366bc..3c7ad3c 100644 --- a/main.py +++ b/main.py @@ -38,12 +38,15 @@ def to_x509(self): class Cert(): """Cert class helps in the generation of SSL certificates.""" - def __init__(self, attributes=None): + def __init__(self, attributes=None, private_key=None): # Accept the attributes that define the certs. if attributes: self.attributes = attributes + if private_key: + self.private_key = private_key + def _private_key(): """Generates a private key.""" return rsa.generate_private_key( @@ -52,12 +55,15 @@ def _private_key(): backend=default_backend() ) - def _certificate(self, certattrs, ca_cert=False): + def _certificate(self, certattrs, ca_cert=False, self_sign=True): """Generate a certificate.""" if not certattrs: certattrs = self.attributes - key = self._private_key() + if self_sign: + key = self._private_key() + else: + key = self.private_key # Create certificate and sign it. subject = issuer = certattrs.to_x509() @@ -88,7 +94,7 @@ def _certificate(self, certattrs, ca_cert=False): def gen_self_signed_cert(self): """Generates a self signed cert.""" - return self._certificate(self.attributes) + return self._certificate(self.attributes, self_sign=True) def gen_ca_cert(self): """