From 2b5cc2054417a3deaea8bc2c4fa7cbcad1a27dc7 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Oct 10 2023 14:02:07 +0000 Subject: [PATCH 1/3] Implemenent limited x509_name_from_rfc4514_string Add support for versions of python-cryptography that don't have x509.Name.from_rfc4514_string. Our implementation is quite limited, and doesn't implement some values from RFC4514, but it does the most common ones. Signed-off-by: Patrick Uiterwijk --- diff --git a/src/server.py b/src/server.py index 4449012..9f1fdbb 100644 --- a/src/server.py +++ b/src/server.py @@ -2184,7 +2184,7 @@ def cmd_sign_certificate(db, conn): required=True, identifier=True, ) - subject_name = x509.Name.from_rfc4514_string( + subject_name = server_common.x509_name_from_rfc4514_string( conn.safe_outer_field("subject", required=True) ) serial_number = x509.random_serial_number() diff --git a/src/server_common.py b/src/server_common.py index e93b9d5..1f6ced2 100644 --- a/src/server_common.py +++ b/src/server_common.py @@ -26,7 +26,9 @@ import shutil import subprocess import tempfile +from cryptography import x509 import cryptography.hazmat.primitives.asymmetric.ec +from cryptography.x509.oid import NameOID import nss.nss import sqlalchemy @@ -710,3 +712,35 @@ def gpg_detached_signature(config, signature_file, cleartext_file, fingerprint, ctx.armor = armor ctx.textmode = False ctx.sign(cleartext_file, signature_file, ourgpg.constants.SIG_MODE_DETACH) + + +def x509_name_from_rfc4514_string(full_string): + if hasattr(x509.Name, 'from_rfc4514_string'): + return x509.Name.from_rfc4514_string(full_string) + # This is too old of a python-cryptography to have this method. + # Instead, we parse just a few basic things + # If it contains any unsupported (or unparseable) parts, + # we just return an error + attrs = [] + if '%' in full_string: + raise ValueError("Unsupported RFC4514 value '%s'" % part) + for part in full_string.split(','): + key, split, value = part.partition("=") + if split != '=': + raise ValueError("Invalid RFC4514 part '%s'" % part) + if key == "CN": + oid = NameOID.COMMON_NAME + elif key == "L": + oid = NameOID.LOCALITY_NAME + elif key == "ST": + oid = NameOID.STATE_OR_PROVINCE_NAME + elif key == "O": + oid = NameOID.ORGANIZATION_NAME + elif key == "OU": + oid = NameOID.ORGANIZATIONAL_UNIT_NAME + elif key == "C": + oid = NameOID.COUNTRY_NAME + else: + raise ValueError("Unsupported RFC4514 part '%s'" % part) + attrs.append(x509.NameAttribute(oid, value)) + return x509.Name(attrs) From ff4f3aa2cad9ce9699c44c9a78a82b09ab40e999 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Oct 13 2023 08:06:11 +0000 Subject: [PATCH 2/3] Disable digital_signature for CA cert Disable the digitalSignature key usage flag on CA certificates. Signed-off-by: Patrick Uiterwijk --- diff --git a/src/server.py b/src/server.py index 9f1fdbb..c2aabe2 100644 --- a/src/server.py +++ b/src/server.py @@ -2242,7 +2242,7 @@ def cmd_sign_certificate(db, conn): key_usage = None if cert_type == 'ca': key_usage = x509.KeyUsage( - digital_signature=True, + digital_signature=False, key_cert_sign=True, crl_sign=True, content_commitment=False, From 23f65929474dce5ff060ec929ed9aa92174f644b Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Jan 16 2024 09:11:00 +0000 Subject: [PATCH 3/3] Always add AuthorityKeyIdentifier on certicates RFC5280 says AKI MAY be omitted on self-signed certificates, but seemingly the Linux kernel contains a bug that makes it consider a certificate without it invalid. As such, we just always add it. Signed-off-by: Patrick Uiterwijk --- diff --git a/src/server.py b/src/server.py index c2aabe2..f27a5d0 100644 --- a/src/server.py +++ b/src/server.py @@ -2308,7 +2308,18 @@ def cmd_sign_certificate(db, conn): x509.SubjectKeyIdentifier.from_public_key(subject_pubkey), critical=False, ) - if issuer_cert is not None: + if issuer_cert is None: + # Technically AKI can be omitted with a self-signed cert per RFC5280 + # (issuer_cert is None), but seemingly the Linux kernel is buggy in + # that it requires it. So in order to be compatible, just add it + # always. + builder = builder.add_extension( + x509.AuthorityKeyIdentifier.from_issuer_public_key( + subject_pubkey, + ), + critical=False, + ) + else: issuer_ski = issuer_cert.extensions.get_extension_for_class( x509.SubjectKeyIdentifier )