add RSA verification support
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index f001719..de1fff7 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -112,3 +112,10 @@
         for b in self._filtered_backends(RSABackend):
             return b.create_rsa_signature_ctx(private_key, padding, algorithm)
         raise UnsupportedAlgorithm
+
+    def create_rsa_verification_ctx(self, public_key, signature, padding,
+                                    algorithm):
+        for b in self._filtered_backends(RSABackend):
+            return b.create_rsa_verification_ctx(public_key, signature,
+                                                 padding, algorithm)
+        raise UnsupportedAlgorithm
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 00fdc26..d080cc8 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -18,7 +18,7 @@
 from cryptography import utils
 from cryptography.exceptions import (
     UnsupportedAlgorithm, InvalidTag, InternalError, AlreadyFinalized,
-    UnsupportedPadding
+    UnsupportedPadding, InvalidSignature
 )
 from cryptography.hazmat.backends.interfaces import (
     CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend
@@ -336,9 +336,21 @@
         ctx.iqmp = self._int_to_bn(private_key.iqmp)
         return ctx
 
+    def _rsa_cdata_from_public_key(self, public_key):
+        ctx = self._lib.RSA_new()
+        ctx = self._ffi.gc(ctx, self._lib.RSA_free)
+        ctx.e = self._int_to_bn(public_key.e)
+        ctx.n = self._int_to_bn(public_key.n)
+        return ctx
+
     def create_rsa_signature_ctx(self, private_key, padding, algorithm):
         return _RSASignatureContext(self, private_key, padding, algorithm)
 
+    def create_rsa_verification_ctx(self, public_key, signature, padding,
+                                    algorithm):
+        return _RSAVerificationContext(self, public_key, signature, padding,
+                                       algorithm)
+
 
 class GetCipherByName(object):
     def __init__(self, fmt):
@@ -686,4 +698,90 @@
         return self._backend._ffi.buffer(sig_buf)[:sig_len[0]]
 
 
+@utils.register_interface(interfaces.AsymmetricVerificationContext)
+class _RSAVerificationContext(object):
+    def __init__(self, backend, public_key, signature, padding, algorithm):
+        self._backend = backend
+        self._public_key = public_key
+        self._signature = signature
+        if not isinstance(padding, interfaces.AsymmetricPadding):
+            raise TypeError(
+                "Expected interface of interfaces.AsymmetricPadding")
+
+        if padding.name == "EMSA-PKCS1-v1_5":
+            if self._backend._lib.Cryptography_HAS_PKEY_CTX:
+                self._verify_method = self._verify_pkey_ctx
+                self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
+            else:
+                self._verify_method = self._verify_pkcs1
+        else:
+            raise UnsupportedPadding
+
+        self._padding = padding
+        self._algorithm = algorithm
+        self._hash_ctx = _HashContext(backend, self._algorithm)
+
+    def update(self, data):
+        if self._hash_ctx is None:
+            raise AlreadyFinalized("Context was already finalized")
+
+        self._hash_ctx.update(data)
+
+    def verify(self):
+        if self._hash_ctx is None:
+            raise AlreadyFinalized("Context was already finalized")
+
+        evp_pkey = self._backend._lib.EVP_PKEY_new()
+        assert evp_pkey != self._backend._ffi.NULL
+        evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free)
+        rsa_cdata = backend._rsa_cdata_from_public_key(self._public_key)
+        res = self._backend._lib.RSA_blinding_on(
+            rsa_cdata, self._backend._ffi.NULL)
+        assert res == 1
+        res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
+        assert res == 1
+        evp_md = self._backend._lib.EVP_get_digestbyname(
+            self._algorithm.name.encode("ascii"))
+        assert evp_md != self._backend._ffi.NULL
+
+        self._verify_method(rsa_cdata, evp_pkey, evp_md)
+
+    def _verify_pkey_ctx(self, rsa_cdata, evp_pkey, evp_md):
+        pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
+            evp_pkey, self._backend._ffi.NULL
+        )
+        assert pkey_ctx != self._backend._ffi.NULL
+        res = self._backend._lib.EVP_PKEY_verify_init(pkey_ctx)
+        assert res == 1
+        res = self._backend._lib.EVP_PKEY_CTX_set_signature_md(
+            pkey_ctx, evp_md)
+        assert res > 0
+
+        res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
+            pkey_ctx, self._padding_enum)
+        assert res > 0
+        data_to_verify = self._hash_ctx.finalize()
+        self._hash_ctx = None
+        res = self._backend._lib.EVP_PKEY_verify(
+            pkey_ctx,
+            self._signature,
+            len(self._signature),
+            data_to_verify,
+            len(data_to_verify)
+        )
+        if res != 1:
+            raise InvalidSignature
+
+    def _verify_pkcs1(self, rsa_cdata, evp_pkey, evp_md):
+        res = self._backend._lib.EVP_VerifyFinal(
+            self._hash_ctx._ctx,
+            self._signature,
+            len(self._signature),
+            evp_pkey
+        )
+        self._hash_ctx = None
+        if res != 1:
+            raise InvalidSignature
+
+
 backend = Backend()
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 2f9e424..d825404 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -49,6 +49,10 @@
         self._public_exponent = public_exponent
         self._modulus = modulus
 
