diff options
-rw-r--r-- | docs/glossary.rst | 4 | ||||
-rw-r--r-- | docs/x509.rst | 120 | ||||
-rw-r--r-- | src/cryptography/x509.py | 86 | ||||
-rw-r--r-- | tests/test_x509.py | 42 |
4 files changed, 252 insertions, 0 deletions
diff --git a/docs/glossary.rst b/docs/glossary.rst index ef422a6e..dc6f3ebf 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -60,3 +60,7 @@ Glossary This is a property of encryption systems whereby two encrypted messages aren't distinguishable without knowing the encryption key. This is considered a basic, necessary property for a working encryption system. + + text + This type corresponds to ``unicode`` on Python 2 and ``str`` on Python + 3. This is equivalent to ``six.text_type``. diff --git a/docs/x509.rst b/docs/x509.rst index 26b91873..26dd2a07 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -181,6 +181,126 @@ X.509 Certificate Object For version 3 X.509 certificates. +.. class:: NameAttribute + + .. versionadded:: 0.8 + + An X.509 name consists of a list of NameAttribute instances. + + .. attribute:: oid + + :type: :class:`ObjectIdentifier` + + The attribute OID. + + .. attribute:: value + + :type: :term:`text` + + The value of the attribute. + +.. class:: ObjectIdentifier + + .. versionadded:: 0.8 + + Object identifiers (frequently seen abbreviated as OID) identify the type + of a value (see: :class:`NameAttribute`). + + .. attribute:: dotted_string + + :type: :class:`str` + + The dotted string value of the OID (e.g. ``"2.5.4.3"``) + +Object Identifiers +~~~~~~~~~~~~~~~~~~ + +X.509 elements are frequently identified by :class:`ObjectIdentifier` +instances. The following common OIDs are available as constants. + +.. data:: OID_COMMON_NAME + + Corresponds to the dotted string ``"2.5.4.3"``. Historically the domain + name would be encoded here for server certificates. :rfc:`2818` deprecates + this practice and names of that type should now be located in a + SubjectAlternativeName extension. This OID is typically seen in X.509 names. + +.. data:: OID_COUNTRY_NAME + + Corresponds to the dotted string ``"2.5.4.6"``. This OID is typically seen + in X.509 names. + +.. data:: OID_LOCALITY_NAME + + Corresponds to the dotted string ``"2.5.4.7"``. This OID is typically seen + in X.509 names. + +.. data:: OID_STATE_OR_PROVINCE_NAME + + Corresponds to the dotted string ``"2.5.4.8"``. This OID is typically seen + in X.509 names. + +.. data:: OID_ORGANIZATION_NAME + + Corresponds to the dotted string ``"2.5.4.10"``. This OID is typically seen + in X.509 names. + +.. data:: OID_ORGANIZATIONAL_UNIT_NAME + + Corresponds to the dotted string ``"2.5.4.11"``. This OID is typically seen + in X.509 names. + +.. data:: OID_SERIAL_NUMBER + + Corresponds to the dotted string ``"2.5.4.5"``. This is distinct from the + serial number of the certificate itself (which can be obtained with + :func:`Certificate.serial`). This OID is typically seen in X.509 names. + +.. data:: OID_SURNAME + + Corresponds to the dotted string ``"2.5.4.4"``. This OID is typically seen + in X.509 names. + +.. data:: OID_GIVEN_NAME + + Corresponds to the dotted string ``"2.5.4.42"``. This OID is typically seen + in X.509 names. + +.. data:: OID_TITLE + + Corresponds to the dotted string ``"2.5.4.12"``. This OID is typically seen + in X.509 names. + +.. data:: OID_GENERATION_QUALIFIER + + Corresponds to the dotted string ``"2.5.4.44"``. This OID is typically seen + in X.509 names. + +.. data:: OID_DN_QUALIFIER + + Corresponds to the dotted string ``"2.5.4.46"``. This specifies + disambiguating information to add to the relative distinguished name of an + entry. See :rfc:`2256`. This OID is typically seen in X.509 names. + +.. data:: OID_PSEUDONYM + + Corresponds to the dotted string ``"2.5.4.65"``. This OID is typically seen + in X.509 names. + +.. data:: OID_DOMAIN_COMPONENT + + Corresponds to the dotted string ``"0.9.2342.19200300.100.1.25"``. A string + holding one component of a domain name. See :rfc:`4519`. This OID is + typically seen in X.509 names. + +.. data:: OID_EMAIL_ADDRESS + + Corresponds to the dotted string ``"1.2.840.113549.1.9.1"``. This OID is + typically seen in X.509 names. + +Exceptions +~~~~~~~~~~ + .. class:: InvalidVersion This is raised when an X.509 certificate has an invalid version number. diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index be1298b6..e280980b 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -9,6 +9,27 @@ from enum import Enum import six +from cryptography import utils + + +_OID_NAMES = { + "2.5.4.3": "commonName", + "2.5.4.6": "countryName", + "2.5.4.7": "localityName", + "2.5.4.8": "stateOrProvinceName", + "2.5.4.10": "organizationName", + "2.5.4.11": "organizationalUnitName", + "2.5.4.5": "serialNumber", + "2.5.4.4": "surname", + "2.5.4.42": "givenName", + "2.5.4.12": "title", + "2.5.4.44": "generationQualifier", + "2.5.4.46": "dnQualifier", + "2.5.4.65": "pseudonym", + "0.9.2342.19200300.100.1.25": "domainComponent", + "1.2.840.113549.1.9.1": "emailAddress", +} + class Version(Enum): v1 = 0 @@ -29,6 +50,71 @@ class InvalidVersion(Exception): self.parsed_version = parsed_version +class NameAttribute(object): + def __init__(self, oid, value): + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + self._oid = oid + self._value = value + + oid = utils.read_only_property("_oid") + value = utils.read_only_property("_value") + + def __eq__(self, other): + if not isinstance(other, NameAttribute): + return NotImplemented + + return ( + self.oid == other.oid and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + + +class ObjectIdentifier(object): + def __init__(self, dotted_string): + self._dotted_string = dotted_string + + def __eq__(self, other): + if not isinstance(other, ObjectIdentifier): + return NotImplemented + + return self._dotted_string == other._dotted_string + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "<ObjectIdentifier(oid={0}, name={1})>".format( + self._dotted_string, + _OID_NAMES.get(self._dotted_string, "Unknown OID") + ) + + dotted_string = utils.read_only_property("_dotted_string") + + +OID_COMMON_NAME = ObjectIdentifier("2.5.4.3") +OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6") +OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7") +OID_STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") +OID_ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") +OID_ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") +OID_SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") +OID_SURNAME = ObjectIdentifier("2.5.4.4") +OID_GIVEN_NAME = ObjectIdentifier("2.5.4.42") +OID_TITLE = ObjectIdentifier("2.5.4.12") +OID_GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") +OID_DN_QUALIFIER = ObjectIdentifier("2.5.4.46") +OID_PSEUDONYM = ObjectIdentifier("2.5.4.65") +OID_DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") +OID_EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") + + @six.add_metaclass(abc.ABCMeta) class Certificate(object): @abc.abstractmethod diff --git a/tests/test_x509.py b/tests/test_x509.py index 5383871a..cf583247 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -248,3 +248,45 @@ class TestECDSACertificate(object): ) with pytest.raises(NotImplementedError): cert.public_key() + + +class TestNameAttribute(object): + def test_eq(self): + assert x509.NameAttribute( + x509.ObjectIdentifier('oid'), 'value' + ) == x509.NameAttribute( + x509.ObjectIdentifier('oid'), 'value' + ) + + def test_ne(self): + assert x509.NameAttribute( + x509.ObjectIdentifier('2.5.4.3'), 'value' + ) != x509.NameAttribute( + x509.ObjectIdentifier('2.5.4.5'), 'value' + ) + assert x509.NameAttribute( + x509.ObjectIdentifier('oid'), 'value' + ) != x509.NameAttribute( + x509.ObjectIdentifier('oid'), 'value2' + ) + assert x509.NameAttribute( + x509.ObjectIdentifier('oid'), 'value' + ) != object() + + +class TestObjectIdentifier(object): + def test_eq(self): + oid1 = x509.ObjectIdentifier('oid') + oid2 = x509.ObjectIdentifier('oid') + assert oid1 == oid2 + + def test_ne(self): + oid1 = x509.ObjectIdentifier('oid') + assert oid1 != x509.ObjectIdentifier('oid1') + assert oid1 != object() + + def test_repr(self): + oid = x509.ObjectIdentifier("2.5.4.3") + assert repr(oid) == "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>" + oid = x509.ObjectIdentifier("oid1") + assert repr(oid) == "<ObjectIdentifier(oid=oid1, name=Unknown OID)>" |