Merge pull request #2247 from reaperhulk/oid-name

namespace Name OIDs
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index 353cd5b..82e8361 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -8,19 +8,21 @@
     AccessDescription, AuthorityInformationAccess, AuthorityKeyIdentifier,
     BasicConstraints, CRLDistributionPoints, Certificate, CertificateBuilder,
     CertificatePolicies, CertificateRevocationList, CertificateSigningRequest,
-    CertificateSigningRequestBuilder, DNSName, DirectoryName,
-    DistributionPoint, DuplicateExtension, ExtendedKeyUsage,
-    Extension, ExtensionNotFound, ExtensionType, Extensions, GeneralName,
-    GeneralNames, IPAddress, InhibitAnyPolicy, InvalidVersion,
-    IssuerAlternativeName, KeyUsage, NameConstraints,
-    NoticeReference, OCSPNoCheck, ObjectIdentifier, OtherName,
-    PolicyInformation, RFC822Name, ReasonFlags, RegisteredID,
+    CertificateSigningRequestBuilder, DistributionPoint,
+    DuplicateExtension, ExtendedKeyUsage, Extension, ExtensionNotFound,
+    ExtensionType, Extensions, GeneralNames, InhibitAnyPolicy,
+    InvalidVersion, IssuerAlternativeName, KeyUsage, NameConstraints,
+    NoticeReference, OCSPNoCheck, ObjectIdentifier,
+    PolicyInformation, ReasonFlags,
     RevokedCertificate, SubjectAlternativeName, SubjectKeyIdentifier,
-    UniformResourceIdentifier, UnsupportedExtension,
-    UnsupportedGeneralNameType, UserNotice, Version, _GENERAL_NAMES,
-    load_der_x509_certificate,
+    UnsupportedExtension, UserNotice, Version, load_der_x509_certificate,
     load_der_x509_csr, load_pem_x509_certificate, load_pem_x509_csr,
 )
