diff --git a/docs/public.rst b/docs/public.rst
index 9ba2712a8a935b4b1784e0d57d1d9c812b2107c7..3090566bee258db6cdf714e9565be22512c3af43 100644
--- a/docs/public.rst
+++ b/docs/public.rst
@@ -67,16 +67,16 @@ with equal that from (pkbob, skalice).  This is how the system works:
     #   good source of nonce is just 24 random bytes.
     nonce = nacl.utils.random(Box.NONCE_SIZE)
 
-    # Encrypt our message, it will be exactly 16 bytes longer than the original
-    #   message as it stores authentication information alongside it.
-    ciphertext = bob_box.encrypt(message, nonce)
+    # Encrypt our message, it will be exactly 40 bytes longer than the original
+    #   message as it stores authentication information and nonce alongside it.
+    encrypted = bob_box.encrypt(message, nonce)
 
     # Alice creates a second box with her private key to decrypt the message
     alice_box = Box(skalice, pkbob)
 
     # Decrypt our message, an exception will be raised if the encryption was
     #   tampered with or there was otherwise an error.
-    plaintext = alice_box.decrypt(ciphertext, nonce)
+    plaintext = alice_box.decrypt(encrypted)
 
 
 
@@ -85,7 +85,12 @@ Reference
 
 .. autoclass:: nacl.public.PublicKey
     :members:
+
 .. autoclass:: nacl.public.PrivateKey
     :members:
+
 .. autoclass:: nacl.public.Box
     :members:
+
+.. autoclass:: nacl.utils.EncryptedMessage
+    :members:
diff --git a/docs/secret.rst b/docs/secret.rst
index 3a431bc22d56b8c5750346ad81011e8c2138afee..93c6e45298255e35b0d1a977396db5ac9420a275 100644
--- a/docs/secret.rst
+++ b/docs/secret.rst
@@ -33,13 +33,13 @@ Example
     #   good source of nonce is just 24 random bytes.
     nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
 
-    # Encrypt our message, it will be exactly 16 bytes longer than the original
-    #   message as it stores authentication information alongside it.
-    ciphertext = box.encrypt(message, nonce)
+    # Encrypt our message, it will be exactly 40 bytes longer than the original
+    #   message as it stores authentication information and nonce alongside it.
+    encrypted = box.encrypt(message, nonce)
 
     # Decrypt our message, an exception will be raised if the encryption was
     #   tampered with or there was otherwise an error.
-    plaintext = box.decrypt(ciphertext, nonce)
+    plaintext = box.decrypt(encrypted)
 
 
 Requirements
@@ -89,6 +89,10 @@ Reference
 .. autoclass:: nacl.secret.SecretBox
     :members:
 
+.. autoclass:: nacl.utils.EncryptedMessage
+    :members:
+    :noindex:
+
 
 Algorithm details
 -----------------
diff --git a/nacl/public.py b/nacl/public.py
index ae0c792277d07cf2d41fbf533836aa28e3340b82..266dd5faf59d6a8e0b32e16a22242d583a261901 100644
--- a/nacl/public.py
+++ b/nacl/public.py
@@ -5,7 +5,7 @@ from . import six
 
 from . import nacl, encoding
 from .exceptions import CryptoError
-from .utils import random
+from .utils import EncryptedMessage, random
 
 
 class PublicKey(encoding.Encodable, six.StringFixer, object):
@@ -144,7 +144,7 @@ class Box(encoding.Encodable, six.StringFixer, object):
         :param plaintext: [:class:`bytes`] The plaintext message to encrypt
         :param nonce: [:class:`bytes`] The nonce to use in the encryption
         :param encoder: The encoder to use to encode the ciphertext
-        :rtype: [:class:`bytes`]
+        :rtype: [:class:`nacl.utils.EncryptedMessage`]
         """
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
@@ -165,9 +165,16 @@ class Box(encoding.Encodable, six.StringFixer, object):
         box_zeros = nacl.lib.crypto_box_BOXZEROBYTES
         ciphertext = nacl.ffi.buffer(ciphertext, len(padded))[box_zeros:]
 
-        return encoder.encode(ciphertext)
+        encoded_nonce = encoder.encode(nonce)
+        encoded_ciphertext = encoder.encode(ciphertext)
+
+        return EncryptedMessage._from_parts(
+                    encoded_nonce,
+                    encoded_ciphertext,
+                    encoder.encode(nonce + ciphertext),
+                )
 
-    def decrypt(self, ciphertext, nonce, encoder=encoding.RawEncoder):
+    def decrypt(self, ciphertext, nonce=None, encoder=encoding.RawEncoder):
         """
         Decrypts the ciphertext using the given nonce and returns the
         plaintext message.
