diff --git a/docs/encoding.rst b/docs/encoding.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d698cd3e17aa5e257fb8a5d4caf4065fb4f84acf
--- /dev/null
+++ b/docs/encoding.rst
@@ -0,0 +1,58 @@
+Encoders
+========
+
+PyNaCl supports a simple method of encoding and decoding messages in different
+formats. Encoders are simple classes with staticmethods that encode/decode and
+are typically passed as a keyword argument `encoder` to various methods.
+
+For example you can generate a signing key and encode it in hex with:
+
+.. code:: python
+
+    hex_key = nacl.signing.SigningKey.generate().encode(encoder=nacl.encoding.HexEncoder)
+
+Then you can later decode it from hex:
+
+.. code:: python
+
+    signing_key = nacl.signing.SigningKey(hex_key, encoder=nacl.encoding.HexEncoder)
+
+
+Built in Encoders
+-----------------
+
+.. autoclass:: nacl.encoding.RawEncoder
+    :members:
+
+.. autoclass:: nacl.encoding.HexEncoder
+    :members:
+
+.. autoclass:: nacl.encoding.Base16Encoder
+    :members:
+
+.. autoclass:: nacl.encoding.Base32Encoder
+    :members:
+
+.. autoclass:: nacl.encoding.Base64Encoder
+    :members:
+
+
+Defining your own Encoder
+-------------------------
+
+Defining your own encoder is easy. Each encoder is simply a class with 2 static
+methods. For example here is the hex encoder:
+
+.. code:: python
+
+    import binascii
+
+    class HexEncoder(object):
+
+        @staticmethod
+        def encode(data):
+            return binascii.hexlify(data)
+
+        @staticmethod
+        def decode(data):
+            return binascii.unhexlify(data)
diff --git a/docs/index.rst b/docs/index.rst
index c987ffbf29cde87ca443be70fc9d622fbfa8f050..6be9babe475bb5974449dd8b13fb582799513859 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -9,6 +9,15 @@ Contents:
    signing
 
 
+Support Features
+----------------
+
+.. toctree::
+    :maxdepth: 2
+
+    encoding
+
+
 Api Documentation
 -----------------
 
diff --git a/docs/signing.rst b/docs/signing.rst
index 8a67a8ca5f25e7de0e9c4e58e2824f7d8f22617b..f4e24950c24f5b6d05fe55871295632d8d8ce63a 100644
--- a/docs/signing.rst
+++ b/docs/signing.rst
@@ -19,7 +19,7 @@ Signer's perspective (:class:`~nacl.signing.SigningKey`)
 
 .. code:: python
 
-    import binascii
+    import nacl.encoding
     import nacl.signing
 
     # Generate a new random signing key
@@ -32,17 +32,16 @@ Signer's perspective (:class:`~nacl.signing.SigningKey`)
     verify_key = signing_key.verify_key
 
     # Serialize the verify key to send it to a third party
-    binascii.hexlify(bytes(verify_key))
+    verify_key_hex = verify_key.encode(encoder=nacl.encoding.HexEncoder)
 
 Verifier's perspective (:class:`~nacl.signing.VerifyKey`)
 
 .. code:: python
 
-    import binascii
     import nacl.signing
 
     # Create a VerifyKey object from a hex serialized public key
-    verify_key = nacl.signing.VerifyKey(binascii.unhexlify(verify_key_hex))
+    verify_key = nacl.signing.VerifyKey(verify_key_hex, encoder=nacl.encoding.HexEncoder)
 
     # Check the validity of a message's signature
     # Will raise nacl.signing.BadSignatureError if the signature check fails
