add CRLDistributionPoints and associated classes
diff --git a/docs/x509.rst b/docs/x509.rst
index f4ea2a5..9ef8e14 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -781,6 +781,8 @@
 
 .. class:: AccessDescription
 
+    .. versionadded:: 0.9
+
     .. attribute:: access_method
 
         :type: :class:`ObjectIdentifier`
@@ -798,6 +800,76 @@
 
         Where to access the information defined by the access method.
 
+.. class:: CRLDistributionPoints
+
+    .. versionadded:: 0.9
+
+    The CRL distribution points extension identifies how CRL information is
+    obtained. It is an iterable, containing one or more
+    :class:`DistributionPoint` instances.
+
+.. class:: DistributionPoint
+
+    .. versionadded:: 0.9
+
+    .. attribute:: distribution_point
+
+        :type: list of :class:`GeneralName` instances, :class:`Name`, or None
+
+        This field describes methods to retrieve the CRL.
+
+    .. attribute:: crl_issuer
+
+        :type: list of :class:`GeneralName` instances or None
+
+        Information about the issuer of the CRL.
+
+    .. attribute:: reasons
+
+        :type: :class:`ReasonFlags` or None
+
+        The reasons a given distribution point may be used for when performing
+        revocation checks.
+
+.. class:: ReasonFlags
+
+    .. versionadded:: 0.9
+
+    This class holds reasons a distribution point may be used for when
+    performing revocation checks.
+
+    .. attribute:: key_compromise
+
+        :type: bool
+
+    .. attribute:: ca_compromise
+
+        :type: bool
+
+    .. attribute:: affiliation_changed
+
+        :type: bool
+
+    .. attribute:: superseded
+
+        :type: bool
+
+    .. attribute:: cessation_of_operation
+
+        :type: bool
+
+    .. attribute:: certificate_hold
+
+        :type: bool
+
+    .. attribute:: privilege_withdrawn
+
+        :type: bool
+
+    .. attribute:: aa_compromise
+
+        :type: bool
+
 Object Identifiers
 ~~~~~~~~~~~~~~~~~~
 
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 0d87cd5..671294e 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -481,6 +481,150 @@
         return not self == other
 
 
