From c3d38b5d80a955aee4b160bb97464a20c4992da7 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Sat, 8 Dec 2018 03:26:07 +0200 Subject: Add RFC 4514 Distinguished Name formatting for Name, RDN and NameAttribute (#4304) --- src/cryptography/x509/extensions.py | 4 +-- src/cryptography/x509/name.py | 70 +++++++++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 4 deletions(-) (limited to 'src/cryptography/x509') diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index 12071b66..bdd445d9 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -541,8 +541,8 @@ class DistributionPoint(object): def __repr__(self): return ( "".format(self) + "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_issuer})>" + .format(self) ) def __eq__(self, other): diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py index 5548eda8..470862c2 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -36,6 +36,41 @@ _NAMEOID_DEFAULT_TYPE = { NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, } +#: Short attribute names from RFC 4514: +#: https://tools.ietf.org/html/rfc4514#page-7 +_NAMEOID_TO_NAME = { + NameOID.COMMON_NAME: 'CN', + NameOID.LOCALITY_NAME: 'L', + NameOID.STATE_OR_PROVINCE_NAME: 'ST', + NameOID.ORGANIZATION_NAME: 'O', + NameOID.ORGANIZATIONAL_UNIT_NAME: 'OU', + NameOID.COUNTRY_NAME: 'C', + NameOID.STREET_ADDRESS: 'STREET', + NameOID.DOMAIN_COMPONENT: 'DC', + NameOID.USER_ID: 'UID', +} + + +def _escape_dn_value(val): + """Escape special characters in RFC4514 Distinguished Name value.""" + + # See https://tools.ietf.org/html/rfc4514#section-2.4 + val = val.replace('\\', '\\\\') + val = val.replace('"', '\\"') + val = val.replace('+', '\\+') + val = val.replace(',', '\\,') + val = val.replace(';', '\\;') + val = val.replace('<', '\\<') + val = val.replace('>', '\\>') + val = val.replace('\0', '\\00') + + if val[0] in ('#', ' '): + val = '\\' + val + if val[-1] == ' ': + val = val[:-1] + '\\ ' + + return val + class NameAttribute(object): def __init__(self, oid, value, _type=_SENTINEL): @@ -80,6 +115,16 @@ class NameAttribute(object): oid = utils.read_only_property("_oid") value = utils.read_only_property("_value") + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + + Use short attribute name if available, otherwise fall back to OID + dotted string. + """ + key = _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) + return '%s=%s' % (key, _escape_dn_value(self.value)) + def __eq__(self, other): if not isinstance(other, NameAttribute): return NotImplemented @@ -117,6 +162,15 @@ class RelativeDistinguishedName(object): def get_attributes_for_oid(self, oid): return [i for i in self if i.oid == oid] + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + + Within each RDN, attributes are joined by '+', although that is rarely + used in certificates. + """ + return '+'.join(attr.rfc4514_string() for attr in self._attributes) + def __eq__(self, other): if not isinstance(other, RelativeDistinguishedName): return NotImplemented @@ -136,7 +190,7 @@ class RelativeDistinguishedName(object): return len(self._attributes) def __repr__(self): - return "".format(list(self)) + return "".format(self.rfc4514_string()) class Name(object): @@ -154,6 +208,18 @@ class Name(object): " or a list RelativeDistinguishedName" ) + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + For example 'CN=foobar.com,O=Foo Corp,C=US' + + An X.509 name is a two-level structure: a list of sets of attributes. + Each list element is separated by ',' and within each list element, set + elements are separated by '+'. The latter is almost never used in + real world certificates. + """ + return ', '.join(attr.rfc4514_string() for attr in self._attributes) + def get_attributes_for_oid(self, oid): return [i for i in self if i.oid == oid] @@ -187,4 +253,4 @@ class Name(object): return sum(len(rdn) for rdn in self._attributes) def __repr__(self): - return "".format(list(self)) + return "".format(self.rfc4514_string()) -- cgit v1.2.3