From 1925207a6da5ebd8a58c3ff877e50851cf93db03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Wed, 25 Jan 2017 11:34:08 +0000 Subject: [PATCH] Add OpenVPN bundle generation --- certidude/api/bundle.py | 19 ++++++++---- certidude/authority.py | 35 ++++++++++++++++++++++- certidude/config.py | 3 ++ certidude/templates/certidude-server.conf | 5 ++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/certidude/api/bundle.py b/certidude/api/bundle.py index 1cc6243..0249640 100644 --- a/certidude/api/bundle.py +++ b/certidude/api/bundle.py @@ -12,6 +12,8 @@ KEYWORDS = ( (u"iPhone", u"iphone"), (u"iPad", u"ipad"), (u"Ubuntu", u"ubuntu"), + (u"Fedora", u"fedora"), + (u"Linux", u"linux"), ) class BundleResource(object): @@ -29,8 +31,15 @@ class BundleResource(object): hashlib.sha256(req.user_agent).hexdigest()[:8]) logger.info(u"Signing bundle %s for %s", common_name, req.context.get("user")) - resp.set_header("Content-Type", "application/x-pkcs12") - resp.set_header("Content-Disposition", "attachment; filename=%s.p12" % common_name.encode("ascii")) - resp.body, cert = authority.generate_pkcs12_bundle(common_name, - owner=req.context.get("user")) - + if config.BUNDLE_FORMAT == "p12": + resp.set_header("Content-Type", "application/x-pkcs12") + resp.set_header("Content-Disposition", "attachment; filename=%s.p12" % common_name.encode("ascii")) + resp.body, cert = authority.generate_pkcs12_bundle(common_name, + owner=req.context.get("user")) + elif config.BUNDLE_FORMAT == "ovpn": + resp.set_header("Content-Type", "application/x-openvpn") + resp.set_header("Content-Disposition", "attachment; filename=%s.ovpn" % common_name.encode("ascii")) + resp.body, cert = authority.generate_ovpn_bundle(common_name, + owner=req.context.get("user")) + else: + raise ValueError("Unknown bundle format %s" % config.BUNDLE_FORMAT) diff --git a/certidude/authority.py b/certidude/authority.py index 237ef8a..aea94f6 100644 --- a/certidude/authority.py +++ b/certidude/authority.py @@ -14,6 +14,7 @@ from cryptography.hazmat.primitives import hashes, serialization from certidude import config, push, mailer, const from certidude.wrappers import Certificate, Request from certidude import errors +from jinja2 import Template RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(@(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))?$" @@ -21,7 +22,6 @@ RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z # https://jamielinux.com/docs/openssl-certificate-authority/ # http://pycopia.googlecode.com/svn/trunk/net/pycopia/ssl/certs.py - # Cache CA certificate certificate = Certificate(open(config.AUTHORITY_CERTIFICATE_PATH)) @@ -185,6 +185,39 @@ def delete_request(common_name): requests.delete(config.PUSH_PUBLISH % request.fingerprint(), headers={"User-Agent": "Certidude API"}) +def generate_ovpn_bundle(common_name, owner=None): + # Construct private key + click.echo("Generating 4096-bit RSA key...") + + key = rsa.generate_private_key( + public_exponent=65537, + key_size=4096, + backend=default_backend() + ) + + csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ + x509.NameAttribute(k, v) for k, v in ( + (NameOID.COMMON_NAME, common_name), + (NameOID.GIVEN_NAME, owner and owner.given_name), + (NameOID.SURNAME, owner and owner.surname), + ) if v + ])) + + # Sign CSR + cert = sign(Request( + csr.sign(key, hashes.SHA512(), default_backend()).public_bytes(serialization.Encoding.PEM)), overwrite=True) + + bundle = Template(open(config.OPENVPN_BUNDLE_TEMPLATE).read()).render( + ca = certificate.dump(), + key = key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + ), + cert = cert.dump(), + crl=export_crl(), + ) + return bundle, cert def generate_pkcs12_bundle(common_name, key_size=4096, owner=None): """ diff --git a/certidude/config.py b/certidude/config.py index 7fed3d6..3da5af7 100644 --- a/certidude/config.py +++ b/certidude/config.py @@ -40,6 +40,9 @@ SIGNED_DIR = cp.get("authority", "signed dir") REVOKED_DIR = cp.get("authority", "revoked dir") OUTBOX = cp.get("authority", "outbox") +BUNDLE_FORMAT = cp.get("authority", "bundle format") +OPENVPN_BUNDLE_TEMPLATE = cp.get("authority", "openvpn bundle template") + USER_CERTIFICATE_ENROLLMENT = { "forbidden": False, "single allowed": True, "multiple allowed": True }[ cp.get("authority", "user certificate enrollment")] diff --git a/certidude/templates/certidude-server.conf b/certidude/templates/certidude-server.conf index 7866be0..8b43a4c 100644 --- a/certidude/templates/certidude-server.conf +++ b/certidude/templates/certidude-server.conf @@ -95,3 +95,8 @@ revoked dir = {{ directory }}/revoked/ expired dir = {{ directory }}/expired/ outbox = {{ outbox }} +bundle format = p12 +;bundle format = ovpn + +openvpn bundle template = /etc/certidude/template.ovpn +