diff --git a/nacl/nacl.py b/nacl/nacl.py
index 51cd239e4ac493dccc0228b3043d133224cdbff3..9b48c145af618ac8f56a11bc6aee60f9db74f544 100644
--- a/nacl/nacl.py
+++ b/nacl/nacl.py
@@ -21,6 +21,7 @@ ffi.cdef(
 
         int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, unsigned char *seed);
         int crypto_sign(unsigned char *sm, unsigned long long *smlen, const unsigned char *m, unsigned long long mlen, const unsigned char *sk);
+        int crypto_sign_open(unsigned char *m, unsigned long long *mlen, const unsigned char *sm, unsigned long long smlen, const unsigned char *pk);
     """
 
     # Hashing
@@ -58,6 +59,7 @@ def wrap_nacl_function(func):
 
 lib.crypto_sign_seed_keypair = wrap_nacl_function(lib.crypto_sign_seed_keypair)
 lib.crypto_sign = wrap_nacl_function(lib.crypto_sign)
+lib.crypto_sign_open = wrap_nacl_function(lib.crypto_sign_open)
 
 lib.crypto_hash = wrap_nacl_function(lib.crypto_hash)
 lib.crypto_hash_sha256 = wrap_nacl_function(lib.crypto_hash_sha256)
diff --git a/nacl/signing.py b/nacl/signing.py
index 1c8dd4657a9a4cb87421d2983d4279f2c2d910d3..f97fa426183c04da5d4a65a2fa307e8660ce888a 100644
--- a/nacl/signing.py
+++ b/nacl/signing.py
@@ -7,6 +7,12 @@ from .exceptions import CryptoError
 from .random import random
 
 
+class BadSignatureError(CryptoError):
+    """
+    Raised when the signature was forged or otherwise corrupt.
+    """
+
+
 class SignedMessage(six.binary_type):
 
     @property
@@ -18,6 +24,30 @@ class SignedMessage(six.binary_type):
         return self[nacl.lib.crypto_sign_BYTES:]
 
 
+class VerifyKey(object):
+
+    def __init__(self, key):
+        if len(key) != nacl.lib.crypto_sign_PUBLICKEYBYTES:
+            raise ValueError("The key must be exactly %s bytes long" %
+                                nacl.lib.crypto_sign_PUBLICKEYBYTES)
+
+        self._key = key
+
+    def verify(self, smessage, signature=None):
+        if signature is not None:
+            # If we were given the message and signature separately, combine
+            #   them.
+            smessage = signature + smessage
+
+        message = nacl.ffi.new("unsigned char[]", len(smessage))
+        message_len = nacl.ffi.new("unsigned long long *")
+
+        if not nacl.lib.crypto_sign_open(message, message_len, smessage, len(smessage), self._key):
+            raise BadSignatureError("Signature was forged or corrupt")
+
+        return nacl.ffi.buffer(message, message_len[0])[:]
+
+
 class SigningKey(object):
 
     def __init__(self, seed):
@@ -37,6 +67,9 @@ class SigningKey(object):
         self._seed = seed
         self._signing_key = nacl.ffi.buffer(sk, nacl.lib.crypto_sign_SECRETKEYBYTES)[:]
 
+        # Public values
+        self.verify_key = VerifyKey(nacl.ffi.buffer(pk, nacl.lib.crypto_sign_PUBLICKEYBYTES)[:])
+
     @classmethod
     def generate(cls):
         return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2))
diff --git a/tests/test_signing.py b/tests/test_signing.py
index 0d6295d3b8b8188be19bd6c2225fa8d0773a8759..b8c50ac9e38bbfadc9005a472b32f2fd7d113d67 100644
--- a/tests/test_signing.py
+++ b/tests/test_signing.py
@@ -50,3 +50,33 @@ class TestSigningKey:
         assert binascii.hexlify(signed) == expected
         assert binascii.hexlify(signed.message) == message
         assert binascii.hexlify(signed.signature) == signature
+
+
+class TestVerifyKey:
+
+    @pytest.mark.parametrize(("public_key", "signed", "message", "signature"),
+        [(x["public_key"], x["signed"], x["message"], x["signature"]) for x in ed25519_known_answers()]
+    )
+    def test_valid_signed_message(self, public_key, signed, message, signature):
+        key = nacl.signing.VerifyKey(binascii.unhexlify(public_key))
+        signedb = binascii.unhexlify(signed)
+        messageb = binascii.unhexlify(message)
+        signatureb = binascii.unhexlify(signature)
+
+        assert binascii.hexlify(key.verify(signedb)) == message
+        assert binascii.hexlify(key.verify(messageb, signatureb)) == message
+
+    def test_invalid_signed_message(self):
+        skey = nacl.signing.SigningKey.generate()
+        smessage = skey.sign("A Test Message!")
+        signature, message = smessage.signature, "A Forged Test Message!"
+
+        # Small sanity check
+        assert skey.verify_key.verify(smessage)
+
+        with pytest.raises(nacl.signing.BadSignatureError):
+            skey.verify_key.verify(message, signature)
+
+        with pytest.raises(nacl.signing.BadSignatureError):
+            forged = nacl.signing.SignedMessage(signature + message)
+            skey.verify_key.verify(forged)