diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index 8a4fc6bd..675cecb4 100644 --- a/lemur/certificates/models.py +++ b/lemur/certificates/models.py @@ -9,9 +9,10 @@ from datetime import timedelta import arrow from cryptography import x509 -from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import rsa, ec from flask import current_app from idna.core import InvalidCodepoint +from lemur.common.utils import get_key_type_from_ec_curve from sqlalchemy import ( event, Integer, @@ -302,6 +303,8 @@ class Certificate(db.Model): return "RSA{key_size}".format( key_size=self.parsed_cert.public_key().key_size ) + elif isinstance(self.parsed_cert.public_key(), ec.EllipticCurvePublicKey): + return get_key_type_from_ec_curve(self.parsed_cert.public_key().curve.name) @property def validity_remaining(self): diff --git a/lemur/certificates/schemas.py b/lemur/certificates/schemas.py index 42e444bc..56c91196 100644 --- a/lemur/certificates/schemas.py +++ b/lemur/certificates/schemas.py @@ -148,6 +148,13 @@ class CertificateInputSchema(CertificateCreationSchema): data["extensions"]["subAltNames"]["names"] = [] data["extensions"]["subAltNames"]["names"] = csr_sans + + common_name = cert_utils.get_cn_from_csr(data["csr"]) + if common_name: + data["common_name"] = common_name + key_type = cert_utils.get_key_type_from_csr(data["csr"]) + if key_type: + data["key_type"] = key_type return missing.convert_validity_years(data) diff --git a/lemur/certificates/utils.py b/lemur/certificates/utils.py index 4e6cc4f1..e642e058 100644 --- a/lemur/certificates/utils.py +++ b/lemur/certificates/utils.py @@ -12,6 +12,8 @@ Utils to parse certificate data. from cryptography import x509 from cryptography.hazmat.backends import default_backend from marshmallow.exceptions import ValidationError +from cryptography.hazmat.primitives.asymmetric import rsa, ec +from lemur.common.utils import get_key_type_from_ec_curve def get_sans_from_csr(data): @@ -39,3 +41,45 @@ def get_sans_from_csr(data): pass return sub_alt_names + + +def get_cn_from_csr(data): + """ + Fetches common name (CN) from CSR. + Works with any kind of SubjectAlternativeName + :param data: PEM-encoded string with CSR + :return: the common name + """ + try: + request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend()) + except Exception: + raise ValidationError("CSR presented is not valid.") + + common_name = request.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME) + return common_name[0].value + + +def get_key_type_from_csr(data): + """ + Fetches key_type from CSR. + Works with any kind of SubjectAlternativeName + :param data: PEM-encoded string with CSR + :return: key_type + """ + try: + request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend()) + except Exception: + raise ValidationError("CSR presented is not valid.") + + try: + if isinstance(request.public_key(), rsa.RSAPublicKey): + return "RSA{key_size}".format( + key_size=request.public_key().key_size + ) + elif isinstance(request.public_key(), ec.EllipticCurvePublicKey): + return get_key_type_from_ec_curve(request.public_key().curve.name) + else: + raise Exception("Unsupported key type") + + except NotImplemented: + raise NotImplemented() diff --git a/lemur/common/utils.py b/lemur/common/utils.py index c33722b2..01cc64ae 100644 --- a/lemur/common/utils.py +++ b/lemur/common/utils.py @@ -114,6 +114,39 @@ def get_authority_key(body): return authority_key.hex() +def get_key_type_from_ec_curve(curve_name): + """ + Give an EC curve name, return the matching key_type. + + :param: curve_name + :return: key_type + """ + + _CURVE_TYPES = { + ec.SECP192R1().name: "ECCPRIME192V1", + ec.SECP256R1().name: "ECCPRIME256V1", + ec.SECP224R1().name: "ECCSECP224R1", + ec.SECP384R1().name: "ECCSECP384R1", + ec.SECP521R1().name: "ECCSECP521R1", + ec.SECP256K1().name: "ECCSECP256K1", + ec.SECT163K1().name: "ECCSECT163K1", + ec.SECT233K1().name: "ECCSECT233K1", + ec.SECT283K1().name: "ECCSECT283K1", + ec.SECT409K1().name: "ECCSECT409K1", + ec.SECT571K1().name: "ECCSECT571K1", + ec.SECT163R2().name: "ECCSECT163R2", + ec.SECT233R1().name: "ECCSECT233R1", + ec.SECT283R1().name: "ECCSECT283R1", + ec.SECT409R1().name: "ECCSECT409R1", + ec.SECT571R1().name: "ECCSECT571R2", + } + + if curve_name in _CURVE_TYPES.keys(): + return _CURVE_TYPES[curve_name] + else: + return None + + def generate_private_key(key_type): """ Generates a new private key based on key_type. @@ -128,11 +161,11 @@ def generate_private_key(key_type): """ _CURVE_TYPES = { - "ECCPRIME192V1": ec.SECP192R1(), - "ECCPRIME256V1": ec.SECP256R1(), - "ECCSECP192R1": ec.SECP192R1(), + "ECCPRIME192V1": ec.SECP192R1(), # duplicate + "ECCPRIME256V1": ec.SECP256R1(), # duplicate + "ECCSECP192R1": ec.SECP192R1(), # duplicate "ECCSECP224R1": ec.SECP224R1(), - "ECCSECP256R1": ec.SECP256R1(), + "ECCSECP256R1": ec.SECP256R1(), # duplicate "ECCSECP384R1": ec.SECP384R1(), "ECCSECP521R1": ec.SECP521R1(), "ECCSECP256K1": ec.SECP256K1(), diff --git a/lemur/static/app/angular/certificates/certificate/options.tpl.html b/lemur/static/app/angular/certificates/certificate/options.tpl.html index 7e47cf18..7e6ad428 100644 --- a/lemur/static/app/angular/certificates/certificate/options.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/options.tpl.html @@ -20,7 +20,7 @@ name="certificate signing request" ng-model="certificate.csr" placeholder="PEM encoded string..." class="form-control" - ng-pattern="/^-----BEGIN CERTIFICATE REQUEST-----/"> + ng-pattern="/(^-----BEGIN CERTIFICATE REQUEST-----[\S\s]*-----END CERTIFICATE REQUEST-----)|(^-----BEGIN NEW CERTIFICATE REQUEST-----[\S\s]*-----END NEW CERTIFICATE REQUEST-----)/">

Enter a valid certificate signing request.

diff --git a/lemur/tests/test_utils.py b/lemur/tests/test_utils.py index 2e117d25..1dac39bb 100644 --- a/lemur/tests/test_utils.py +++ b/lemur/tests/test_utils.py @@ -11,6 +11,12 @@ from lemur.tests.vectors import ( ) +def test_get_key_type_from_ec_curve(): + from lemur.common.utils import get_key_type_from_ec_curve + + assert get_key_type_from_ec_curve("secp256r1") == "ECCPRIME256V1" + + def test_generate_private_key(): from lemur.common.utils import generate_private_key