diff --git a/nacl/encoding.py b/nacl/encoding.py
new file mode 100644
index 0000000000000000000000000000000000000000..e885c9b7e62cd6b60c106058d4375fdc408e3737
--- /dev/null
+++ b/nacl/encoding.py
@@ -0,0 +1,63 @@
+import base64
+import binascii
+
+
+class RawEncoder(object):
+
+    @staticmethod
+    def encode(data):
+        return data
+
+    @staticmethod
+    def decode(data):
+        return data
+
+
+class HexEncoder(object):
+
+    @staticmethod
+    def encode(data):
+        return binascii.hexlify(data)
+
+    @staticmethod
+    def decode(data):
+        return binascii.unhexlify(data)
+
+
+class Base16Encoder(object):
+
+    @staticmethod
+    def encode(data):
+        return base64.b16encode(data)
+
+    @staticmethod
+    def decode(data):
+        return base64.b16decode(data)
+
+
+class Base32Encoder(object):
+
+    @staticmethod
+    def encode(data):
+        return base64.b32encode(data)
+
+    @staticmethod
+    def decode(data):
+        return base64.b32decode(data)
+
+
+class Base64Encoder(object):
+
+    @staticmethod
+    def encode(data):
+        return base64.b64encode(data)
+
+    @staticmethod
+    def decode(data):
+        return base64.b64decode(data)
+
+
+class Encodable(object):
+
+    def encode(self, encoder=RawEncoder):
+        return encoder.encode(bytes(self))
diff --git a/nacl/hash.py b/nacl/hash.py
index c2ef41124708b8c56fd818e3f7fa4ef6a5877bd3..a82c3ec1e7b48471b838c92902fff9a16b0a3f9e 100644
--- a/nacl/hash.py
+++ b/nacl/hash.py
@@ -1,29 +1,23 @@
 from __future__ import absolute_import
 from __future__ import division
 
-import binascii
-
-from . import nacl
+from . import nacl, encoding
 from .exceptions import CryptoError
 
 
-def sha256(message, binary=False):
+def sha256(message, encoder=encoding.HexEncoder):
     digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha256_BYTES)
     if not nacl.lib.crypto_hash_sha256(digest, message, len(message)):
         raise CryptoError("Hashing failed")
     digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha256_BYTES)[:]
 
-    if binary:
-        return digest
-    return binascii.hexlify(digest)
+    return encoder.encode(digest)
 
 
-def sha512(message, binary=False):
+def sha512(message, encoder=encoding.HexEncoder):
     digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha512_BYTES)
     if not nacl.lib.crypto_hash_sha512(digest, message, len(message)):
         raise CryptoError("Hashing failed")
     digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha512_BYTES)[:]
 
-    if binary:
-        return digest
-    return binascii.hexlify(digest)
+    return encoder.encode(digest)
diff --git a/nacl/signing.py b/nacl/signing.py
index 53a33112462be63ddfa3e6e899c493cb710a890d..f08e3abf6164e92b7fa733cf34a950875cc3b1ba 100644
--- a/nacl/signing.py
+++ b/nacl/signing.py
@@ -3,7 +3,7 @@ from __future__ import division
 
 from . import six
 
-from . import nacl
+from . import nacl, encoding
 from .exceptions import CryptoError
 from .random import random
 
@@ -19,37 +19,51 @@ class SignedMessage(six.binary_type):
     A bytes subclass that holds a messaged that has been signed by a :class:`SigningKey`.
     """
 
+    @classmethod
+    def _from_parts(cls, signature, message, combined):
+        obj = cls(combined)
+        obj._signature = signature
+        obj._message = message
+        return obj
+
     @property
     def signature(self):
         """
         The signature contained within the :class:`SignedMessage`.
         """
-        return self[:nacl.lib.crypto_sign_BYTES]
+        return self._signature
 
     @property
     def message(self):
         """
         The message contained within the :class:`SignedMessage`.
         """
-        return self[nacl.lib.crypto_sign_BYTES:]
+        return self._message
 
 
-class VerifyKey(object):
+class VerifyKey(encoding.Encodable, six.StringFixer, object):
     """
     The public key counterpart to an Ed25519 SigningKey for producing digital
     signatures.
 
     :param key: [:class:`bytes`] Serialized Ed25519 public key
