add authority information access classes
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index b7c4c6c..badb500 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -40,6 +40,7 @@
 naïve
 namespace
 namespaces
+online
 paddings
 pickleable
 plaintext
diff --git a/docs/x509.rst b/docs/x509.rst
index 5f36a92..f66178a 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -719,6 +719,29 @@
         :returns: A list of values extracted from the matched general names.
 
 
+.. class:: AuthorityInformationAccess
+
+    .. versionadded:: 0.9
+
+    The authority information access extension indicates how to access
+    information and services for the issuer of the certificate in which
+    the extension appears. Information and services may include online
+    validation services (such as OCSP) and issuer data. It is an iterable,
+    containing one or more :class:`AccessDescription` instances.
+
+
+.. class:: AccessDescription
+
+    .. attribute:: access_method
+
+        :type: :class:`ObjectIdentifier`
+
+        Either :data:`OID_OCSP` or :data:`OID_CA_ISSUERS`
+
+    .. attribute:: access_location
+
+        :type: :class:`GeneralName`
+
 Object Identifiers
 ~~~~~~~~~~~~~~~~~~
 
@@ -911,6 +934,19 @@
     Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.9"``. This is used to
     denote that a certificate may be used for signing OCSP responses.
 
+Authority Information Access OIDs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. data:: OID_OCSP
+
+    Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.1"``. Used as the
+    identifier for OCSP data in :class:`AccessDescription` objects.
+
+.. data:: OID_CA_ISSUERS
+
+    Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.2"``. Used as the
+    identifier for CA issuer data in :class:`AccessDescription` objects.
+
 .. _extension_oids:
 
 Extension OIDs
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index a37e2d0..2bbd14d 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -67,6 +67,8 @@
     "1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
     "1.3.6.1.5.5.7.1.11": "subjectInfoAccess",
     "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck",
+    "1.3.6.1.5.5.7.48.2": "caIssuers",
+    "1.3.6.1.5.5.7.48.1": "OCSP",
 }
 
 
@@ -394,6 +396,68 @@
                     self, encipher_only, decipher_only)
 
 
+class AuthorityInformationAccess(object):
+    def __init__(self, descriptions):
+        if not all(isinstance(x, AccessDescription) for x in descriptions):
+            raise TypeError(
+                "Every item in the descriptions list must be an "
+                "AccessDescription"
+            )
+
+        self._descriptions = descriptions
+
+    def __iter__(self):
+        return iter(self._descriptions)
+
+    def __len__(self):
+        return len(self._descriptions)
+
+    def __repr__(self):
+        return "<AuthorityInformationAccess({0})>".format(self._descriptions)
+
+    def __eq__(self, other):
+        if not isinstance(other, AuthorityInformationAccess):
+            return NotImplemented
+
+        return self._descriptions == other._descriptions
+
+    def __ne__(self, other):
+        return not self == other
+
+
+class AccessDescription(object):
+    def __init__(self, access_method, access_location):
+        if not (access_method == OID_OCSP or access_method == OID_CA_ISSUERS):
+            raise TypeError("access_method must be OID_OCSP or OID_CA_ISSUERS")
+
+        if not isinstance(access_location, GeneralName):
+            raise TypeError("access_location must be a GeneralName")
+
+        self._access_method = access_method
+        self._access_location = access_location
+
+    def __repr__(self):
+        return (
+            "<AccessDescription(access_method={0.access_method}, access_locati"
+            "on={0.access_location})>".format(self)
+        )
+
+    def __eq__(self, other):
+        if not isinstance(other, AccessDescription):
+            return NotImplemented
+
+        return (
+            self.access_method == other.access_method and
+            self.access_location == other.access_location
+        )
+
+    def __ne__(self, other):
+        return not self == other
+
+    access_method = utils.read_only_property("_access_method")
+    access_location = utils.read_only_property("_access_location")
+
+
 class SubjectKeyIdentifier(object):
     def __init__(self, digest):
         self._digest = digest
@@ -680,6 +744,9 @@
 OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
 OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
 
+OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
+OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
+
 
 @six.add_metaclass(abc.ABCMeta)
 class Certificate(object):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 92e616e..711b6b7 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -988,3 +988,145 @@
             x509.ObjectIdentifier("2.5.29.37.0"),
             x509.ObjectIdentifier("2.16.840.1.113730.4.1"),
         ] == list(ext.value)
+
+
+class TestAccessDescription(object):
+    def test_invalid_access_method(self):
+        with pytest.raises(TypeError):
+            x509.AccessDescription("notanoid", x509.DNSName(u"test"))
+
+    def test_invalid_access_location(self):
+        with pytest.raises(TypeError):
+            x509.AccessDescription(x509.OID_CA_ISSUERS, "invalid")
+
+    def test_repr(self):
+        ad = x509.AccessDescription(
+            x509.OID_OCSP,
+            x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+        )
+        assert repr(ad) == (
+            "<AccessDescription(access_method=<ObjectIdentifier(oid=1.3.6.1.5."
+            "5.7.48.1, name=OCSP)>, access_location=<UniformResourceIdentifier"
+            "(value=http://ocsp.domain.com)>)>"
+        )
+
+    def test_eq(self):
+        ad = x509.AccessDescription(
+            x509.OID_OCSP,
+            x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+        )
+        ad2 = x509.AccessDescription(
+            x509.OID_OCSP,
+            x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+        )
+        assert ad == ad2
+
+    def test_ne(self):
+        ad = x509.AccessDescription(
+            x509.OID_OCSP,
+            x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+        )
+        ad2 = x509.AccessDescription(
+            x509.OID_CA_ISSUERS,
+            x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+        )
+        ad3 = x509.AccessDescription(
+            x509.OID_OCSP,
+            x509.UniformResourceIdentifier(u"http://notthesame")
+        )
+        assert ad != ad2
+        assert ad != ad3
+        assert ad != object()
+
+
+class TestAuthorityInformationAccess(object):
+    def test_invalid_descriptions(self):
+        with pytest.raises(TypeError):
+            x509.AuthorityInformationAccess(["notanAccessDescription"])
+
+    def test_iter_len(self):
+        aia = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+        assert len(aia) == 2
+        assert list(aia) == [
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ]
+
+    def test_repr(self):
+        aia = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+        assert repr(aia) == (
+            "<AuthorityInformationAccess([<AccessDescription(access_method=<Ob"
+            "jectIdentifier(oid=1.3.6.1.5.5.7.48.1, name=OCSP)>, access_locati"
+            "on=<UniformResourceIdentifier(value=http://ocsp.domain.com)>)>, <"
+            "AccessDescription(access_method=<ObjectIdentifier(oid=1.3.6.1.5.5"
+            ".7.48.2, name=caIssuers)>, access_location=<UniformResourceIdenti"
+            "fier(value=http://domain.com/ca.crt)>)>])>"
+        )
+
+    def test_eq(self):
+        aia = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+        aia2 = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+        assert aia == aia2
+
+    def test_ne(self):
+        aia = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+        aia2 = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+        ])
+
+        assert aia != aia2
+        assert aia != object()