aboutsummaryrefslogtreecommitdiffstats
path: root/src/cryptography/x509
diff options
context:
space:
mode:
authorMarti Raudsepp <marti@juffo.org>2018-12-08 03:26:07 +0200
committerPaul Kehrer <paul.l.kehrer@gmail.com>2018-12-08 09:26:07 +0800
commitc3d38b5d80a955aee4b160bb97464a20c4992da7 (patch)
tree8d6580c1a56be5bccd9f98a414b5bb234b527d20 /src/cryptography/x509
parent7e422821b9f800f5345c37011c510dc9e76f552c (diff)
downloadcryptography-c3d38b5d80a955aee4b160bb97464a20c4992da7.tar.gz
cryptography-c3d38b5d80a955aee4b160bb97464a20c4992da7.tar.bz2
cryptography-c3d38b5d80a955aee4b160bb97464a20c4992da7.zip
Add RFC 4514 Distinguished Name formatting for Name, RDN and NameAttribute (#4304)
Diffstat (limited to 'src/cryptography/x509')
-rw-r--r--src/cryptography/x509/extensions.py4
-rw-r--r--src/cryptography/x509/name.py70
2 files changed, 70 insertions, 4 deletions
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 (
"<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
- "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_is"
- "suer})>".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 "<RelativeDistinguishedName({0!r})>".format(list(self))
+ return "<RelativeDistinguishedName({0})>".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 "<Name({0!r})>".format(list(self))
+ return "<Name({0})>".format(self.rfc4514_string())