aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cryptography/x509/name.py22
-rw-r--r--tests/x509/test_x509.py151
2 files changed, 150 insertions, 23 deletions
diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py
index 2fbaee91..0daa8bbd 100644
--- a/src/cryptography/x509/name.py
+++ b/src/cryptography/x509/name.py
@@ -27,6 +27,14 @@ class _ASN1Type(Enum):
_ASN1_TYPE_TO_ENUM = dict((i.value, i) for i in _ASN1Type)
_SENTINEL = object()
+_NAMEOID_DEFAULT_TYPE = {
+ NameOID.COUNTRY_NAME: _ASN1Type.PrintableString,
+ NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString,
+ NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString,
+ NameOID.DN_QUALIFIER: _ASN1Type.PrintableString,
+ NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String,
+ NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String,
+}
class NameAttribute(object):
@@ -50,17 +58,17 @@ class NameAttribute(object):
"Country name must be a 2 character country code"
)
- if _type == _SENTINEL:
- _type = _ASN1Type.PrintableString
-
if len(value) == 0:
raise ValueError("Value cannot be an empty string")
- # Set the default string type for encoding ASN1 strings to UTF8. This
- # is the default for newer OpenSSLs for several years (1.0.1h+) and is
- # recommended in RFC 2459.
+ # The appropriate ASN1 string type varies by OID and is defined across
+ # multiple RFCs including 2459, 3280, and 5280. In general UTF8String
+ # is preferred (2459), but 3280 and 5280 specify several OIDs with
+ # alternate types. This means when we see the sentinel value we need
+ # to look up whether the OID has a non-UTF8 type. If it does, set it
+ # to that. Otherwise, UTF8!
if _type == _SENTINEL:
- _type = _ASN1Type.UTF8String
+ _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String)
if not isinstance(_type, _ASN1Type):
raise TypeError("_type must be from the _ASN1Type enum")
diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py
index 27e284a3..6c33043b 100644
--- a/tests/x509/test_x509.py
+++ b/tests/x509/test_x509.py
@@ -1650,6 +1650,59 @@ class TestCertificateBuilder(object):
builder.sign(private_key, hashes.SHA256(), backend)
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_subject_dn_asn1_types(self, backend):
+ private_key = RSA_KEY_2048.private_key(backend)
+
+ name = x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, u"mysite.com"),
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"value"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"value"),
+ x509.NameAttribute(NameOID.STREET_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"value"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"value"),
+ x509.NameAttribute(NameOID.SERIAL_NUMBER, u"value"),
+ x509.NameAttribute(NameOID.SURNAME, u"value"),
+ x509.NameAttribute(NameOID.GIVEN_NAME, u"value"),
+ x509.NameAttribute(NameOID.TITLE, u"value"),
+ x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u"value"),
+ x509.NameAttribute(NameOID.X500_UNIQUE_IDENTIFIER, u"value"),
+ x509.NameAttribute(NameOID.DN_QUALIFIER, u"value"),
+ x509.NameAttribute(NameOID.PSEUDONYM, u"value"),
+ x509.NameAttribute(NameOID.USER_ID, u"value"),
+ x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u"value"),
+ x509.NameAttribute(NameOID.EMAIL_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.JURISDICTION_COUNTRY_NAME, u"US"),
+ x509.NameAttribute(NameOID.JURISDICTION_LOCALITY_NAME, u"value"),
+ x509.NameAttribute(
+ NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME, u"value"
+ ),
+ x509.NameAttribute(NameOID.BUSINESS_CATEGORY, u"value"),
+ x509.NameAttribute(NameOID.POSTAL_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.POSTAL_CODE, u"value"),
+ ])
+ cert = x509.CertificateBuilder().subject_name(
+ name
+ ).issuer_name(
+ name
+ ).public_key(
+ private_key.public_key()
+ ).serial_number(
+ 777
+ ).not_valid_before(
+ datetime.datetime(1999, 1, 1)
+ ).not_valid_after(
+ datetime.datetime(2020, 1, 1)
+ ).sign(private_key, hashes.SHA256(), backend)
+
+ for dn in (cert.subject, cert.issuer):
+ for oid, asn1_type in TestNameAttribute.EXPECTED_TYPES:
+ assert dn.get_attributes_for_oid(
+ oid
+ )[0]._type == asn1_type
+
@pytest.mark.skipif(sys.platform != "win32", reason="Requires windows")
@pytest.mark.parametrize(
("not_valid_before", "not_valid_after"),
@@ -2748,6 +2801,47 @@ class TestCertificateSigningRequestBuilder(object):
]
@pytest.mark.requires_backend_interface(interface=RSABackend)
+ def test_subject_dn_asn1_types(self, backend):
+ private_key = RSA_KEY_2048.private_key(backend)
+
+ request = x509.CertificateSigningRequestBuilder().subject_name(
+ x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, u"mysite.com"),
+ x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(NameOID.LOCALITY_NAME, u"value"),
+ x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"value"),
+ x509.NameAttribute(NameOID.STREET_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"value"),
+ x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u"value"),
+ x509.NameAttribute(NameOID.SERIAL_NUMBER, u"value"),
+ x509.NameAttribute(NameOID.SURNAME, u"value"),
+ x509.NameAttribute(NameOID.GIVEN_NAME, u"value"),
+ x509.NameAttribute(NameOID.TITLE, u"value"),
+ x509.NameAttribute(NameOID.GENERATION_QUALIFIER, u"value"),
+ x509.NameAttribute(NameOID.X500_UNIQUE_IDENTIFIER, u"value"),
+ x509.NameAttribute(NameOID.DN_QUALIFIER, u"value"),
+ x509.NameAttribute(NameOID.PSEUDONYM, u"value"),
+ x509.NameAttribute(NameOID.USER_ID, u"value"),
+ x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u"value"),
+ x509.NameAttribute(NameOID.EMAIL_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.JURISDICTION_COUNTRY_NAME, u"US"),
+ x509.NameAttribute(
+ NameOID.JURISDICTION_LOCALITY_NAME, u"value"
+ ),
+ x509.NameAttribute(
+ NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME, u"value"
+ ),
+ x509.NameAttribute(NameOID.BUSINESS_CATEGORY, u"value"),
+ x509.NameAttribute(NameOID.POSTAL_ADDRESS, u"value"),
+ x509.NameAttribute(NameOID.POSTAL_CODE, u"value"),
+ ])
+ ).sign(private_key, hashes.SHA256(), backend)
+ for oid, asn1_type in TestNameAttribute.EXPECTED_TYPES:
+ assert request.subject.get_attributes_for_oid(
+ oid
+ )[0]._type == asn1_type
+
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
def test_build_ca_request_with_multivalue_rdns(self, backend):
private_key = RSA_KEY_2048.private_key(backend)
subject = x509.Name([
@@ -3668,6 +3762,47 @@ class TestOtherCertificate(object):
class TestNameAttribute(object):
+ EXPECTED_TYPES = [
+ (NameOID.COMMON_NAME, _ASN1Type.UTF8String),
+ (NameOID.COUNTRY_NAME, _ASN1Type.PrintableString),
+ (NameOID.LOCALITY_NAME, _ASN1Type.UTF8String),
+ (NameOID.STATE_OR_PROVINCE_NAME, _ASN1Type.UTF8String),
+ (NameOID.STREET_ADDRESS, _ASN1Type.UTF8String),
+ (NameOID.ORGANIZATION_NAME, _ASN1Type.UTF8String),
+ (NameOID.ORGANIZATIONAL_UNIT_NAME, _ASN1Type.UTF8String),
+ (NameOID.SERIAL_NUMBER, _ASN1Type.PrintableString),
+ (NameOID.SURNAME, _ASN1Type.UTF8String),
+ (NameOID.GIVEN_NAME, _ASN1Type.UTF8String),
+ (NameOID.TITLE, _ASN1Type.UTF8String),
+ (NameOID.GENERATION_QUALIFIER, _ASN1Type.UTF8String),
+ (NameOID.X500_UNIQUE_IDENTIFIER, _ASN1Type.UTF8String),
+ (NameOID.DN_QUALIFIER, _ASN1Type.PrintableString),
+ (NameOID.PSEUDONYM, _ASN1Type.UTF8String),
+ (NameOID.USER_ID, _ASN1Type.UTF8String),
+ (NameOID.DOMAIN_COMPONENT, _ASN1Type.IA5String),
+ (NameOID.EMAIL_ADDRESS, _ASN1Type.IA5String),
+ (NameOID.JURISDICTION_COUNTRY_NAME, _ASN1Type.PrintableString),
+ (NameOID.JURISDICTION_LOCALITY_NAME, _ASN1Type.UTF8String),
+ (
+ NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME,
+ _ASN1Type.UTF8String
+ ),
+ (NameOID.BUSINESS_CATEGORY, _ASN1Type.UTF8String),
+ (NameOID.POSTAL_ADDRESS, _ASN1Type.UTF8String),
+ (NameOID.POSTAL_CODE, _ASN1Type.UTF8String),
+ ]
+
+ def test_default_types(self):
+ for oid, asn1_type in TestNameAttribute.EXPECTED_TYPES:
+ na = x509.NameAttribute(oid, u"US")
+ assert na._type == asn1_type
+
+ def test_alternate_type(self):
+ na2 = x509.NameAttribute(
+ NameOID.COMMON_NAME, u"common", _ASN1Type.IA5String
+ )
+ assert na2._type == _ASN1Type.IA5String
+
def test_init_bad_oid(self):
with pytest.raises(TypeError):
x509.NameAttribute(None, u'value')
@@ -3697,22 +3832,6 @@ class TestNameAttribute(object):
with pytest.raises(ValueError):
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'')
- def test_country_name_type(self):
- na = x509.NameAttribute(NameOID.COUNTRY_NAME, u"US")
- assert na._type == _ASN1Type.PrintableString
- na2 = x509.NameAttribute(
- NameOID.COUNTRY_NAME, u"US", _ASN1Type.IA5String
- )
- assert na2._type == _ASN1Type.IA5String
-
- def test_types(self):
- na = x509.NameAttribute(NameOID.COMMON_NAME, u"common")
- assert na._type == _ASN1Type.UTF8String
- na2 = x509.NameAttribute(
- NameOID.COMMON_NAME, u"common", _ASN1Type.IA5String
- )
- assert na2._type == _ASN1Type.IA5String
-
def test_invalid_type(self):
with pytest.raises(TypeError):
x509.NameAttribute(NameOID.COMMON_NAME, u"common", "notanenum")