+from cryptography.x509.general_name import (
+    DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name,
+    RegisteredID, UniformResourceIdentifier, UnsupportedGeneralNameType,
+    _GENERAL_NAMES
+)
 from cryptography.x509.name import Name, NameAttribute
 from cryptography.x509.oid import (
     ExtensionOID, NameOID, OID_ANY_POLICY,
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index 78a3edb..8eabee8 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -8,21 +8,17 @@
 import datetime
 import hashlib
 import ipaddress
-from email.utils import parseaddr
 from enum import Enum
 
-import idna
-
 from pyasn1.codec.der import decoder
 from pyasn1.type import namedtype, univ
 
 import six
 
-from six.moves import urllib_parse
-
 from cryptography import utils
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.x509.general_name import GeneralName, IPAddress, OtherName
 from cryptography.x509.name import Name
 from cryptography.x509.oid import (
     ExtensionOID, OID_CA_ISSUERS, OID_OCSP, ObjectIdentifier
@@ -57,18 +53,6 @@
     return hashlib.sha1(data).digest()
 
 
-_GENERAL_NAMES = {
-    0: "otherName",
-    1: "rfc822Name",
-    2: "dNSName",
-    3: "x400Address",
-    4: "directoryName",
-    5: "ediPartyName",
-    6: "uniformResourceIdentifier",
-    7: "iPAddress",
-    8: "registeredID",
-}
-
 _UNIX_EPOCH = datetime.datetime(1970, 1, 1)
 
 
@@ -117,12 +101,6 @@
         self.oid = oid
 
 
-class UnsupportedGeneralNameType(Exception):
-    def __init__(self, msg, type):
-        super(UnsupportedGeneralNameType, self).__init__(msg)
-        self.type = type
-
-
 class Extensions(object):
     def __init__(self, extensions):
         self._extensions = extensions
@@ -801,233 +779,6 @@
     skip_certs = utils.read_only_property("_skip_certs")
 
 
-@six.add_metaclass(abc.ABCMeta)
-class GeneralName(object):
-    @abc.abstractproperty
-    def value(self):
-        """
-        Return the value of the object
-        """
-
-
-@utils.register_interface(GeneralName)
-class RFC822Name(object):
-    def __init__(self, value):
-        if not isinstance(value, six.text_type):
-            raise TypeError("value must be a unicode string")
-
-        name, address = parseaddr(value)
-        parts = address.split(u"@")
-        if name or not address:
-            # parseaddr has found a name (e.g. Name <email>) or the entire
-            # value is an empty string.
-            raise ValueError("Invalid rfc822name value")
-        elif len(parts) == 1:
-            # Single label email name. This is valid for local delivery.
-            # No IDNA encoding needed since there is no domain component.
-            encoded = address.encode("ascii")
-        else:
-            # A normal email of the form user@domain.com. Let's attempt to
-            # encode the domain component and reconstruct the address.
-            encoded = parts[0].encode("ascii") + b"@" + idna.encode(parts[1])
-
-        self._value = value
-        self._encoded = encoded
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<RFC822Name(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, RFC822Name):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class DNSName(object):
-    def __init__(self, value):
-        if not isinstance(value, six.text_type):
-            raise TypeError("value must be a unicode string")
-
-        self._value = value
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<DNSName(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, DNSName):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class UniformResourceIdentifier(object):
-    def __init__(self, value):
-        if not isinstance(value, six.text_type):
-            raise TypeError("value must be a unicode string")
-
-        parsed = urllib_parse.urlparse(value)
-        if not parsed.hostname:
-            netloc = ""
-        elif parsed.port:
-            netloc = (
-                idna.encode(parsed.hostname) +
-                ":{0}".format(parsed.port).encode("ascii")
-            ).decode("ascii")
-        else:
-            netloc = idna.encode(parsed.hostname).decode("ascii")
-
-        # Note that building a URL in this fashion means it should be
-        # semantically indistinguishable from the original but is not
-        # guaranteed to be exactly the same.
-        uri = urllib_parse.urlunparse((
-            parsed.scheme,
-            netloc,
-            parsed.path,
-            parsed.params,
-            parsed.query,
-            parsed.fragment
-        )).encode("ascii")
-
-        self._value = value
-        self._encoded = uri
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<UniformResourceIdentifier(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, UniformResourceIdentifier):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class DirectoryName(object):
-    def __init__(self, value):
-        if not isinstance(value, Name):
-            raise TypeError("value must be a Name")
-
-        self._value = value
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<DirectoryName(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, DirectoryName):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class RegisteredID(object):
-    def __init__(self, value):
-        if not isinstance(value, ObjectIdentifier):
-            raise TypeError("value must be an ObjectIdentifier")
-
-        self._value = value
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<RegisteredID(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, RegisteredID):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class IPAddress(object):
-    def __init__(self, value):
-        if not isinstance(
-            value,
-            (
-                ipaddress.IPv4Address,
-                ipaddress.IPv6Address,
-                ipaddress.IPv4Network,
-                ipaddress.IPv6Network
-            )
-        ):
-            raise TypeError(
-                "value must be an instance of ipaddress.IPv4Address, "
-                "ipaddress.IPv6Address, ipaddress.IPv4Network, or "
-                "ipaddress.IPv6Network"
-            )
-
-        self._value = value
-
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<IPAddress(value={0})>".format(self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, IPAddress):
-            return NotImplemented
-
-        return self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
-@utils.register_interface(GeneralName)
-class OtherName(object):
-    def __init__(self, type_id, value):
-        if not isinstance(type_id, ObjectIdentifier):
-            raise TypeError("type_id must be an ObjectIdentifier")
-        if not isinstance(value, bytes):
-            raise TypeError("value must be a binary string")
-
-        self._type_id = type_id
-        self._value = value
-
-    type_id = utils.read_only_property("_type_id")
-    value = utils.read_only_property("_value")
-
-    def __repr__(self):
-        return "<OtherName(type_id={0}, value={1!r})>".format(
-            self.type_id, self.value)
-
-    def __eq__(self, other):
-        if not isinstance(other, OtherName):
-            return NotImplemented
-
-        return self.type_id == other.type_id and self.value == other.value
-
-    def __ne__(self, other):
-        return not self == other
-
-
 class GeneralNames(object):
     def __init__(self, general_names):
         if not all(isinstance(x, GeneralName) for x in general_names):
diff --git a/src/cryptography/x509/general_name.py b/src/cryptography/x509/general_name.py
new file mode 100644
index 0000000..f5bd30f
--- /dev/null
+++ b/src/cryptography/x509/general_name.py
@@ -0,0 +1,265 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import abc
+import ipaddress
+from email.utils import parseaddr
+
+import idna
+
+import six
+
+from six.moves import urllib_parse
+
+from cryptography import utils
+from cryptography.x509.name import Name
+from cryptography.x509.oid import ObjectIdentifier
+
+
+_GENERAL_NAMES = {
+    0: "otherName",
+    1: "rfc822Name",
+    2: "dNSName",
+    3: "x400Address",
+    4: "directoryName",
+    5: "ediPartyName",
+    6: "uniformResourceIdentifier",
+    7: "iPAddress",
+    8: "registeredID",
+}
+
+
+class UnsupportedGeneralNameType(Exception):
+    def __init__(self, msg, type):
+        super(UnsupportedGeneralNameType, self).__init__(msg)
+        self.type = type
+
+
+@six.add_metaclass(abc.ABCMeta)
+class GeneralName(object):
+    @abc.abstractproperty
+    def value(self):
+        """
+        Return the value of the object
+        """
+
+
+@utils.register_interface(GeneralName)
+class RFC822Name(object):
+    def __init__(self, value):
+        if not isinstance(value, six.text_type):
+            raise TypeError("value must be a unicode string")
+
+        name, address = parseaddr(value)
+        parts = address.split(u"@")
+        if name or not address:
+            # parseaddr has found a name (e.g. Name <email>) or the entire
+            # value is an empty string.
+            raise ValueError("Invalid rfc822name value")
+        elif len(parts) == 1:
+            # Single label email name. This is valid for local delivery.
+            # No IDNA encoding needed since there is no domain component.
+            encoded = address.encode("ascii")
+        else:
+            # A normal email of the form user@domain.com. Let's attempt to
+            # encode the domain component and reconstruct the address.
+            encoded = parts[0].encode("ascii") + b"@" + idna.encode(parts[1])
+
+        self._value = value
+        self._encoded = encoded
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<RFC822Name(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, RFC822Name):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class DNSName(object):
+    def __init__(self, value):
+        if not isinstance(value, six.text_type):
+            raise TypeError("value must be a unicode string")
+
+        self._value = value
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<DNSName(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, DNSName):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class UniformResourceIdentifier(object):
+    def __init__(self, value):
+        if not isinstance(value, six.text_type):
+            raise TypeError("value must be a unicode string")
+
+        parsed = urllib_parse.urlparse(value)
+        if not parsed.hostname:
+            netloc = ""
+        elif parsed.port:
+            netloc = (
+                idna.encode(parsed.hostname) +
+                ":{0}".format(parsed.port).encode("ascii")
+            ).decode("ascii")
+        else:
+            netloc = idna.encode(parsed.hostname).decode("ascii")
+
+        # Note that building a URL in this fashion means it should be
+        # semantically indistinguishable from the original but is not
+        # guaranteed to be exactly the same.
+        uri = urllib_parse.urlunparse((
+            parsed.scheme,
+            netloc,
+            parsed.path,
+            parsed.params,
+            parsed.query,
+            parsed.fragment
+        )).encode("ascii")
+
+        self._value = value
+        self._encoded = uri
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<UniformResourceIdentifier(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, UniformResourceIdentifier):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class DirectoryName(object):
+    def __init__(self, value):
+        if not isinstance(value, Name):
+            raise TypeError("value must be a Name")
+
+        self._value = value
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<DirectoryName(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, DirectoryName):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class RegisteredID(object):
+    def __init__(self, value):
+        if not isinstance(value, ObjectIdentifier):
+            raise TypeError("value must be an ObjectIdentifier")
+
+        self._value = value
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<RegisteredID(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, RegisteredID):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class IPAddress(object):
+    def __init__(self, value):
+        if not isinstance(
+            value,
+            (
+                ipaddress.IPv4Address,
+                ipaddress.IPv6Address,
+                ipaddress.IPv4Network,
+                ipaddress.IPv6Network
+            )
+        ):
+            raise TypeError(
+                "value must be an instance of ipaddress.IPv4Address, "
+                "ipaddress.IPv6Address, ipaddress.IPv4Network, or "
+                "ipaddress.IPv6Network"
+            )
+
+        self._value = value
+
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<IPAddress(value={0})>".format(self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, IPAddress):
+            return NotImplemented
+
+        return self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
+@utils.register_interface(GeneralName)
+class OtherName(object):
+    def __init__(self, type_id, value):
+        if not isinstance(type_id, ObjectIdentifier):
+            raise TypeError("type_id must be an ObjectIdentifier")
+        if not isinstance(value, bytes):
+            raise TypeError("value must be a binary string")
+
+        self._type_id = type_id
+        self._value = value
+
+    type_id = utils.read_only_property("_type_id")
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<OtherName(type_id={0}, value={1!r})>".format(
+            self.type_id, self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, OtherName):
+            return NotImplemented
+
+        return self.type_id == other.type_id and self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other