+    :param encoder: A class that is able to decode the `key`
     """
 
-    def __init__(self, key):
+    def __init__(self, key, encoder=encoding.RawEncoder):
+        # Decode the key
+        key = encoder.decode(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):
+    def __bytes__(self):
+        return self._key
+
+    def verify(self, smessage, signature=None, encoder=encoding.RawEncoder):
         """
         Verifies the signature of a signed message, returning the message
         if it has not been tampered with else raising
@@ -59,6 +73,8 @@ class VerifyKey(object):
             signature and message concated together.
         :param signature: [:class:`bytes`] If an unsigned message is given for
             smessage then the detached signature must be provded.
+        :param encoder: A class that is able to decode the secret message and
+            signature.
         :rtype: :class:`bytes`
         """
         if signature is not None:
@@ -66,6 +82,9 @@ class VerifyKey(object):
             #   them.
             smessage = signature + smessage
 
+        # Decode the signed message
+        smessage = encoder.decode(smessage)
+
         message = nacl.ffi.new("unsigned char[]", len(smessage))
         message_len = nacl.ffi.new("unsigned long long *")
 
@@ -75,7 +94,7 @@ class VerifyKey(object):
         return nacl.ffi.buffer(message, message_len[0])[:]
 
 
-class SigningKey(object):
+class SigningKey(encoding.Encodable, six.StringFixer, object):
     """
     Private key for producing digital signatures using the Ed25519 algorithm.
 
@@ -88,12 +107,16 @@ class SigningKey(object):
         masquerade as you.
 
     :param seed: [:class:`bytes`] Random 32-byte value (i.e. private key)
+    :param encoder: A class that is able to decode the seed
 
     :ivar: verify_key: [:class:`~nacl.signing.VerifyKey`] The verify
         (i.e. public) key that corresponds with this signing key.
     """
 
-    def __init__(self, seed):
+    def __init__(self, seed, encoder=encoding.RawEncoder):
+        # Decode the seed
+        seed = encoder.decode(seed)
+
         # Verify that our seed is the proper size
         seed_size = nacl.lib.crypto_sign_SECRETKEYBYTES // 2
         if len(seed) != seed_size:
@@ -113,6 +136,9 @@ class SigningKey(object):
         # Public values
         self.verify_key = VerifyKey(nacl.ffi.buffer(pk, nacl.lib.crypto_sign_PUBLICKEYBYTES)[:])
 
+    def __bytes__(self):
+        return self._seed
+
     @classmethod
     def generate(cls):
         """
@@ -120,13 +146,16 @@ class SigningKey(object):
 
         :rtype: :class:`~nacl.signing.SigningKey`
         """
-        return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2))
+        return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2),
+                    encoder=encoding.RawEncoder,
+                )
 
-    def sign(self, message):
+    def sign(self, message, encoder=encoding.RawEncoder):
         """
         Sign a message using this key.
 
         :param message: [:class:`bytes`] The data to be signed.