+    def verifier(self, signature, padding, algorithm, backend):
+        return backend.create_rsa_verification_ctx(self, signature, padding,
+                                                   algorithm)
+
     @property
     def key_size(self):
         return _bit_length(self.modulus)
diff --git a/docs/exceptions.rst b/docs/exceptions.rst
index 0982426..7f9ae34 100644
--- a/docs/exceptions.rst
+++ b/docs/exceptions.rst
@@ -10,8 +10,8 @@
 
 .. class:: InvalidSignature
 
-    This is raised when the verify method of a hash context's computed digest
-    does not match the expected digest.
+    This is raised when signature verification fails. This can occur with
+    HMAC or asymmetric key signature validation.
 
 
 .. class:: NotYetFinalized
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 682820b..528b532 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -111,6 +111,42 @@
                         or ``modulus`` do not match the bounds specified in
                         :rfc:`3447`.
 
+    .. method:: verifier(signature, padding, algorithm, backend)
+
+        .. versionadded:: 0.3
+
+        :param bytes signature: The signature to verify.
+
+        :param padding: An instance of a
+            :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
+            provider.
+
+        :param algorithm: An instance of a
+            :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+            provider.
+
+        :param backend: A
+            :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+            provider.
+
+        :returns:
+            :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricVerificationContext`
+
+        .. doctest::
+
+            >>> from cryptography.hazmat.backends import default_backend
+            >>> from cryptography.hazmat.primitives import hashes
+            >>> from cryptography.hazmat.primitives.asymmetric import rsa, padding
+            >>> private_key = rsa.RSAPrivateKey.generate(65537, 2048, default_backend())
+            >>> signer = private_key.signer(padding.PKCS1(), hashes.SHA256(), default_backend())
+            >>> data= b"this is some data I'd like to sign"
+            >>> signer.update(data)
+            >>> signature = signer.finalize()
+            >>> public_key = private_key.public_key()
+            >>> verifier = public_key.verifier(signature, padding.PKCS1(), hashes.SHA256(), default_backend())
+            >>> verifier.update(data)
+            >>> verifier.verify()
+
 .. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
 .. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
 .. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index 5be3dd9..5311322 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -254,8 +254,8 @@
 
     .. method:: verify()
 
-        :raises cryptography.exceptions.InvalidSignature: If signature does not
-            validate.
+        :raises :class:`~cryptography.exceptions.InvalidAsymmetricSignature`: If
+            the signature does not validate.
 
 
 .. class:: AsymmetricPadding
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index be1e76e..5a8f993 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -89,6 +89,10 @@
     def create_rsa_signature_ctx(self, private_key, padding, algorithm):
         pass
 
+    def create_rsa_verification_ctx(self, public_key, signature, padding,
+                                    algorithm):
+        pass
+
 
 class TestMultiBackend(object):
     def test_ciphers(self):
@@ -165,6 +169,9 @@
         backend.create_rsa_signature_ctx("private_key", padding.PKCS1v15(),
                                          hashes.MD5())
 
+        backend.create_rsa_verification_ctx("public_key", "sig",
+                                            padding.PKCS1(), hashes.MD5())
+
         backend = MultiBackend([])
         with pytest.raises(UnsupportedAlgorithm):
             backend.generate_rsa_private_key(key_size=1024, public_exponent=3)
@@ -172,3 +179,7 @@
         with pytest.raises(UnsupportedAlgorithm):
             backend.create_rsa_signature_ctx("private_key", padding.PKCS1v15(),
                                              hashes.MD5())
+
+        with pytest.raises(UnsupportedAlgorithm):
+            backend.create_rsa_verification_ctx("public_key", "sig",
+                                                padding.PKCS1(), hashes.MD5())
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 647c51b..552bfc5 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -443,3 +443,91 @@
         )
         with pytest.raises(TypeError):
             private_key.signer("notpadding", hashes.SHA1(), backend)
+
+
+@pytest.mark.rsa
+class TestRSAVerification(object):
+    @pytest.mark.parametrize(
+        "pkcs1_example",
+        _flatten_pkcs1_examples(load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "RSA", "pkcs1v15sign-vectors.txt"),
+            load_pkcs1_vectors
+        ))
+    )
+    def test_pkcs1v15_verification(self, pkcs1_example, backend):
+        private, public, example = pkcs1_example
+        public_key = rsa.RSAPublicKey(**public)
+        verifier = public_key.verifier(
+            binascii.unhexlify(example["signature"]),
+            padding.PKCS1(),
+            hashes.SHA1(),
+            backend
+        )
+        verifier.update(binascii.unhexlify(example["message"]))
+        verifier.verify()
+
+    def test_invalid_pkcs1v15_signature_wrong_data(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        public_key = private_key.public_key()
+        signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend)
+        signer.update(b"sign me")
+        signature = signer.finalize()
+        verifier = public_key.verifier(
+            signature,
+            padding.PKCS1(),
+            hashes.SHA1(),
+            backend
+        )
+        verifier.update(b"incorrect data")
+        with pytest.raises(exceptions.InvalidAsymmetricSignature):
+            verifier.verify()
+
+    def test_invalid_pkcs1v15_signature_wrong_key(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        public_key = private_key.public_key()
+        public_key._modulus += 2
+        signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend)
+        signer.update(b"sign me")
+        signature = signer.finalize()
+        verifier = public_key.verifier(
+            signature,
+            padding.PKCS1(),
+            hashes.SHA1(),
+            backend
+        )
+        verifier.update(b"sign me")
+        with pytest.raises(exceptions.InvalidAsymmetricSignature):
+            verifier.verify()
+
+    def test_use_after_finalize(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        public_key = private_key.public_key()
+        signer = private_key.signer(padding.PKCS1(), hashes.SHA1(), backend)
+        signer.update(b"sign me")
+        signature = signer.finalize()
+
+        verifier = public_key.verifier(
+            signature,
+            padding.PKCS1(),
+            hashes.SHA1(),
+            backend
+        )
+        verifier.update(b"sign me")
+        verifier.verify()
+        with pytest.raises(exceptions.AlreadyFinalized):
+            verifier.verify()
+        with pytest.raises(exceptions.AlreadyFinalized):
+            verifier.update(b"more data")
+
+    def test_unsupported_padding(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        public_key = private_key.public_key()
+        with pytest.raises(exceptions.UnsupportedAsymmetricPadding):
+            public_key.verifier(b"sig", DummyPadding(), hashes.SHA1(), backend)
+
+    def test_padding_incorrect_type(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(65537, 512, backend)
+        public_key = private_key.public_key()
+        with pytest.raises(TypeError):
+            public_key.verifier(b"sig", "notpadding", hashes.SHA1(), backend)