add support for serialization of EC private keys
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 8b9a584..cf616a0 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -326,6 +326,45 @@
         :returns: An :class:`EllipticCurvePrivateNumbers` instance.
 
 
+.. class:: EllipticCurvePrivateKeyWithSerialization
+
+    .. versionadded:: 0.8
+
+    Extends :class:`EllipticCurvePrivateKey`.
+
+    .. method:: private_numbers()
+
+        Create a :class:`EllipticCurvePrivateNumbers` object.
+
+        :returns: An :class:`EllipticCurvePrivateNumbers` instance.
+
+    .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+        Allows serialization of the key to bytes. Encoding (
+        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+        :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`),
+        format (
+        :attr:`~cryptography.hazmat.primitives.serialization.Format.TraditionalOpenSSL`
+        or
+        :attr:`~cryptography.hazmat.primitives.serialization.Format.PKCS8`) and
+        encryption algorithm (such as
+        :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption`
+        or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`)
+        are chosen to define the exact serialization.
+
+        :param encoding: A value from the
+            :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+        :param format: A value from the
+            :class:`~cryptography.hazmat.primitives.serialization.Format` enum.
+
+        :param encryption_algorithm: An instance of an object conforming to the
+            :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
+            interface.
+
+        :return bytes: Serialized key.
+
+
 .. class:: EllipticCurvePublicKey
 
     .. versionadded:: 0.5
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index e11b02a..49a0e36 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -290,8 +290,11 @@
 
     .. versionadded:: 0.8
 
-    An enumeration for private key formats. Used with
-    :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`.
+    An enumeration for private key formats. Used with the ``private_bytes``
+    method available on
+    :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+    and
+    :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`.
 
     .. attribute:: TraditionalOpenSSL
 
@@ -311,8 +314,11 @@
 
     .. versionadded:: 0.8
 
-    An enumeration for encoding types. Used with
-    :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`.
+    An enumeration for encoding types. Used with the ``private_bytes`` method
+    available on
+    :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+    and
+    :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`.
 
     .. attribute:: PEM
 
@@ -329,8 +335,10 @@
 .. class:: KeySerializationEncryption
 
     Objects with this interface are usable as encryption types with methods
-    like
-    :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`.
+    like ``private_bytes`` available on
+    :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+    and
+    :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`.
     All other classes in this section represent the available choices for
     encryption and have this interface. They are used with
     :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`.
diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py
index 52c93da..19d646e 100644
--- a/src/cryptography/hazmat/backends/openssl/ec.py
+++ b/src/cryptography/hazmat/backends/openssl/ec.py
@@ -148,7 +148,7 @@
         return True
 
 
-@utils.register_interface(ec.EllipticCurvePrivateKeyWithNumbers)
+@utils.register_interface(ec.EllipticCurvePrivateKeyWithSerialization)
 class _EllipticCurvePrivateKey(object):
     def __init__(self, backend, ec_key_cdata):
         self._backend = backend
@@ -200,6 +200,23 @@
             public_numbers=self.public_key().public_numbers()
         )
 
+    def private_bytes(self, encoding, format, encryption_algorithm):
+        evp_pkey = self._backend._lib.EVP_PKEY_new()
+        assert evp_pkey != self._backend._ffi.NULL
+        evp_pkey = self._backend._ffi.gc(
+            evp_pkey, self._backend._lib.EVP_PKEY_free
+        )
+        res = self._backend._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, self._ec_key)
+        assert res == 1
+        return self._backend._private_key_bytes(
+            encoding,
+            format,
+            encryption_algorithm,
+            self._backend._lib.PEM_write_bio_ECPrivateKey,
+            evp_pkey,
+            self._ec_key
+        )
+
 
 @utils.register_interface(ec.EllipticCurvePublicKeyWithNumbers)
 class _EllipticCurvePublicKey(object):
diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py
index d0c70f5..98c7648 100644
--- a/src/cryptography/hazmat/bindings/openssl/pem.py
+++ b/src/cryptography/hazmat/bindings/openssl/pem.py
@@ -72,9 +72,23 @@
 """
 
 MACROS = """
