From 6aedd3b0d821adaf04b89cb7c958622c47d4f61f Mon Sep 17 00:00:00 2001 From: sayali Date: Tue, 25 Aug 2020 18:40:36 -0700 Subject: [PATCH 01/13] Datepicker enhancements --- lemur/certificates/models.py | 2 -- .../certificates/certificate/tracking.tpl.html | 4 +++- .../app/angular/certificates/services.js | 18 ++++++++++-------- .../angular/pending_certificates/services.js | 16 +++++++++------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index 5f6c4ba9..9d4cda34 100644 --- a/lemur/certificates/models.py +++ b/lemur/certificates/models.py @@ -317,8 +317,6 @@ class Certificate(db.Model): if self.name.lower() in [ca.lower() for ca in public_CA]: return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397) - return current_app.config.get("DEFAULT_MAX_VALIDITY_DAYS", 1095) # 3 years default - @property def subject(self): return self.parsed_cert.subject diff --git a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html index 07d6b0f4..6b2edee6 100644 --- a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html @@ -96,7 +96,7 @@ Certificate Authority
- + {{$select.selected.name}} -
-
-
-
+
-
+
Date: Mon, 31 Aug 2020 18:20:32 -0700 Subject: [PATCH 04/13] Logs during cert validity truncate for digicert --- lemur/plugins/lemur_digicert/plugin.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_digicert/plugin.py b/lemur/plugins/lemur_digicert/plugin.py index fd8c4e2d..4bd11bc8 100644 --- a/lemur/plugins/lemur_digicert/plugin.py +++ b/lemur/plugins/lemur_digicert/plugin.py @@ -18,8 +18,9 @@ import json import arrow import pem import requests +import sys from cryptography import x509 -from flask import current_app +from flask import current_app, g from lemur.common.utils import validate_conf from lemur.extensions import metrics from lemur.plugins import lemur_digicert as digicert @@ -129,6 +130,9 @@ def map_fields(options, csr): data["validity_years"] = determine_validity_years(options.get("validity_years")) elif options.get("validity_end"): data["custom_expiration_date"] = determine_end_date(options.get("validity_end")).format("YYYY-MM-DD") + # check if validity got truncated. If resultant validity is not equal to requested validity, it just got truncated + if data["custom_expiration_date"] != options.get("validity_end"): + log_validity_truncation(options, f"{__name__}.{sys._getframe().f_code.co_name}") else: data["validity_years"] = determine_validity_years(0) @@ -154,6 +158,9 @@ def map_cis_fields(options, csr): validity_end = determine_end_date(arrow.utcnow().shift(years=options["validity_years"])) elif options.get("validity_end"): validity_end = determine_end_date(options.get("validity_end")) + # check if validity got truncated. If resultant validity is not equal to requested validity, it just got truncated + if validity_end != options.get("validity_end"): + log_validity_truncation(options, f"{__name__}.{sys._getframe().f_code.co_name}") else: validity_end = determine_end_date(False) @@ -178,6 +185,16 @@ def map_cis_fields(options, csr): return data +def log_validity_truncation(options, function): + log_data = { + "cn": options["common_name"], + "creator": g.user.username + } + metrics.send("digicert_validity_truncated", "counter", 1, metric_tags=log_data) + + log_data["function"] = function + log_data["message"] = "Digicert Plugin truncated the validity of certificate, cn = {0}".format(options["common_name"]) + current_app.logger.info(log_data) def handle_response(response): """ From 8ad4448c85d7e1c15ab4dba404aa92c00bfe8fbf Mon Sep 17 00:00:00 2001 From: sayali Date: Tue, 1 Sep 2020 12:44:49 -0700 Subject: [PATCH 05/13] Match date format for comparison + expected new lines --- lemur/plugins/lemur_digicert/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_digicert/plugin.py b/lemur/plugins/lemur_digicert/plugin.py index 4bd11bc8..ad4272dc 100644 --- a/lemur/plugins/lemur_digicert/plugin.py +++ b/lemur/plugins/lemur_digicert/plugin.py @@ -131,7 +131,7 @@ def map_fields(options, csr): elif options.get("validity_end"): data["custom_expiration_date"] = determine_end_date(options.get("validity_end")).format("YYYY-MM-DD") # check if validity got truncated. If resultant validity is not equal to requested validity, it just got truncated - if data["custom_expiration_date"] != options.get("validity_end"): + if data["custom_expiration_date"] != options.get("validity_end").format("YYYY-MM-DD"): log_validity_truncation(options, f"{__name__}.{sys._getframe().f_code.co_name}") else: data["validity_years"] = determine_validity_years(0) @@ -185,6 +185,7 @@ def map_cis_fields(options, csr): return data + def log_validity_truncation(options, function): log_data = { "cn": options["common_name"], @@ -196,6 +197,7 @@ def log_validity_truncation(options, function): log_data["message"] = "Digicert Plugin truncated the validity of certificate, cn = {0}".format(options["common_name"]) current_app.logger.info(log_data) + def handle_response(response): """ Handle the DigiCert API response and any errors it might have experienced. From de0c38e9ba90a6709a1e5a3e88ea36f2dd1de2f2 Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:47:51 -0700 Subject: [PATCH 06/13] mapping of curve name to key_type --- lemur/common/utils.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lemur/common/utils.py b/lemur/common/utils.py index c33722b2..528a6a57 100644 --- a/lemur/common/utils.py +++ b/lemur/common/utils.py @@ -114,6 +114,41 @@ 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.SECP192R1().name: "ECCSECP192R1", + ec.SECP224R1().name: "ECCSECP224R1", + ec.SECP256R1().name: "ECCSECP256R1", + 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. From 6fa15c4cb3b9f4387ba7d93614e238c706e1bd91 Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:48:21 -0700 Subject: [PATCH 07/13] methods to extract cn and key_type from csr --- lemur/certificates/utils.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) 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() From 5ab9626cbd67c4fee01b41ecc8a5e2a288e63f39 Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:52:59 -0700 Subject: [PATCH 08/13] overwriting cn and key_type values from CSR, as they take precedence --- lemur/certificates/schemas.py | 7 +++++++ 1 file changed, 7 insertions(+) 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) From 60fd2134ca3913a712a801fb5748cdc95d24a6e8 Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:53:35 -0700 Subject: [PATCH 09/13] removing duplicate curves, and marking them in existing mapping --- lemur/common/utils.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lemur/common/utils.py b/lemur/common/utils.py index 528a6a57..01cc64ae 100644 --- a/lemur/common/utils.py +++ b/lemur/common/utils.py @@ -125,9 +125,7 @@ def get_key_type_from_ec_curve(curve_name): _CURVE_TYPES = { ec.SECP192R1().name: "ECCPRIME192V1", ec.SECP256R1().name: "ECCPRIME256V1", - ec.SECP192R1().name: "ECCSECP192R1", ec.SECP224R1().name: "ECCSECP224R1", - ec.SECP256R1().name: "ECCSECP256R1", ec.SECP384R1().name: "ECCSECP384R1", ec.SECP521R1().name: "ECCSECP521R1", ec.SECP256K1().name: "ECCSECP256K1", @@ -163,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(), From aff7ad7ea250fd3e20fdb490946f2e0e31e8a05c Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:53:59 -0700 Subject: [PATCH 10/13] testing --- lemur/tests/test_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) 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 From 4923157dc21759edc929b6eab968b94dbdceebfa Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:54:20 -0700 Subject: [PATCH 11/13] expanding key_type to with EC support --- lemur/certificates/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index 5f6c4ba9..a52ec1a8 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): From a7be8b6dceb1e71c2db93cea3e298ad849fe69d6 Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Wed, 9 Sep 2020 19:54:53 -0700 Subject: [PATCH 12/13] adding support for different types of CSR encodings --- .../app/angular/certificates/certificate/options.tpl.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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.

From 09a2a8fc76801c1a04b0ace0265bf31345f58a1c Mon Sep 17 00:00:00 2001 From: sayali Date: Fri, 11 Sep 2020 15:53:05 -0700 Subject: [PATCH 13/13] Log message change PR comments --- lemur/plugins/lemur_digicert/plugin.py | 2 +- .../app/angular/certificates/certificate/tracking.tpl.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_digicert/plugin.py b/lemur/plugins/lemur_digicert/plugin.py index ad4272dc..3948acbb 100644 --- a/lemur/plugins/lemur_digicert/plugin.py +++ b/lemur/plugins/lemur_digicert/plugin.py @@ -194,7 +194,7 @@ def log_validity_truncation(options, function): metrics.send("digicert_validity_truncated", "counter", 1, metric_tags=log_data) log_data["function"] = function - log_data["message"] = "Digicert Plugin truncated the validity of certificate, cn = {0}".format(options["common_name"]) + log_data["message"] = "Digicert Plugin truncated the validity of certificate" current_app.logger.info(log_data) diff --git a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html index e024972b..d60a1a6a 100644 --- a/lemur/static/app/angular/certificates/certificate/tracking.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/tracking.tpl.html @@ -133,7 +133,7 @@