@@ -178,12 +185,18 @@ class Box(encoding.Encodable, six.StringFixer, object):
         :param encoder: The encoder used to decode the ciphertext.
         :rtype: [:class:`bytes`]
         """
+        # Decode our ciphertext
+        ciphertext = encoder.decode(ciphertext)
+
+        if nonce is None:
+            # If we were given the nonce and ciphertext combined, split them.
+            nonce = ciphertext[:self.NONCE_SIZE]
+            ciphertext = ciphertext[self.NONCE_SIZE:]
+
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
                              self.NONCE_SIZE)
 
-        ciphertext = encoder.decode(ciphertext)
-
         padded = b"\x00" * nacl.lib.crypto_box_BOXZEROBYTES + ciphertext
         plaintext = nacl.ffi.new("unsigned char[]", len(padded))
 
diff --git a/nacl/secret.py b/nacl/secret.py
index f2d066573bcc61817f501735a3489a41e8c9046f..d9e2353907e20f5f6c5042dcbc65266efb697442 100644
--- a/nacl/secret.py
+++ b/nacl/secret.py
@@ -5,6 +5,7 @@ from . import six
 
 from . import nacl, encoding
 from .exceptions import CryptoError
+from .utils import EncryptedMessage
 
 
 class SecretBox(encoding.Encodable, six.StringFixer, object):
@@ -56,7 +57,7 @@ class SecretBox(encoding.Encodable, six.StringFixer, object):
         :param plaintext: [:class:`bytes`] The plaintext message to encrypt
         :param nonce: [:class:`bytes`] The nonce to use in the encryption
         :param encoder: The encoder to use to encode the ciphertext
-        :rtype: [:class:`bytes`]
+        :rtype: [:class:`nacl.utils.EncryptedMessage`]
         """
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
@@ -73,9 +74,16 @@ class SecretBox(encoding.Encodable, six.StringFixer, object):
         box_zeros = nacl.lib.crypto_secretbox_BOXZEROBYTES
         ciphertext = nacl.ffi.buffer(ciphertext, len(padded))[box_zeros:]
 
-        return encoder.encode(ciphertext)
+        encoded_nonce = encoder.encode(nonce)
+        encoded_ciphertext = encoder.encode(ciphertext)
+
+        return EncryptedMessage._from_parts(
+                    encoded_nonce,
+                    encoded_ciphertext,
+                    encoder.encode(nonce + ciphertext),
+                )
 
-    def decrypt(self, ciphertext, nonce, encoder=encoding.RawEncoder):
+    def decrypt(self, ciphertext, nonce=None, encoder=encoding.RawEncoder):
         """
         Decrypts the ciphertext using the given nonce and returns the plaintext
         message.
@@ -86,12 +94,18 @@ class SecretBox(encoding.Encodable, six.StringFixer, object):
         :param encoder: The encoder used to decode the ciphertext.
         :rtype: [:class:`bytes`]
         """
+        # Decode our ciphertext
+        ciphertext = encoder.decode(ciphertext)
+
+        if nonce is None:
+            # If we were given the nonce and ciphertext combined, split them.
+            nonce = ciphertext[:self.NONCE_SIZE]
+            ciphertext = ciphertext[self.NONCE_SIZE:]
+
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
                                 nacl.lib.crypto_secretbox_NONCEBYTES)
 
-        ciphertext = encoder.decode(ciphertext)
-
         padded = b"\x00" * nacl.lib.crypto_secretbox_BOXZEROBYTES + ciphertext
         plaintext = nacl.ffi.new("unsigned char[]", len(padded))
 
diff --git a/nacl/utils.py b/nacl/utils.py
index 3902ee69b9c0f31f17e6c66df9dfb0366ed247bf..f70328f8bc72c0303565e78af5b597ed07ab4d7e 100644
--- a/nacl/utils.py
+++ b/nacl/utils.py
@@ -2,6 +2,35 @@ from __future__ import absolute_import
 from __future__ import division
 
 from . import nacl
+from . import six
+
+
+class EncryptedMessage(six.binary_type):
+    """
+    A bytes subclass that holds a messaged that has been encrypted by a
+    :class:`SecretBox`.
+    """
+
+    @classmethod
+    def _from_parts(cls, nonce, ciphertext, combined):
+        obj = cls(combined)
+        obj._nonce = nonce
+        obj._ciphertext = ciphertext
+        return obj
+
+    @property
+    def nonce(self):
+        """
+        The nonce used during the encryption of the :class:`EncryptedMessage`.
+        """
+        return self._nonce
+
+    @property
+    def ciphertext(self):
+        """
+        The ciphertext contained within the :class:`EncryptedMessage`.
+        """
+        return self._ciphertext
 
 
 def random(size=32):