+int PEM_write_bio_ECPrivateKey(BIO *, EC_KEY *, const EVP_CIPHER *,
+                               unsigned char *, int, pem_password_cb *,
+                               void *);
 """
 
 CUSTOMIZATIONS = """
+// Cryptography_HAS_EC is provided by ec.py so we don't need to define it here
+#ifdef OPENSSL_NO_EC
+int (*PEM_write_bio_ECPrivateKey)(BIO *, EC_KEY *, const EVP_CIPHER *,
+                                  unsigned char *, int, pem_password_cb *,
+                                  void *) = NULL;
+#endif
+
 """
 
-CONDITIONAL_NAMES = {}
+CONDITIONAL_NAMES = {
+    "Cryptography_HAS_EC": [
+        "PEM_write_bio_ECPrivateKey"
+    ]
+}
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py
index c7749ca..52e1481 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -57,13 +57,30 @@
 
 
 @six.add_metaclass(abc.ABCMeta)
-class EllipticCurvePrivateKeyWithNumbers(EllipticCurvePrivateKey):
+class EllipticCurvePrivateKeyWithSerialization(EllipticCurvePrivateKey):
     @abc.abstractmethod
     def private_numbers(self):
         """
         Returns an EllipticCurvePrivateNumbers.
         """
 
+    @abc.abstractmethod
+    def private_bytes(self, encoding, format, encryption_algorithm):
+        """
+        Returns the key serialized as bytes.
+        """
+
+
+EllipticCurvePrivateKeyWithNumbers = utils.deprecated(
+    EllipticCurvePrivateKeyWithSerialization,
+    __name__,
+    (
+        "The EllipticCurvePrivateKeyWithNumbers interface has been renamed to "
+        "EllipticCurvePrivateKeyWithSerialization"
+    ),
+    utils.DeprecatedIn08
+)
+
 
 @six.add_metaclass(abc.ABCMeta)
 class EllipticCurvePublicKey(object):
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index ea621ad..f1c49cf 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -10,8 +10,10 @@
 import pytest
 
 from cryptography import exceptions, utils
-from cryptography.hazmat.backends.interfaces import EllipticCurveBackend
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.backends.interfaces import (
+    EllipticCurveBackend, PEMSerializationBackend
+)
+from cryptography.hazmat.primitives import hashes, serialization
 from cryptography.hazmat.primitives.asymmetric import ec
 from cryptography.hazmat.primitives.asymmetric.utils import (
     encode_rfc6979_signature
@@ -31,6 +33,13 @@
 }
 
 
+def _skip_if_no_serialization(key, backend):
+    if not isinstance(key, ec.EllipticCurvePrivateKeyWithSerialization):
+        pytest.skip(
+            "{0} does not support EC key serialization".format(backend)
+        )
+
+
 def _skip_ecdsa_vector(backend, curve_type, hash_type):
     if not backend.elliptic_curve_signature_algorithm_supported(
         ec.ECDSA(hash_type()),
@@ -63,12 +72,22 @@
     algorithm = None
 
 
+@utils.register_interface(serialization.KeySerializationEncryption)
+class DummyKeyEncryption(object):
+    pass
+
+
 @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
 def test_skip_curve_unsupported(backend):
     with pytest.raises(pytest.skip.Exception):
         _skip_curve_unsupported(backend, DummyCurve())
 
 
+def test_skip_no_serialization():
+    with pytest.raises(pytest.skip.Exception):
+        _skip_if_no_serialization("fakebackend", "fakekey")
+
+
 def test_ec_numbers():
     numbers = ec.EllipticCurvePrivateNumbers(
         1,
@@ -378,3 +397,144 @@
             1, ec.EllipticCurvePublicNumbers(1, 2, ec.SECP521R1())
         )
         assert priv != object()
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestECSerialization(object):
+    @pytest.mark.parametrize(
+        ("fmt", "password"),
+        itertools.product(
+            [
+                serialization.Format.TraditionalOpenSSL,
+                serialization.Format.PKCS8
+            ],
+            [
+                b"s",
+                b"longerpassword",
+                b"!*$&(@#$*&($T@%_somesymbols",
+                b"\x01" * 1000,
+            ]
+        )
+    )
+    def test_private_bytes_encrypted_pem(self, backend, fmt, password):
+        key_bytes = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: pemfile.read().encode()
+        )
+        key = serialization.load_pem_private_key(key_bytes, None, backend)
+        _skip_if_no_serialization(key, backend)
+        serialized = key.private_bytes(
+            serialization.Encoding.PEM,
+            fmt,
+            serialization.BestAvailableEncryption(password)
+        )
+        loaded_key = serialization.load_pem_private_key(
+            serialized, password, backend
+        )
+        loaded_priv_num = loaded_key.private_numbers()
+        priv_num = key.private_numbers()
+        assert loaded_priv_num == priv_num
+
+    @pytest.mark.parametrize(
+        "fmt",
+        [serialization.Format.TraditionalOpenSSL, serialization.Format.PKCS8],
+    )
+    def test_private_bytes_unencrypted_pem(self, backend, fmt):
+        key_bytes = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: pemfile.read().encode()
+        )
+        key = serialization.load_pem_private_key(key_bytes, None, backend)
+        _skip_if_no_serialization(key, backend)
+        serialized = key.private_bytes(
+            serialization.Encoding.PEM,
+            fmt,
+            serialization.NoEncryption()
+        )
+        loaded_key = serialization.load_pem_private_key(
+            serialized, None, backend
+        )
+        loaded_priv_num = loaded_key.private_numbers()
+        priv_num = key.private_numbers()
+        assert loaded_priv_num == priv_num
+
+    def test_private_bytes_traditional_openssl_unencrypted_pem(self, backend):
+        key_bytes = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PEM_Serialization", "ec_private_key.pem"),
+            lambda pemfile: pemfile.read().encode()
+        )
+        key = serialization.load_pem_private_key(key_bytes, None, backend)
+        serialized = key.private_bytes(
+            serialization.Encoding.PEM,
+            serialization.Format.TraditionalOpenSSL,
+            serialization.NoEncryption()
+        )
+        assert serialized == key_bytes
+
+    def test_private_bytes_invalid_encoding(self, backend):
+        key = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: serialization.load_pem_private_key(
+                pemfile.read().encode(), None, backend
+            )
+        )
+        _skip_if_no_serialization(key, backend)
+        with pytest.raises(TypeError):
+            key.private_bytes(
+                "notencoding",
+                serialization.Format.PKCS8,
+                serialization.NoEncryption()
+            )
+
+    def test_private_bytes_invalid_format(self, backend):
+        key = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: serialization.load_pem_private_key(
+                pemfile.read().encode(), None, backend
+            )
+        )
+        _skip_if_no_serialization(key, backend)
+        with pytest.raises(TypeError):
+            key.private_bytes(
+                serialization.Encoding.PEM,
+                "invalidformat",
+                serialization.NoEncryption()
+            )
+
+    def test_private_bytes_invalid_encryption_algorithm(self, backend):
+        key = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: serialization.load_pem_private_key(
+                pemfile.read().encode(), None, backend
+            )
+        )
+        _skip_if_no_serialization(key, backend)
+        with pytest.raises(TypeError):
+            key.private_bytes(
+                serialization.Encoding.PEM,
+                serialization.Format.TraditionalOpenSSL,
+                "notanencalg"
+            )
+
+    def test_private_bytes_unsupported_encryption_type(self, backend):
+        key = load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "PKCS8", "ec_private_key.pem"),
+            lambda pemfile: serialization.load_pem_private_key(
+                pemfile.read().encode(), None, backend
+            )
+        )
+        _skip_if_no_serialization(key, backend)
+        with pytest.raises(ValueError):
+            key.private_bytes(
+                serialization.Encoding.PEM,
+                serialization.Format.TraditionalOpenSSL,
+                DummyKeyEncryption()
+            )