+class CRLDistributionPoints(object):
+    def __init__(self, distribution_points):
+        if not all(
+            isinstance(x, DistributionPoint) for x in distribution_points
+        ):
+            raise TypeError(
+                "distribution_points must be a list of DistributionPoint "
+                "objects"
+            )
+
+        self._distribution_points = distribution_points
+
+    def __iter__(self):
+        return iter(self._distribution_points)
+
+    def __len__(self):
+        return len(self._distribution_points)
+
+    def __repr__(self):
+        return "<CRLDistributionPoints({0})>".format(self._distribution_points)
+
+    def __eq__(self, other):
+        if not isinstance(other, CRLDistributionPoints):
+            return NotImplemented
+
+        return self._distribution_points == other._distribution_points
+
+    def __ne__(self, other):
+        return not self == other
+
+
+class DistributionPoint(object):
+    def __init__(self, distribution_point, reasons, crl_issuer):
+        if distribution_point:
+            if (
+                (
+                    isinstance(distribution_point, list) and
+                    not all(
+                        isinstance(x, GeneralName) for x in distribution_point
+                    )
+                ) or not isinstance(distribution_point, (list, Name))
+            ):
+                raise TypeError(
+                    "distribution_point must be None, a list of general names"
+                    ", or a Name"
+                )
+
+        if crl_issuer and not all(
+            isinstance(x, GeneralName) for x in crl_issuer
+        ):
+            raise TypeError(
+                "crl_issuer must be None or a list of general names"
+            )
+
+        if reasons and not isinstance(reasons, ReasonFlags):
+            raise TypeError("reasons must be None or ReasonFlags")
+
+        if reasons and not crl_issuer and not distribution_point:
+            raise ValueError(
+                "You must supply crl_issuer or distribution_point when "
+                "reasons is not None"
+            )
+
+        self._distribution_point = distribution_point
+        self._reasons = reasons
+        self._crl_issuer = crl_issuer
+
+    def __repr__(self):
+        return (
+            "<DistributionPoint(distribution_point={0.distribution_point}, rea"
+            "sons={0.reasons}, crl_issuer={0.crl_issuer})>".format(self)
+        )
+
+    def __eq__(self, other):
+        if not isinstance(other, DistributionPoint):
+            return NotImplemented
+
+        return (
+            self.distribution_point == other.distribution_point and
+            self.reasons == other.reasons and
+            self.crl_issuer == other.crl_issuer
+        )
+
+    def __ne__(self, other):
+        return not self == other
+
+    distribution_point = utils.read_only_property("_distribution_point")
+    reasons = utils.read_only_property("_reasons")
+    crl_issuer = utils.read_only_property("_crl_issuer")
+
+
+class ReasonFlags(object):
+    def __init__(self, key_compromise, ca_compromise, affiliation_changed,
+                 superseded, cessation_of_operation, certificate_hold,
+                 privilege_withdrawn, aa_compromise):
+        self._key_compromise = key_compromise
+        self._ca_compromise = ca_compromise
+        self._affiliation_changed = affiliation_changed
+        self._superseded = superseded
+        self._cessation_of_operation = cessation_of_operation
+        self._certificate_hold = certificate_hold
+        self._privilege_withdrawn = privilege_withdrawn
+        self._aa_compromise = aa_compromise
+
+    def __repr__(self):
+        return (
+            "<ReasonFlags(key_compromise={0.key_compromise}, ca_compromise"
+            "={0.ca_compromise}, affiliation_changed={0.affiliation_changed},"
+            "superseded={0.superseded}, cessation_of_operation={0.cessation_o"
+            "f_operation}, certificate_hold={0.certificate_hold}, privilege_w"
+            "ithdrawn={0.privilege_withdrawn}, aa_compromise={0.aa_compromise"
+            "})>".format(self)
+        )
+
+    def __eq__(self, other):
+        if not isinstance(other, ReasonFlags):
+            return NotImplemented
+
+        return (
+            self.key_compromise == other.key_compromise and
+            self.ca_compromise == other.ca_compromise and
+            self.affiliation_changed == other.affiliation_changed and
+            self.superseded == other.superseded and
+            self.cessation_of_operation == other.cessation_of_operation and
+            self.certificate_hold == other.certificate_hold and
+            self.privilege_withdrawn == other.privilege_withdrawn and
+            self.aa_compromise == other.aa_compromise
+        )
+
+    def __ne__(self, other):
+        return not self == other
+
+    key_compromise = utils.read_only_property("_key_compromise")
+    ca_compromise = utils.read_only_property("_ca_compromise")
+    affiliation_changed = utils.read_only_property("_affiliation_changed")
+    superseded = utils.read_only_property("_superseded")
+    cessation_of_operation = utils.read_only_property(
+        "_cessation_of_operation"
+    )
+    certificate_hold = utils.read_only_property("_certificate_hold")
+    privilege_withdrawn = utils.read_only_property("_privilege_withdrawn")
+    aa_compromise = utils.read_only_property("_aa_compromise")
+
+
 @six.add_metaclass(abc.ABCMeta)
 class GeneralName(object):
     @abc.abstractproperty
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 8a22795..1ccb361 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1318,3 +1318,285 @@
             )
         ]
         assert ext.value.authority_cert_serial_number == 3