+        :param encoder: A class that is used to encode the signed message.
         :rtype: :class:`~nacl.signing.SignedMessage`
         """
         sm = nacl.ffi.new("unsigned char[]", len(message) + nacl.lib.crypto_sign_BYTES)
@@ -135,4 +164,10 @@ class SigningKey(object):
         if not nacl.lib.crypto_sign(sm, smlen, message, len(message), self._signing_key):
             raise CryptoError("Failed to sign the message")
 
-        return SignedMessage(nacl.ffi.buffer(sm, smlen[0])[:])
+        raw_signed = nacl.ffi.buffer(sm, smlen[0])[:]
+
+        signature = encoder.encode(raw_signed[:nacl.lib.crypto_sign_BYTES])
+        message = encoder.encode(raw_signed[nacl.lib.crypto_sign_BYTES:])
+        signed = encoder.encode(raw_signed)
+
+        return SignedMessage._from_parts(signature, message, signed)
diff --git a/nacl/six.py b/nacl/six.py
index ab45e3841ac5b373ba0d155e3c71030105a5691c..0838cf7472b5f1dffa6cab336395a13a5bd30484 100644
--- a/nacl/six.py
+++ b/nacl/six.py
@@ -390,3 +390,13 @@ _add_doc(reraise, """Reraise an exception.""")
 def with_metaclass(meta, base=object):
     """Create a base class with a metaclass."""
     return meta("NewBase", (base,), {})
+
+
+# PyNaCl additions
+class StringFixer(object):
+
+    def __str__(self):
+        if PY3:
+            return self.__unicode__()
+        else:
+            return self.__bytes__()
diff --git a/tests/test_hash.py b/tests/test_hash.py
index 700c3bd041240d93d34158b6be9fa6a902cf1e1d..b24b25bd31392705e06035dc842d7f2468ed7392 100644
--- a/tests/test_hash.py
+++ b/tests/test_hash.py
@@ -1,6 +1,8 @@
-import nacl
 import pytest
 
+import nacl
+import nacl.encoding
+
 
 @pytest.mark.parametrize(("inp", "expected"), [
     (
@@ -27,7 +29,7 @@ def test_sha256_hex(inp, expected):
     )
 ])
 def test_sha256_binary(inp, expected):
-    assert nacl.hash.sha256(inp, binary=True) == expected
+    assert nacl.hash.sha256(inp, encoder=nacl.encoding.RawEncoder) == expected
 
 
 @pytest.mark.parametrize(("inp", "expected"), [
@@ -55,4 +57,4 @@ def test_sha512_hex(inp, expected):
     )
 ])
 def test_sha512_binary(inp, expected):
-    assert nacl.hash.sha512(inp, binary=True) == expected
+    assert nacl.hash.sha512(inp, encoder=nacl.encoding.RawEncoder) == expected
diff --git a/tests/test_signing.py b/tests/test_signing.py
index fe57c970f15eb44e7fea66e00968578c9e259d84..d22e2944320d1eadc84a1d51c8b6d37fcfcaf8f7 100644
--- a/tests/test_signing.py
+++ b/tests/test_signing.py
@@ -7,6 +7,7 @@ import os
 import pytest
 
 import nacl
+import nacl.encoding
 import nacl.nacl
 
 
@@ -38,19 +39,19 @@ class TestSigningKey:
         b"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
     ])
     def test_initialization_with_seed(self, seed):
-        nacl.signing.SigningKey(binascii.unhexlify(seed))
+        nacl.signing.SigningKey(seed, encoder=nacl.encoding.HexEncoder)
 
     @pytest.mark.parametrize(("seed", "message", "signature", "expected"),
             [(x["seed"], x["message"], x["signature"], x["signed"])
                 for x in ed25519_known_answers()]
         )
     def test_message_signing(self, seed, message, signature, expected):
-        signing_key = nacl.signing.SigningKey(binascii.unhexlify(seed))
-        signed = signing_key.sign(binascii.unhexlify(message))
+        signing_key = nacl.signing.SigningKey(seed, encoder=nacl.encoding.HexEncoder)
+        signed = signing_key.sign(binascii.unhexlify(message), encoder=nacl.encoding.HexEncoder)
 
-        assert binascii.hexlify(signed) == expected
-        assert binascii.hexlify(signed.message) == message
-        assert binascii.hexlify(signed.signature) == signature
+        assert signed == expected
+        assert signed.message == message
+        assert signed.signature == signature
 
 
 class TestVerifyKey:
@@ -59,13 +60,10 @@ class TestVerifyKey:
         [(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)
+        key = nacl.signing.VerifyKey(public_key, encoder=nacl.encoding.HexEncoder)
 
-        assert binascii.hexlify(key.verify(signedb)) == message
-        assert binascii.hexlify(key.verify(messageb, signatureb)) == message
+        assert binascii.hexlify(key.verify(signed, encoder=nacl.encoding.HexEncoder)) == message
+        assert binascii.hexlify(key.verify(message, signature, encoder=nacl.encoding.HexEncoder)) == message
 
     def test_invalid_signed_message(self):
         skey = nacl.signing.SigningKey.generate()