From 5a2bb54bbb7b68a7407ab5d62c828c329166bd81 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 19 Oct 2015 23:45:59 -0500 Subject: encode countryName with PrintableString This commit adds a dependency on asn1crypto for testing purposes to parse the certificate and confirm that countryName is encoded with PrintableString while other fields are UTF8String. This is a test only dep. --- dev-requirements.txt | 1 + setup.py | 1 + .../hazmat/backends/openssl/backend.py | 14 ++++---- tests/test_x509.py | 39 ++++++++++++++++++++++ tox.ini | 1 + 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index d82c13b6..2c0ca18c 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,4 @@ +asn1crypto coverage flake8 flake8-import-order diff --git a/setup.py b/setup.py index 9c97e1dd..d54afc02 100644 --- a/setup.py +++ b/setup.py @@ -63,6 +63,7 @@ test_requirements = [ "pretend", "iso8601", "hypothesis", + "asn1crypto", ] # If there's no vectors locally that probably means we are in a tarball and diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f86c3aa1..db7022e5 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -52,7 +52,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( from cryptography.hazmat.primitives.ciphers.modes import ( CBC, CFB, CFB8, CTR, ECB, GCM, OFB ) -from cryptography.x509.oid import ExtensionOID +from cryptography.x509.oid import ExtensionOID, NameOID _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) @@ -119,12 +119,14 @@ def _encode_name(backend, attributes): for attribute in attributes: value = attribute.value.encode('utf8') obj = _txt2obj_gc(backend, attribute.oid.dotted_string) + if attribute.oid == NameOID.COUNTRY_NAME: + # Per RFC5280 countryName should be encoded as PrintableString, + # not UTF8String + type = backend._lib.MBSTRING_ASC + else: + type = backend._lib.MBSTRING_UTF8 res = backend._lib.X509_NAME_add_entry_by_OBJ( - subject, - obj, - backend._lib.MBSTRING_UTF8, - value, - -1, -1, 0, + subject, obj, type, value, -1, -1, 0, ) backend.openssl_assert(res == 1) return subject diff --git a/tests/test_x509.py b/tests/test_x509.py index 8035886c..1fa4d82a 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -9,6 +9,8 @@ import datetime import ipaddress import os +from asn1crypto import core, x509 as asn1cryptox509 + import pytest import six @@ -834,6 +836,43 @@ class TestRSACertificateRequest(object): x509.DNSName(u"cryptography.io"), ] + def test_build_cert_printable_string_country_name(self, backend): + issuer_private_key = RSA_KEY_2048.private_key(backend) + subject_private_key = RSA_KEY_2048.private_key(backend) + + not_valid_before = datetime.datetime(2002, 1, 1, 12, 1) + not_valid_after = datetime.datetime(2030, 12, 31, 8, 30) + + builder = x509.CertificateBuilder().serial_number( + 777 + ).issuer_name(x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'), + ])).subject_name(x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'), + ])).public_key( + subject_private_key.public_key() + ).not_valid_before( + not_valid_before + ).not_valid_after( + not_valid_after + ) + + cert = builder.sign(issuer_private_key, hashes.SHA256(), backend) + + parsedasn1 = asn1cryptox509.Certificate.load( + cert.public_bytes(serialization.Encoding.DER) + ) + assert isinstance( + parsedasn1.subject.chosen[0][0]['value'].chosen, + core.PrintableString + ) + assert isinstance( + parsedasn1.subject.chosen[1][0]['value'].chosen, + core.UTF8String + ) + class TestCertificateBuilder(object): @pytest.mark.requires_backend_interface(interface=RSABackend) diff --git a/tox.ini b/tox.ini index 1ed03a5e..820eccf3 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ deps = pretend pytest hypothesis>=1.11.4 + asn1crypto ./vectors passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME commands = -- cgit v1.2.3 From 8b5d094ca3bb5cafa61001b83c1798e40af37223 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 09:35:17 +0900 Subject: switch to using pyasn1_modules for the test --- dev-requirements.txt | 2 +- setup.py | 2 +- tests/test_x509.py | 26 ++++++++++++++------------ tox.ini | 2 +- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 2c0ca18c..f6eec132 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,3 @@ -asn1crypto coverage flake8 flake8-import-order @@ -6,6 +5,7 @@ invoke iso8601 pep8-naming pretend +pyasn1_modules pytest requests sphinx diff --git a/setup.py b/setup.py index d54afc02..19f1e663 100644 --- a/setup.py +++ b/setup.py @@ -63,7 +63,7 @@ test_requirements = [ "pretend", "iso8601", "hypothesis", - "asn1crypto", + "pyasn1_modules", ] # If there's no vectors locally that probably means we are in a tarball and diff --git a/tests/test_x509.py b/tests/test_x509.py index 1fa4d82a..79424752 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -9,7 +9,9 @@ import datetime import ipaddress import os -from asn1crypto import core, x509 as asn1cryptox509 +from pyasn1.codec.der import decoder + +from pyasn1_modules import rfc2459 import pytest @@ -861,17 +863,17 @@ class TestRSACertificateRequest(object): cert = builder.sign(issuer_private_key, hashes.SHA256(), backend) - parsedasn1 = asn1cryptox509.Certificate.load( - cert.public_bytes(serialization.Encoding.DER) - ) - assert isinstance( - parsedasn1.subject.chosen[0][0]['value'].chosen, - core.PrintableString - ) - assert isinstance( - parsedasn1.subject.chosen[1][0]['value'].chosen, - core.UTF8String - ) + parsed, _ = decoder.decode( + cert.public_bytes(serialization.Encoding.DER), + asn1Spec=rfc2459.Certificate() + ) + tbs_cert = parsed.getComponentByName('tbsCertificate') + subject = tbs_cert.getComponentByName('subject') + issuer = tbs_cert.getComponentByName('issuer') + # \x13 is printable string. The first byte of the value of the + # node corresponds to the ASN.1 string type. + assert str(subject[0][0][0][1])[0] == "\x13" + assert str(issuer[0][0][0][1])[0] == "\x13" class TestCertificateBuilder(object): diff --git a/tox.ini b/tox.ini index 820eccf3..35dc5671 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ deps = pretend pytest hypothesis>=1.11.4 - asn1crypto + pyasn1_modules ./vectors passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME commands = -- cgit v1.2.3 From 39c4445347ec3778a3cf0c68a0116dbbc4c53fa6 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 10:43:37 +0900 Subject: remove unneeded str --- tests/test_x509.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index 79424752..19b525d3 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -872,8 +872,8 @@ class TestRSACertificateRequest(object): issuer = tbs_cert.getComponentByName('issuer') # \x13 is printable string. The first byte of the value of the # node corresponds to the ASN.1 string type. - assert str(subject[0][0][0][1])[0] == "\x13" - assert str(issuer[0][0][0][1])[0] == "\x13" + assert subject[0][0][0][1][0] == "\x13" + assert issuer[0][0][0][1][0] == "\x13" class TestCertificateBuilder(object): -- cgit v1.2.3 From 225a08f9e4b507493744f566b8f0a8bf2a06e82e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 10:51:00 +0900 Subject: work on py3 --- tests/test_x509.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index 19b525d3..43eca472 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -872,8 +872,8 @@ class TestRSACertificateRequest(object): issuer = tbs_cert.getComponentByName('issuer') # \x13 is printable string. The first byte of the value of the # node corresponds to the ASN.1 string type. - assert subject[0][0][0][1][0] == "\x13" - assert issuer[0][0][0][1][0] == "\x13" + assert subject[0][0][0][1][0] == b"\x13"[0] + assert issuer[0][0][0][1][0] == b"\x13"[0] class TestCertificateBuilder(object): -- cgit v1.2.3 From 8fdd1d3a00ea2c1de04a214314e20a39009c7c29 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Tue, 27 Oct 2015 11:35:37 +0900 Subject: update comment to include a bit more detail --- src/cryptography/hazmat/backends/openssl/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index db7022e5..775430d4 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -120,8 +120,8 @@ def _encode_name(backend, attributes): value = attribute.value.encode('utf8') obj = _txt2obj_gc(backend, attribute.oid.dotted_string) if attribute.oid == NameOID.COUNTRY_NAME: - # Per RFC5280 countryName should be encoded as PrintableString, - # not UTF8String + # Per RFC5280 Appendix A.1 countryName should be encoded as + # PrintableString, not UTF8String type = backend._lib.MBSTRING_ASC else: type = backend._lib.MBSTRING_UTF8 -- cgit v1.2.3