+
+
+class TestReasonFlags(object):
+    def test_flags(self):
+        flags = x509.ReasonFlags(
+            True, True, False, False, True, True, False, False
+        )
+        assert flags.key_compromise is True
+        assert flags.ca_compromise is True
+        assert flags.affiliation_changed is False
+        assert flags.superseded is False
+        assert flags.cessation_of_operation is True
+        assert flags.certificate_hold is True
+        assert flags.privilege_withdrawn is False
+        assert flags.aa_compromise is False
+
+    def test_eq(self):
+        flags = x509.ReasonFlags(
+            True, True, False, False, True, True, False, False
+        )
+        flags2 = x509.ReasonFlags(
+            True, True, False, False, True, True, False, False
+        )
+        assert flags == flags2
+
+    def test_ne(self):
+        flags = x509.ReasonFlags(
+            True, True, False, False, True, True, False, False
+        )
+        flags2 = x509.ReasonFlags(
+            True, True, False, False, True, True, False, True
+        )
+        assert flags != flags2
+        assert flags != object()
+
+    def test_repr(self):
+        flags = x509.ReasonFlags(
+            True, True, False, False, True, True, False, False
+        )
+        assert repr(flags) == (
+            "<ReasonFlags(key_compromise=True, ca_compromise=True, affiliatio"
+            "n_changed=False,superseded=False, cessation_of_operation=True, c"
+            "ertificate_hold=True, privilege_withdrawn=False, aa_compromise=F"
+            "alse)>"
+        )
+
+
+class TestDistributionPoint(object):
+    def test_distribution_point_list_not_general_names(self):
+        with pytest.raises(TypeError):
+            x509.DistributionPoint(["notgn"], None, None)
+
+    def test_distribution_point_not_name(self):
+        with pytest.raises(TypeError):
+            x509.DistributionPoint("notname", None, None)
+
+    def test_crl_issuer_not_general_names(self):
+        with pytest.raises(TypeError):
+            x509.DistributionPoint(None, None, ["notgn"])
+
+    def test_reason_not_reasonflags(self):
+        with pytest.raises(TypeError):
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+                "notreasonflags",
+                None
+            )
+
+    def test_reason_only(self):
+        with pytest.raises(ValueError):
+            x509.DistributionPoint(
+                None,
+                x509.ReasonFlags(
+                    True, True, False, False, True, True, False, False
+                ),
+                None
+            )
+
+    def test_eq(self):
+        dp = x509.DistributionPoint(
+            [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+            x509.ReasonFlags(
+                False, False, False, False, False, True, False, False
+            ),
+            [
+                x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "Important CA"
+                        )
+                    ])
+                )
+            ],
+        )
+        dp2 = x509.DistributionPoint(
+            [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+            x509.ReasonFlags(
+                False, False, False, False, False, True, False, False
+            ),
+            [
+                x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "Important CA"
+                        )
+                    ])
+                )
+            ],
+        )
+        assert dp == dp2
+
+    def test_ne(self):
+        dp = x509.DistributionPoint(
+            [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+            x509.ReasonFlags(
+                False, False, False, False, False, True, False, False
+            ),
+            [
+                x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "Important CA"
+                        )
+                    ])
+                )
+            ],
+        )
+        dp2 = x509.DistributionPoint(
+            [x509.UniformResourceIdentifier(u"http://crypt.og/crl")],
+            None,
+            None
+        )
+        assert dp != dp2
+        assert dp != object()
+
+    def test_repr(self):
+        dp = x509.DistributionPoint(
+            x509.Name([
+                x509.NameAttribute(x509.OID_COMMON_NAME, "myCN")
+            ]),
+            x509.ReasonFlags(
+                False, False, False, False, False, True, False, False
+            ),
+            [
+                x509.DirectoryName(
+                    x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME, "Important CA"
+                        )
+                    ])
+                )
+            ],
+        )
+        assert repr(dp) == (
+            "<DistributionPoint(distribution_point=<Name([<NameAttribute(oid="
+            "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='myCN')>"
+            "])>, reasons=<ReasonFlags(key_compromise=False, ca_compromise=Fa"
+            "lse, affiliation_changed=False,superseded=False, cessation_of_op"
+            "eration=False, certificate_hold=True, privilege_withdrawn=False,"
+            " aa_compromise=False)>, crl_issuer=[<DirectoryName(value=<Name(["
+            "<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonNam"
+            "e)>, value='Important CA')>])>)>])>"
+        )
+
+
+class TestCRLDistributionPoints(object):
+    def test_invalid_distribution_points(self):
+        with pytest.raises(TypeError):
+            x509.CRLDistributionPoints(["notadistributionpoint"])
+
+    def test_iter_len(self):
+        cdp = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"http://domain")],
+                None,
+                None
+            ),
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                None
+            ),
+        ])
+        assert len(cdp) == 2
+        assert list(cdp) == [
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"http://domain")],
+                None,
+                None
+            ),
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                None
+            ),
+        ]
+
+    def test_repr(self):
+        cdp = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                None
+            ),
+        ])
+        assert repr(cdp) == (
+            "<CRLDistributionPoints([<DistributionPoint(distribution_point=[<"
+            "UniformResourceIdentifier(value=ftp://domain)>], reasons=<Reason"
+            "Flags(key_compromise=True, ca_compromise=True, affiliation_chang"
+            "ed=True,superseded=True, cessation_of_operation=True, certificat"
+            "e_hold=True, privilege_withdrawn=True, aa_compromise=True)>, crl"
+            "_issuer=None)>])>"
+        )
+
+    def test_eq(self):
+        cdp = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing")],
+            ),
+        ])
+        cdp2 = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing")],
+            ),
+        ])
+        assert cdp == cdp2
+
+    def test_ne(self):
+        cdp = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing")],
+            ),
+        ])
+        cdp2 = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain2")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing")],
+            ),
+        ])
+        cdp3 = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, False
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing")],
+            ),
+        ])
+        cdp4 = x509.CRLDistributionPoints([
+            x509.DistributionPoint(
+                [x509.UniformResourceIdentifier(u"ftp://domain")],
+                x509.ReasonFlags(
+                    True, True, True, True, True, True, True, True
+                ),
+                [x509.UniformResourceIdentifier(u"uri://thing2")],
+            ),
+        ])
+        assert cdp != cdp2
+        assert cdp != cdp3
+        assert cdp != cdp4
+        assert cdp != object()