diff --git a/README.rst b/README.rst index 0cfd4a6..d77169d 100644 --- a/README.rst +++ b/README.rst @@ -12,15 +12,14 @@ Introduction ------------ Certidude is a novel X.509 Certificate Authority management tool -with privilege isolation mechanism and Kerberos authentication aiming to -eventually support PKCS#11 and in far future WebCrypto. - -.. figure:: doc/usecase-diagram.png - -Certidude is mainly designed for StrongSwan and OpenVPN gateway operators to make +with privilege isolation mechanism and Kerberos authentication +mainly designed for OpenVPN gateway operators to make VPN client setup on laptops, desktops and mobile devices as painless as possible. -Certidude can also be used to manage HTTPS client certificates for -eg. maintaining an extra layer of protection for intranet websites. + +.. figure:: doc/certidude.png + +Certidude can also be used to manage IPSec certifcates (StrongSwan) +or HTTPS client certificates to limit access to eg. intranet websites. For a full-blown CA you might want to take a look at `EJBCA `_ or `OpenCA `_. @@ -29,6 +28,8 @@ For a full-blown CA you might want to take a look at Usecases -------- +.. figure:: doc/usecase-diagram.png + Following usecases are covered: * I am a sysadmin. Employees with different operating systems need to access @@ -79,6 +80,7 @@ Common: Virtual private networking: +* Send OpenVPN profile URL tokens via e-mail, for simplified VPN adoption on Android, iOS, Windows, Mac OS X and Ubuntu. * OpenVPN integration, check out ``certidude setup openvpn server`` and ``certidude setup openvpn client``. * strongSwan integration, check out ``certidude setup strongswan server`` and ``certidude setup strongswan client``. * NetworkManager integration, check out ``certidude setup openvpn networkmanager`` and ``certidude setup strongswan networkmanager``. @@ -95,7 +97,7 @@ TODO * `OCSP `_ support, needs a bit hacking since OpenSSL wrappers are not exposing the functionality. * `SCEP `_ support, a client implementation available `here `_. Not sure if we can implement server-side events within current standard. * WebCrypto support, meanwhile check out `hwcrypto.js `_. -* Ability to send OpenVPN profile URL tokens via e-mail, for simplified VPN adoption. +* Use `pki.js `_ for generating keypair in the browser when claiming a token. * Signer process logging. diff --git a/certidude/authority.py b/certidude/authority.py index d7b016b..d1a5500 100644 --- a/certidude/authority.py +++ b/certidude/authority.py @@ -120,7 +120,7 @@ def revoke(common_name): attach_cert = buf, "application/x-pem-file", common_name + ".crt" mailer.send("certificate-revoked.md", attachments=(attach_cert,), - serial_number="%x" % cert.serial, + serial_hex="%x" % cert.serial, common_name=common_name) return revoked_path @@ -298,11 +298,15 @@ def _sign(csr, buf, overwrite=False): from xattr import getxattr, listxattr, setxattr common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME) - cert_path = os.path.join(config.SIGNED_DIR, common_name.value + ".pem") + cert_path = os.path.join(config.SIGNED_DIR, "%s.pem" % common_name.value) renew = False - signed_path = os.path.join(config.SIGNED_DIR, "%s.pem" % common_name.value) + attachments = [ + (buf, "application/x-pem-file", common_name.value + ".csr"), + ] + revoked_path = None + overwritten = False # Move existing certificate if necessary if os.path.exists(cert_path): @@ -313,12 +317,12 @@ def _sign(csr, buf, overwrite=False): renew = prev.public_key().public_numbers() == csr.public_key().public_numbers() if overwrite: - if renew: - # TODO: is this the best approach? - revoked_path = os.path.join(config.REVOKED_DIR, "%x.pem" % prev.serial) - os.rename(signed_path, revoked_path) - else: - revoked_path = revoke(common_name.value) + # TODO: is this the best approach? + prev_serial_hex = "%x" % prev.serial + revoked_path = os.path.join(config.REVOKED_DIR, "%s.pem" % prev_serial_hex) + os.rename(cert_path, revoked_path) + attachments += [(prev_buf, "application/x-pem-file", "deprecated.crt" if renew else "overwritten.crt")] + overwritten = True else: raise EnvironmentError("Will not overwrite existing certificate") @@ -328,42 +332,21 @@ def _sign(csr, buf, overwrite=False): with open(cert_path + ".part", "wb") as fh: fh.write(cert_buf) os.rename(cert_path + ".part", cert_path) + attachments.append((cert_buf, "application/x-pem-file", common_name.value + ".crt")) + cert_serial_hex = "%x" % cert.serial # Copy filesystem attributes to newly signed certificate if revoked_path: for key in listxattr(revoked_path): if not key.startswith("user."): continue - setxattr(signed_path, key, getxattr(revoked_path, key)) + setxattr(cert_path, key, getxattr(revoked_path, key)) # Send mail - recipient = None - - if renew: - mailer.send( - "certificate-renewed.md", - to=recipient, - attachments=( - (prev_buf, "application/x-pem-file", "deprecated.crt"), - (cert_buf, "application/x-pem-file", common_name.value + ".crt") - ), - serial_number="%x" % cert.serial, - common_name=common_name.value, - certificate=cert, - ) - else: - mailer.send( - "certificate-signed.md", - to=recipient, - attachments=( - (buf, "application/x-pem-file", common_name.value + ".csr"), - (cert_buf, "application/x-pem-file", common_name.value + ".crt") - ), - serial_number="%x" % cert.serial, - common_name=common_name.value, - certificate=cert, - ) - + if renew: # Same keypair + mailer.send("certificate-renewed.md", **locals()) + else: # New keypair + mailer.send("certificate-signed.md", **locals()) if config.LONG_POLL_PUBLISH: url = config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest() diff --git a/certidude/templates/mail/certificate-renewed.md b/certidude/templates/mail/certificate-renewed.md index 7ae1b68..817379d 100644 --- a/certidude/templates/mail/certificate-renewed.md +++ b/certidude/templates/mail/certificate-renewed.md @@ -1,9 +1,9 @@ -Renewed {{ common_name }} ({{ serial_number }}) +Renewed {{ common_name.value }} ({{ cert_serial_hex }}) -This is simply to notify that certificate for {{ common_name }} -was renewed and the serial number of the new certificate is {{ serial_number }}. +This is simply to notify that certificate for {{ common_name.value }} +was renewed and the serial number of the new certificate is {{ cert_serial_hex }}. -The new certificate is valid from {{ certificate.not_valid_before }} until -{{ certificate.not_valid_after }}. +The new certificate is valid from {{ cert.not_valid_before }} until +{{ cert.not_valid_after }}. Services making use of those certificates should continue working as expected. diff --git a/certidude/templates/mail/certificate-revoked.md b/certidude/templates/mail/certificate-revoked.md index a56a371..fad903e 100644 --- a/certidude/templates/mail/certificate-revoked.md +++ b/certidude/templates/mail/certificate-revoked.md @@ -1,4 +1,4 @@ -Revoked {{ common_name }} ({{ serial_number }}) +Revoked {{ common_name }} ({{ serial_hex }}) This is simply to notify that certificate {{ common_name }} was revoked. diff --git a/certidude/templates/mail/certificate-signed.md b/certidude/templates/mail/certificate-signed.md index e0ae396..e2abdab 100644 --- a/certidude/templates/mail/certificate-signed.md +++ b/certidude/templates/mail/certificate-signed.md @@ -1,11 +1,14 @@ -Signed {{ common_name }} ({{ serial_number }}) +Signed {{ common_name.value }} ({{ cert_serial_hex }}) -This is simply to notify that certificate {{ common_name }} -with serial number {{ serial_number }} +This is simply to notify that certificate {{ common_name.value }} +with serial number {{ cert_serial_hex }} was signed{% if signer %} by {{ signer }}{% endif %}. -The certificate is valid from {{ certificate.not_valid_before }} until -{{ certificate.not_valid_after }}. +The certificate is valid from {{ cert.not_valid_before }} until +{{ cert.not_valid_after }}. -Any existing certificates with the same common name were rejected by doing so -and services making use of those certificates might become unavailable. +{% if overwritten %} +By doing so existing certificate with the same common name +and serial number {{ prev_serial_hex }} was rejected +and services making use of that certificate might become unavailable. +{% endif %} diff --git a/certidude/templates/mail/token.md b/certidude/templates/mail/token.md index fcf7c15..56a4be1 100644 --- a/certidude/templates/mail/token.md +++ b/certidude/templates/mail/token.md @@ -1,4 +1,4 @@ -Token for setting up VPN +Token for {{ user.name }} {{ issuer }} has provided {{ user }} a token for retrieving profile from the link below. diff --git a/doc/certidude.png b/doc/certidude.png new file mode 100644 index 0000000..2948735 Binary files /dev/null and b/doc/certidude.png differ