diff --git a/tests/test_box.py b/tests/test_box.py
index 539fa815db23341a5f0046ac0fee3afa063fadf3..82ffe436875b3bd94aab3e80da9eb7a1c178058f 100644
--- a/tests/test_box.py
+++ b/tests/test_box.py
@@ -38,11 +38,19 @@ def test_box_encryption(skalice, pkalice, skbob, pkbob, nonce, plaintext, cipher
     skbob = PrivateKey(skbob, encoder=HexEncoder)
 
     box = Box(skbob, pkalice)
+    encrypted = box.encrypt(
+                    binascii.unhexlify(plaintext),
+                    binascii.unhexlify(nonce),
+                    encoder=HexEncoder,
+                )
 
-    plaintext = binascii.unhexlify(plaintext)
-    nonce = binascii.unhexlify(nonce)
+    expected = binascii.hexlify(
+                    binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext),
+                )
 
-    assert box.encrypt(plaintext, nonce, encoder=HexEncoder) == ciphertext
+    assert encrypted == expected
+    assert encrypted.nonce == nonce
+    assert encrypted.ciphertext == ciphertext
 
 
 @pytest.mark.parametrize(("skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext", "ciphertext"), VECTORS)
@@ -59,6 +67,20 @@ def test_box_decryption(skalice, pkalice, skbob, pkbob, nonce, plaintext, cipher
     assert decrypted == plaintext
 
 
+@pytest.mark.parametrize(("skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext", "ciphertext"), VECTORS)
+def test_box_decryption_combined(skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
+    pkbob = PublicKey(pkbob, encoder=HexEncoder)
+    skalice = PrivateKey(skalice, encoder=HexEncoder)
+
+    box = Box(skalice, pkbob)
+
+    combined = binascii.hexlify(
+                    binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext))
+    decrypted = binascii.hexlify(box.decrypt(combined, encoder=HexEncoder))
+
+    assert decrypted == plaintext
+
+
 @pytest.mark.parametrize(("skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext", "ciphertext"), VECTORS)
 def test_box_failed_decryption(skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
     pkbob = PublicKey(pkbob, encoder=HexEncoder)
diff --git a/tests/test_secret.py b/tests/test_secret.py
index 44e0ed7346acfff3308a7e17c8cacc9821e9487e..8c0462e2754af4b4d166dd0850cae3a821f357ca 100644
--- a/tests/test_secret.py
+++ b/tests/test_secret.py
@@ -26,11 +26,19 @@ def test_secret_box_creation():
 @pytest.mark.parametrize(("key", "nonce", "plaintext", "ciphertext"), VECTORS)
 def test_secret_box_encryption(key, nonce, plaintext, ciphertext):
     box = SecretBox(key, encoder=HexEncoder)
+    encrypted = box.encrypt(
+                    binascii.unhexlify(plaintext),
+                    binascii.unhexlify(nonce),
+                    encoder=HexEncoder,
+                )
 
-    plaintext = binascii.unhexlify(plaintext)
-    nonce = binascii.unhexlify(nonce)
+    expected = binascii.hexlify(
+                    binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext),
+                )
 
-    assert box.encrypt(plaintext, nonce, encoder=HexEncoder) == ciphertext
+    assert encrypted == expected
+    assert encrypted.nonce == nonce
+    assert encrypted.ciphertext == ciphertext
 
 
 @pytest.mark.parametrize(("key", "nonce", "plaintext", "ciphertext"), VECTORS)
@@ -42,3 +50,14 @@ def test_secret_box_decryption(key, nonce, plaintext, ciphertext):
                     box.decrypt(ciphertext, nonce, encoder=HexEncoder))
 
     assert decrypted == plaintext
+
+
+@pytest.mark.parametrize(("key", "nonce", "plaintext", "ciphertext"), VECTORS)
+def test_secret_box_decryption_combined(key, nonce, plaintext, ciphertext):
+    box = SecretBox(key, encoder=HexEncoder)
+
+    combined = binascii.hexlify(
+                    binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext))
+    decrypted = binascii.hexlify(box.decrypt(combined, encoder=HexEncoder))
+
+    assert decrypted == plaintext