Merge pull request #2613 from reaperhulk/warn-on-openssl-lt-101

deprecationwarning for OpenSSL < 1.0.1 as upstream has dropped support
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index a882d26..717c9e7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -49,6 +49,8 @@
 * Added :class:`~cryptography.x509.CertificateRevocationListBuilder` and
   :class:`~cryptography.x509.RevokedCertificateBuilder` to allow creation of
   CRLs.
+* Unrecognized non-critical X.509 extensions are now parsed into an
+  :class:`~cryptography.x509.UnrecognizedExtension` object.
 
 1.1.2 - 2015-12-10
 ~~~~~~~~~~~~~~~~~~
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index f14f403..89028c8 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -118,7 +118,12 @@
         :class:`~cryptography.hazmat.backends.interfaces.PEMSerializationBackend`
         provider.
 
-    :returns: A new instance of a private key.
+    :returns: One of
+        :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`,
+        :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`,
+        or
+        :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey`
+        depending on the contents of ``data``.
 
     :raises ValueError: If the PEM data could not be decrypted or if its
         structure could not be decoded successfully.
@@ -151,7 +156,13 @@
         :class:`~cryptography.hazmat.backends.interfaces.PEMSerializationBackend`
         provider.
 
-    :returns: A new instance of a public key.
+
+    :returns: One of
+        :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
+        :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`,
+        or
+        :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+        depending on the contents of ``data``.
 
     :raises ValueError: If the PEM data's structure could not be decoded
         successfully.
@@ -183,7 +194,12 @@
         :class:`~cryptography.hazmat.backends.interfaces.DERSerializationBackend`
         provider.
 
-    :returns: A new instance of a private key.
+    :returns: One of
+        :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`,
+        :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`,
+        or
+        :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey`
+        depending on the contents of ``data``.
 
     :raises ValueError: If the DER data could not be decrypted or if its
         structure could not be decoded successfully.
@@ -218,7 +234,12 @@
         :class:`~cryptography.hazmat.backends.interfaces.DERSerializationBackend`
         provider.
 
-    :returns: A new instance of a public key.
+    :returns: One of
+        :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
+        :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`,
+        or
+        :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+        depending on the contents of ``data``.
 
     :raises ValueError: If the DER data's structure could not be decoded
         successfully.
@@ -275,7 +296,12 @@
         :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`
         depending on the key's type.
 
-    :returns: A new instance of a public key type.
+    :returns: One of
+        :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
+        :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`,
+        or
+        :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+        depending on the contents of ``data``.
 
     :raises ValueError: If the OpenSSH data could not be properly decoded or
         if the key is not in the proper format.
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index df17f95..8bb3f40 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -2435,7 +2435,8 @@
 
 .. class:: UnsupportedExtension
 
-    This is raised when a certificate contains an unsupported extension type.
+    This is raised when a certificate contains an unsupported extension type
+    that is marked ``critical``.
 
     .. attribute:: oid
 
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 293c628..b8614e0 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -213,6 +213,15 @@
                         "Critical extension {0} is not currently supported"
                         .format(oid), oid
                     )
+                else:
+                    # Dump the DER payload into an UnrecognizedExtension object
+                    data = backend._lib.X509_EXTENSION_get_data(ext)
+                    backend.openssl_assert(data != backend._ffi.NULL)
+                    der = backend._ffi.buffer(data.data, data.length)[:]
+                    unrecognized = x509.UnrecognizedExtension(oid, der)
+                    extensions.append(
+                        x509.Extension(oid, critical, unrecognized)
+                    )
             else:
                 # For extensions which are not supported by OpenSSL we pass the
                 # extension object directly to the parsing routine so it can
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index 0c5b552..f7b5d7f 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -91,6 +91,13 @@
         raise ExtensionNotFound("No {0} extension was found".format(oid), oid)
 
     def get_extension_for_class(self, extclass):
+        if extclass is UnrecognizedExtension:
+            raise TypeError(
+                "UnrecognizedExtension can't be used with "
+                "get_extension_for_class because more than one instance of the"
+                " class may be present."
+            )
+
         for ext in self:
             if isinstance(ext.value, extclass):
                 return ext
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 578015e..6145edb 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1093,7 +1093,11 @@
             backend
         )
         extensions = request.extensions
-        assert len(extensions) == 0
+        assert len(extensions) == 1
+        assert extensions[0].oid == x509.ObjectIdentifier("1.2.3.4")
+        assert extensions[0].value == x509.UnrecognizedExtension(
+            x509.ObjectIdentifier("1.2.3.4"), b"value"
+        )
 
     def test_request_basic_constraints(self, backend):
         request = _load_cert(
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 7c5ca5f..03a3730 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1032,17 +1032,33 @@
 
         assert exc.value.oid == x509.ObjectIdentifier("1.2.3.4")
 
+    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
     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", "custom", "unsupported_extension_2.pem"
             ),
             x509.load_pem_x509_certificate,
             backend
         )
         extensions = cert.extensions
-        assert len(extensions) == 0
+        assert len(extensions) == 2
+        assert extensions[0].critical is False
+        assert extensions[0].oid == x509.ObjectIdentifier(
+            "1.3.6.1.4.1.41482.2"
+        )
+        assert extensions[0].value == x509.UnrecognizedExtension(
+            x509.ObjectIdentifier("1.3.6.1.4.1.41482.2"),
+            b"1.3.6.1.4.1.41482.1.2"
+        )
+        assert extensions[1].critical is False
+        assert extensions[1].oid == x509.ObjectIdentifier(
+            "1.3.6.1.4.1.45724.2.1.1"
+        )
+        assert extensions[1].value == x509.UnrecognizedExtension(
+            x509.ObjectIdentifier("1.3.6.1.4.1.45724.2.1.1"),
+            b"\x03\x02\x040"
+        )
 
     def test_no_extensions_get_for_class(self, backend):
         cert = _load_cert(
@@ -1057,6 +1073,11 @@
             exts.get_extension_for_class(x509.IssuerAlternativeName)
         assert exc.value.oid == ExtensionOID.ISSUER_ALTERNATIVE_NAME
 
+    def test_unrecognized_extension_for_class(self):
+        exts = x509.Extensions([])
+        with pytest.raises(TypeError):
+            exts.get_extension_for_class(x509.UnrecognizedExtension)
+
     def test_indexing(self, backend):
         cert = _load_cert(
             os.path.join("x509", "cryptography.io.pem"),