From 4f3c2c1353448ae7844cf2ef6395fac5c125f034 2024-02-23 22:45:03 From: Branko Majic Date: 2024-02-23 22:45:03 Subject: [PATCH] GC-45: Use non-naive datetime objects that include timezone: - Newer versions of cryptography prefer/insists on use of UTC-based datetime objects with correctly set timezone. --- diff --git a/gimmecert/commands.py b/gimmecert/commands.py index 0552af03051a193b1af84eec588002f44ff8cfb3..792885be9c54a4e4a5cd775e4d1373c92c8b781d 100644 --- a/gimmecert/commands.py +++ b/gimmecert/commands.py @@ -523,7 +523,7 @@ def status(stdout, stderr, project_directory): :rtype: int """ - now = datetime.datetime.now() + now = datetime.datetime.now(datetime.timezone.utc) if not gimmecert.storage.is_initialised(project_directory): print("CA hierarchy has not been initialised in current directory.", file=stdout) @@ -556,15 +556,15 @@ def status(stdout, stderr, project_directory): else: print(gimmecert.utils.dn_to_str(certificate.subject), file=stdout) - if certificate.not_valid_before > now: + if certificate.not_valid_before_utc > now: validity_status = " [NOT VALID YET]" - elif certificate.not_valid_after < now: + elif certificate.not_valid_after_utc < now: validity_status = " [EXPIRED]" else: validity_status = "" - print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before, - certificate.not_valid_after), + print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before_utc, + certificate.not_valid_after_utc), validity_status), file=stdout) print(" Certificate: .gimmecert/ca/level%d.cert.pem" % i, file=stdout) @@ -590,16 +590,16 @@ def status(stdout, stderr, project_directory): # Separator. print("", file=stdout) - if certificate.not_valid_before > now: + if certificate.not_valid_before_utc > now: validity_status = " [NOT VALID YET]" - elif certificate.not_valid_after < now: + elif certificate.not_valid_after_utc < now: validity_status = " [EXPIRED]" else: validity_status = "" print(gimmecert.utils.dn_to_str(certificate.subject), file=stdout) - print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before, - certificate.not_valid_after), + print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before_utc, + certificate.not_valid_after_utc), validity_status), file=stdout) print(" DNS: %s" % ", ".join(gimmecert.utils.get_dns_names(certificate)), file=stdout) @@ -632,16 +632,16 @@ def status(stdout, stderr, project_directory): # Separator. print("", file=stdout) - if certificate.not_valid_before > now: + if certificate.not_valid_before_utc > now: validity_status = " [NOT VALID YET]" - elif certificate.not_valid_after < now: + elif certificate.not_valid_after_utc < now: validity_status = " [EXPIRED]" else: validity_status = "" print(gimmecert.utils.dn_to_str(certificate.subject), file=stdout) - print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before, - certificate.not_valid_after), + print(" Validity: %s%s" % (gimmecert.utils.date_range_to_str(certificate.not_valid_before_utc, + certificate.not_valid_after_utc), validity_status), file=stdout) print(" Key algorithm: %s" % key_algorithm, file=stdout) diff --git a/gimmecert/crypto.py b/gimmecert/crypto.py index a8a12b2a0068885dce889a8f0482ab2c3e2e60e0..c03a21cecd75e890f2c731e6760ac5ce08fe8206 100644 --- a/gimmecert/crypto.py +++ b/gimmecert/crypto.py @@ -127,7 +127,7 @@ def get_validity_range(): :rtype: (datetime.datetime, datetime.datetime) """ - now = datetime.datetime.utcnow().replace(microsecond=0) + now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0) not_before = now - datetime.timedelta(minutes=15) not_after = now + relativedelta(years=1) @@ -293,11 +293,11 @@ def issue_server_certificate(name, public_key, issuer_private_key, issuer_certif (cryptography.x509.SubjectAlternativeName([cryptography.x509.DNSName(dns_name) for dns_name in dns_names]), False) ] - if not_before < issuer_certificate.not_valid_before: - not_before = issuer_certificate.not_valid_before + if not_before < issuer_certificate.not_valid_before_utc: + not_before = issuer_certificate.not_valid_before_utc - if not_after > issuer_certificate.not_valid_after: - not_after = issuer_certificate.not_valid_after + if not_after > issuer_certificate.not_valid_after_utc: + not_after = issuer_certificate.not_valid_after_utc certificate = issue_certificate(issuer_certificate.subject, dn, issuer_private_key, public_key, not_before, not_after, extensions) @@ -353,11 +353,11 @@ def issue_client_certificate(name, public_key, issuer_private_key, issuer_certif (cryptography.x509.ExtendedKeyUsage([cryptography.x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]), True), ] - if not_before < issuer_certificate.not_valid_before: - not_before = issuer_certificate.not_valid_before + if not_before < issuer_certificate.not_valid_before_utc: + not_before = issuer_certificate.not_valid_before_utc - if not_after > issuer_certificate.not_valid_after: - not_after = issuer_certificate.not_valid_after + if not_after > issuer_certificate.not_valid_after_utc: + not_after = issuer_certificate.not_valid_after_utc certificate = issue_certificate(issuer_certificate.subject, dn, issuer_private_key, public_key, not_before, not_after, extensions) @@ -389,11 +389,11 @@ def renew_certificate(old_certificate, public_key, issuer_private_key, issuer_ce not_before, not_after = get_validity_range() - if not_before < issuer_certificate.not_valid_before: - not_before = issuer_certificate.not_valid_before + if not_before < issuer_certificate.not_valid_before_utc: + not_before = issuer_certificate.not_valid_before_utc - if not_after > issuer_certificate.not_valid_after: - not_after = issuer_certificate.not_valid_after + if not_after > issuer_certificate.not_valid_after_utc: + not_after = issuer_certificate.not_valid_after_utc new_certificate = issue_certificate(issuer_certificate.subject, old_certificate.subject, diff --git a/tests/test_crypto.py b/tests/test_crypto.py index f0830666cbeaa6d2c4ef0037bfbe88bac12690d6..fa2cb97d3fa1304d4b9e2ec70ce884464315fa86 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -48,11 +48,21 @@ def test_get_validity_range_returns_datetime_tuple(): assert isinstance(not_after, datetime.datetime) +def test_get_validity_range_sets_utc_timezone(): + not_before, not_after = gimmecert.crypto.get_validity_range() + + assert isinstance(not_before.tzinfo, datetime.timezone) + assert not_before.tzinfo == datetime.timezone.utc + + assert isinstance(not_after.tzinfo, datetime.timezone) + assert not_after.tzinfo == datetime.timezone.utc + + @travel(datetime.datetime(2018, 1, 1, 0, 15, 0), tick=False) def test_get_validity_range_not_before_is_within_15_minutes_of_now(): not_before, _ = gimmecert.crypto.get_validity_range() - assert not_before == datetime.datetime(2018, 1, 1, 0, 0) + assert not_before == datetime.datetime(2018, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) @travel(datetime.datetime(2018, 1, 1, 0, 15, 0), tick=False) @@ -95,8 +105,8 @@ def test_issue_certificate_has_correct_content(): assert certificate.issuer == issuer_dn assert certificate.subject == subject_dn - assert certificate.not_valid_before == not_before - assert certificate.not_valid_after == not_after + assert certificate.not_valid_before_utc == not_before + assert certificate.not_valid_after_utc == not_after def test_generate_ca_hierarchy_returns_list_with_3_elements_for_depth_3(): @@ -213,8 +223,8 @@ def test_generate_ca_hierarchy_certificates_have_same_validity(): _, level2_certificate = hierarchy[1] _, level3_certificate = hierarchy[2] - assert level1_certificate.not_valid_before == level2_certificate.not_valid_before == level3_certificate.not_valid_before - assert level1_certificate.not_valid_after == level2_certificate.not_valid_after == level3_certificate.not_valid_after + assert level1_certificate.not_valid_before_utc == level2_certificate.not_valid_before_utc == level3_certificate.not_valid_before_utc + assert level1_certificate.not_valid_after_utc == level2_certificate.not_valid_after_utc == level3_certificate.not_valid_after_utc def test_issue_certificate_sets_extensions(): @@ -361,7 +371,7 @@ def test_issue_server_certificate_not_before_is_15_minutes_in_past(): certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate.not_valid_before == datetime.datetime(2018, 1, 1, 0, 0) + assert certificate.not_valid_before_utc == datetime.datetime(2018, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) def test_issue_server_certificate_not_before_does_not_exceed_ca_validity(): @@ -372,10 +382,10 @@ def test_issue_server_certificate_not_before_does_not_exceed_ca_validity(): private_key = gimmecert.crypto.KeyGenerator('rsa', 2048)() - with travel(issuer_certificate.not_valid_before - datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_before_utc - datetime.timedelta(seconds=1), tick=False): certificate1 = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate1.not_valid_before == issuer_certificate.not_valid_before + assert certificate1.not_valid_before_utc == issuer_certificate.not_valid_before_utc def test_issue_server_certificate_not_after_does_not_exceed_ca_validity(): @@ -386,10 +396,10 @@ def test_issue_server_certificate_not_after_does_not_exceed_ca_validity(): private_key = gimmecert.crypto.KeyGenerator('rsa', 2048)() - with travel(issuer_certificate.not_valid_after + datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_after_utc + datetime.timedelta(seconds=1), tick=False): certificate1 = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate1.not_valid_after == issuer_certificate.not_valid_after + assert certificate1.not_valid_after_utc == issuer_certificate.not_valid_after_utc def test_issue_server_certificate_incorporates_additional_dns_subject_alternative_names(): @@ -497,7 +507,7 @@ def test_issue_client_certificate_not_before_is_15_minutes_in_past(): certificate = gimmecert.crypto.issue_client_certificate('myclient', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate.not_valid_before == datetime.datetime(2018, 1, 1, 0, 0) + assert certificate.not_valid_before_utc == datetime.datetime(2018, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) def test_issue_client_certificate_not_before_does_not_exceed_ca_validity(): @@ -508,10 +518,10 @@ def test_issue_client_certificate_not_before_does_not_exceed_ca_validity(): private_key = gimmecert.crypto.KeyGenerator('rsa', 2048)() - with travel(issuer_certificate.not_valid_before - datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_before_utc - datetime.timedelta(seconds=1), tick=False): certificate1 = gimmecert.crypto.issue_client_certificate('myclient', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate1.not_valid_before == issuer_certificate.not_valid_before + assert certificate1.not_valid_before_utc == issuer_certificate.not_valid_before_utc def test_issue_client_certificate_not_after_does_not_exceed_ca_validity(): @@ -522,10 +532,10 @@ def test_issue_client_certificate_not_after_does_not_exceed_ca_validity(): private_key = gimmecert.crypto.KeyGenerator('rsa', 2048)() - with travel(issuer_certificate.not_valid_after + datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_after_utc + datetime.timedelta(seconds=1), tick=False): certificate1 = gimmecert.crypto.issue_client_certificate('myclient', private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate1.not_valid_after == issuer_certificate.not_valid_after + assert certificate1.not_valid_after_utc == issuer_certificate.not_valid_after_utc def test_renew_certificate_returns_certificate(): @@ -575,7 +585,7 @@ def test_renew_certificate_not_before_is_15_minutes_in_past(): with travel(datetime.datetime(2018, 6, 1, 0, 15, 0), tick=False): certificate = gimmecert.crypto.renew_certificate(old_certificate, private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate.not_valid_before == datetime.datetime(2018, 6, 1, 0, 0) + assert certificate.not_valid_before_utc == datetime.datetime(2018, 6, 1, 0, 0, tzinfo=datetime.timezone.utc) def test_renew_certificate_not_before_does_not_exceed_ca_validity(): @@ -589,10 +599,10 @@ def test_renew_certificate_not_before_does_not_exceed_ca_validity(): old_certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate) # Renew certificate. - with travel(issuer_certificate.not_valid_before - datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_before_utc - datetime.timedelta(seconds=1), tick=False): certificate = gimmecert.crypto.renew_certificate(old_certificate, private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate.not_valid_before == issuer_certificate.not_valid_before + assert certificate.not_valid_before_utc == issuer_certificate.not_valid_before_utc def test_renew_certificate_not_after_does_not_exceed_ca_validity(): @@ -606,10 +616,10 @@ def test_renew_certificate_not_after_does_not_exceed_ca_validity(): old_certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate) # Renew certificate. - with travel(issuer_certificate.not_valid_after + datetime.timedelta(seconds=1), tick=False): + with travel(issuer_certificate.not_valid_after_utc + datetime.timedelta(seconds=1), tick=False): certificate = gimmecert.crypto.renew_certificate(old_certificate, private_key.public_key(), issuer_private_key, issuer_certificate) - assert certificate.not_valid_after == issuer_certificate.not_valid_after + assert certificate.not_valid_after_utc == issuer_certificate.not_valid_after_utc def test_generate_csr_returns_csr_with_passed_in_dn():