add x509 extensions class and basic tests (no extensions supported)
diff --git a/docs/x509.rst b/docs/x509.rst
index 1321891..44d53a4 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -196,6 +196,15 @@
             >>> isinstance(cert.signature_hash_algorithm, hashes.SHA256)
             True
 
+    .. attribute:: extensions
+
+        :type: :class:`Extensions`
+
+        The extensions encoded in the certificate.
+
+        :raises cryptography.x509.DuplicateExtension: If more than one
+            extension of the same type is found within the certificate.
+
 .. class:: Name
 
     .. versionadded:: 0.8
@@ -276,6 +285,13 @@
 X.509 Extensions
 ~~~~~~~~~~~~~~~~
 
+.. class:: Extensions
+
+    .. versionadded:: 0.9
+
+    An X.509 Extensions instance is an ordered list of extensions.  The object
+    is iterable to get every extension.
+
 .. class:: Extension
 
     .. versionadded:: 0.9
@@ -482,7 +498,7 @@
 
 .. data:: OID_DSA_WITH_SHA256
 
-    Corresponds to the dotted string ``2.16.840.1.101.3.4.3.2"``. This is
+    Corresponds to the dotted string ``"2.16.840.1.101.3.4.3.2"``. This is
     a SHA256 digest signed by a DSA key.
 
 .. _extension_oids:
@@ -509,6 +525,27 @@
 
         Returns the raw version that was parsed from the certificate.
 
+.. class:: DuplicateExtension
+
+    This is raised when more than one X.509 extension of the same type is
+    found within a certificate.
+
+    .. attribute:: oid
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns the OID.
+
+.. class:: UnsupportedExtension
+
+    This is raised when a certificate contains an unsupported extension type.
+
+    .. attribute:: oid
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns the OID.
+
 
 .. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure
 .. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 89db016..3502d12 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -14,6 +14,7 @@
 from __future__ import absolute_import, division, print_function
 
 import datetime
+import warnings
 
 from cryptography import utils, x509
 from cryptography.exceptions import UnsupportedAlgorithm
@@ -151,3 +152,38 @@
             raise UnsupportedAlgorithm(
                 "Signature algorithm OID:{0} not recognized".format(oid)
             )
+
+    @property
+    def extensions(self):
+        extensions = []
+        seen_oids = set()
+        extcount = self._backend._lib.X509_get_ext_count(self._x509)
+        for i in range(0, extcount):
+            ext = self._backend._lib.X509_get_ext(self._x509, i)
+            assert ext != self._backend._ffi.NULL
+            crit = self._backend._lib.X509_EXTENSION_get_critical(ext)
+            critical = crit == 1
+            oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object))
+            if oid in seen_oids:
+                raise x509.DuplicateExtension(
+                    "Duplicate {0} extension found".format(oid), oid
+                )
+            elif oid == x509.OID_BASIC_CONSTRAINTS and critical:
+                # TODO: remove this obviously.
+                warnings.warn(
+                    "Extension support is not fully implemented. A basic "
+                    "constraints extension with the critical flag was seen and"
+                    " IGNORED."
+                )
+            elif critical:
+                raise x509.UnsupportedExtension(
+                    "{0} is not currently supported".format(oid), oid
+                )
+            else:
+                # Unsupported non-critical extension, silently skipping for now
+                seen_oids.add(oid)
+                continue
+
+            seen_oids.add(oid)
+
+        return x509.Extensions(extensions)
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 1ad7028..29602b3 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -43,6 +43,7 @@
     "2.16.840.1.101.3.4.3.1": "dsa-with-sha224",
     "2.16.840.1.101.3.4.3.2": "dsa-with-sha256",
     "2.5.29.19": "basicConstraints",
+    "2.5.29.15": "keyUsage",
 }
 
 
@@ -65,6 +66,18 @@
         self.parsed_version = parsed_version
 
 
+class DuplicateExtension(Exception):
+    def __init__(self, msg, oid):
+        super(DuplicateExtension, self).__init__(msg)
+        self.oid = oid
+
+
+class UnsupportedExtension(Exception):
+    def __init__(self, msg, oid):
+        super(UnsupportedExtension, self).__init__(msg)
+        self.oid = oid
+
+
 class NameAttribute(object):
     def __init__(self, oid, value):
         if not isinstance(oid, ObjectIdentifier):
@@ -113,6 +126,9 @@
             _OID_NAMES.get(self._dotted_string, "Unknown OID")
         )
 
+    def __hash__(self):
+        return hash(self.dotted_string)
+
     dotted_string = utils.read_only_property("_dotted_string")
 
 
@@ -139,9 +155,21 @@
         return len(self._attributes)
 
 
+OID_KEY_USAGE = ObjectIdentifier("2.5.29.15")
 OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
 
 
+class Extensions(object):
+    def __init__(self, extensions):
+        self._extensions = extensions
+
+    def __iter__(self):
+        return iter(self._extensions)
+
+    def __len__(self):
+        return len(self._extensions)
+
+
 class Extension(object):
     def __init__(self, oid, critical, value):
         if not isinstance(oid, ObjectIdentifier):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 74d14c5..d828152 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -4,9 +4,14 @@
 
 from __future__ import absolute_import, division, print_function
 
+import os
+
 import pytest
 
 from cryptography import x509
+from cryptography.hazmat.backends.interfaces import RSABackend, X509Backend
+
+from .test_x509 import _load_cert
 
 
 class TestExtension(object):
@@ -55,3 +60,55 @@
         assert repr(na) == (
             "<BasicConstraints(ca=True, path_length=None)>"
         )
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestExtensions(object):
+    def test_no_extensions(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "verisign_md2_root.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        ext = cert.extensions
+        assert len(ext) == 0
+        assert list(ext) == []
+
+    def test_duplicate_extension(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "two_basic_constraints.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        with pytest.raises(x509.DuplicateExtension) as exc:
+            cert.extensions
+
+        assert exc.value.oid == x509.OID_BASIC_CONSTRAINTS
+
+    def test_unsupported_critical_extension(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "unsupported_extension_critical.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        with pytest.raises(x509.UnsupportedExtension) as exc:
+            cert.extensions
+
+        assert exc.value.oid == x509.ObjectIdentifier("1.2.3.4")
+
+    def test_unsupported_extension(self, backend):
+        # TODO: this will raise an exception when all extensions are complete
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "unsupported_extension.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        extensions = cert.extensions
+        assert len(extensions) == 0