Merge pull request #1179 from reaperhulk/dsa-numbers-opaque-backend

DSA Opaque Keys for OpenSSL
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index e4faf32..5ed4996 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -166,8 +166,14 @@
     @abc.abstractmethod
     def generate_dsa_private_key(self, parameters):
         """
-        Generate an DSAPrivateKey instance with parameters as
-        a DSAParameters object.
+        Generate a DSAPrivateKey instance with parameters as a DSAParameters
+        object.
+        """
+
+    @abc.abstractmethod
+    def generate_dsa_private_key_and_parameters(self, key_size):
+        """
+        Generate a DSAPrivateKey instance using key size only.
         """
 
     @abc.abstractmethod
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 5776ea6..b3396ea 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -33,6 +33,7 @@
 )
 from cryptography.hazmat.backends.openssl.cmac import _CMACContext
 from cryptography.hazmat.backends.openssl.dsa import (
+    _DSAParameters, _DSAPrivateKey, _DSAPublicKey,
     _DSASignatureContext, _DSAVerificationContext
 )
 from cryptography.hazmat.backends.openssl.ec import (
@@ -470,19 +471,10 @@
             dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
             assert dsa_cdata != self._ffi.NULL
             dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
-            return self._dsa_cdata_to_private_key(dsa_cdata)
+            return _DSAPrivateKey(self, dsa_cdata)
         else:
             raise UnsupportedAlgorithm("Unsupported key type.")
 
-    def _dsa_cdata_to_private_key(self, cdata):
-        return dsa.DSAPrivateKey(
-            modulus=self._bn_to_int(cdata.p),
-            subgroup_order=self._bn_to_int(cdata.q),
-            generator=self._bn_to_int(cdata.g),
-            x=self._bn_to_int(cdata.priv_key),
-            y=self._bn_to_int(cdata.pub_key)
-        )
-
     def _pem_password_cb(self, password):
         """
         Generate a pem_password_cb function pointer that copied the password to
@@ -618,43 +610,85 @@
 
         assert res == 1
 
-        return dsa.DSAParameters(
-            modulus=self._bn_to_int(ctx.p),
-            subgroup_order=self._bn_to_int(ctx.q),
-            generator=self._bn_to_int(ctx.g)
-        )
+        return _DSAParameters(self, ctx)
 
     def generate_dsa_private_key(self, parameters):
         ctx = self._lib.DSA_new()
         assert ctx != self._ffi.NULL
         ctx = self._ffi.gc(ctx, self._lib.DSA_free)
-        ctx.p = self._int_to_bn(parameters.p)
-        ctx.q = self._int_to_bn(parameters.q)
-        ctx.g = self._int_to_bn(parameters.g)
+        if isinstance(parameters, dsa.DSAParameters):
+            ctx.p = self._int_to_bn(parameters.p)
+            ctx.q = self._int_to_bn(parameters.q)
+            ctx.g = self._int_to_bn(parameters.g)
+        else:
+            ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p)
+            ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q)
+            ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g)
 
         self._lib.DSA_generate_key(ctx)
 
-        return dsa.DSAPrivateKey(
-            modulus=self._bn_to_int(ctx.p),
-            subgroup_order=self._bn_to_int(ctx.q),
-            generator=self._bn_to_int(ctx.g),
-            x=self._bn_to_int(ctx.priv_key),
-            y=self._bn_to_int(ctx.pub_key)
-        )
+        return _DSAPrivateKey(self, ctx)
+
+    def generate_dsa_private_key_and_parameters(self, key_size):
+        parameters = self.generate_dsa_parameters(key_size)
+        return self.generate_dsa_private_key(parameters)
 
     def create_dsa_signature_ctx(self, private_key, algorithm):
-        return _DSASignatureContext(self, private_key, algorithm)
+        dsa_cdata = self._dsa_cdata_from_private_key(private_key)
+        key = _DSAPrivateKey(self, dsa_cdata)
+        return _DSASignatureContext(self, key, algorithm)
 
     def create_dsa_verification_ctx(self, public_key, signature,
                                     algorithm):
-        return _DSAVerificationContext(self, public_key, signature,
-                                       algorithm)
+        dsa_cdata = self._dsa_cdata_from_public_key(public_key)
+        key = _DSAPublicKey(self, dsa_cdata)
+        return _DSAVerificationContext(self, key, signature, algorithm)
+
+    def load_dsa_private_numbers(self, numbers):
+        dsa._check_dsa_private_numbers(numbers)
+        parameter_numbers = numbers.public_numbers.parameter_numbers
+
+        dsa_cdata = self._lib.DSA_new()
+        assert dsa_cdata != self._ffi.NULL
+        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
+
+        dsa_cdata.p = self._int_to_bn(parameter_numbers.p)
+        dsa_cdata.q = self._int_to_bn(parameter_numbers.q)
+        dsa_cdata.g = self._int_to_bn(parameter_numbers.g)
+        dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y)
+        dsa_cdata.priv_key = self._int_to_bn(numbers.x)
+
+        return _DSAPrivateKey(self, dsa_cdata)
+
+    def load_dsa_public_numbers(self, numbers):
+        dsa._check_dsa_parameters(numbers.parameter_numbers)
+        dsa_cdata = self._lib.DSA_new()
+        assert dsa_cdata != self._ffi.NULL
+        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
+
+        dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p)
+        dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q)
+        dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g)
+        dsa_cdata.pub_key = self._int_to_bn(numbers.y)
+
+        return _DSAPublicKey(self, dsa_cdata)
+
+    def load_dsa_parameter_numbers(self, numbers):
+        dsa._check_dsa_parameters(numbers)
+        dsa_cdata = self._lib.DSA_new()
+        assert dsa_cdata != self._ffi.NULL
+        dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
+
+        dsa_cdata.p = self._int_to_bn(numbers.p)
+        dsa_cdata.q = self._int_to_bn(numbers.q)
+        dsa_cdata.g = self._int_to_bn(numbers.g)
+
+        return _DSAParameters(self, dsa_cdata)
 
     def _dsa_cdata_from_public_key(self, public_key):
-        # Does not GC the DSA cdata. You *must* make sure it's freed
-        # correctly yourself!
         ctx = self._lib.DSA_new()
         assert ctx != self._ffi.NULL
+        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
         parameters = public_key.parameters()
         ctx.p = self._int_to_bn(parameters.p)
         ctx.q = self._int_to_bn(parameters.q)
@@ -663,10 +697,9 @@
         return ctx
 
     def _dsa_cdata_from_private_key(self, private_key):
-        # Does not GC the DSA cdata. You *must* make sure it's freed
-        # correctly yourself!
         ctx = self._lib.DSA_new()
         assert ctx != self._ffi.NULL
+        ctx = self._ffi.gc(ctx, self._lib.DSA_free)
         parameters = private_key.parameters()
         ctx.p = self._int_to_bn(parameters.p)
         ctx.q = self._int_to_bn(parameters.q)
diff --git a/cryptography/hazmat/backends/openssl/dsa.py b/cryptography/hazmat/backends/openssl/dsa.py
index ec05c3a..5e7a26f 100644
--- a/cryptography/hazmat/backends/openssl/dsa.py
+++ b/cryptography/hazmat/backends/openssl/dsa.py
@@ -16,6 +16,10 @@
 from cryptography import utils
 from cryptography.exceptions import InvalidSignature
 from cryptography.hazmat.primitives import hashes, interfaces
+from cryptography.hazmat.primitives.asymmetric import dsa
+from cryptography.hazmat.primitives.interfaces import (
+    DSAParametersWithNumbers, DSAPrivateKeyWithNumbers, DSAPublicKeyWithNumbers
+)
 
 
 @utils.register_interface(interfaces.AsymmetricVerificationContext)
@@ -32,9 +36,7 @@
         self._hash_ctx.update(data)
 
     def verify(self):
-        self._dsa_cdata = self._backend._dsa_cdata_from_public_key(
-            self._public_key)
-        self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata,
+        self._dsa_cdata = self._backend._ffi.gc(self._public_key._dsa_cdata,
                                                 self._backend._lib.DSA_free)
 
         data_to_verify = self._hash_ctx.finalize()
@@ -43,7 +45,7 @@
         # must be an integer.
         res = self._backend._lib.DSA_verify(
             0, data_to_verify, len(data_to_verify), self._signature,
-            len(self._signature), self._dsa_cdata)
+            len(self._signature), self._public_key._dsa_cdata)
 
         if res != 1:
             errors = self._backend._consume_errors()
@@ -61,17 +63,13 @@
         self._private_key = private_key
         self._algorithm = algorithm
         self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
-        self._dsa_cdata = self._backend._dsa_cdata_from_private_key(
-            self._private_key)
-        self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata,
-                                                self._backend._lib.DSA_free)
 
     def update(self, data):
         self._hash_ctx.update(data)
 
     def finalize(self):
         data_to_sign = self._hash_ctx.finalize()
-        sig_buf_len = self._backend._lib.DSA_size(self._dsa_cdata)
+        sig_buf_len = self._backend._lib.DSA_size(self._private_key._dsa_cdata)
         sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len)
         buflen = self._backend._ffi.new("unsigned int *")
 
@@ -79,8 +77,114 @@
         # must be an integer.
         res = self._backend._lib.DSA_sign(
             0, data_to_sign, len(data_to_sign), sig_buf,
-            buflen, self._dsa_cdata)
+            buflen, self._private_key._dsa_cdata)
         assert res == 1
         assert buflen[0]
 
         return self._backend._ffi.buffer(sig_buf)[:buflen[0]]
+
+
+@utils.register_interface(DSAParametersWithNumbers)
+class _DSAParameters(object):
+    def __init__(self, backend, dsa_cdata):
+        self._backend = backend
+        self._dsa_cdata = dsa_cdata
+
+    def parameter_numbers(self):
+        return dsa.DSAParameterNumbers(
+            p=self._backend._bn_to_int(self._dsa_cdata.p),
+            q=self._backend._bn_to_int(self._dsa_cdata.q),
+            g=self._backend._bn_to_int(self._dsa_cdata.g)
+        )
+
+    def generate_private_key(self):
+        return self._backend.generate_dsa_private_key(self)
+
+
+@utils.register_interface(DSAPrivateKeyWithNumbers)
+class _DSAPrivateKey(object):
+    def __init__(self, backend, dsa_cdata):
+        self._backend = backend
+        self._dsa_cdata = dsa_cdata
+        self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p)
+
+    @property
+    def key_size(self):
+        return self._key_size
+
+    def signer(self, algorithm):
+        return _DSASignatureContext(self._backend, self, algorithm)
+
+    def private_numbers(self):
+        return dsa.DSAPrivateNumbers(
+            public_numbers=dsa.DSAPublicNumbers(
+                parameter_numbers=dsa.DSAParameterNumbers(
+                    p=self._backend._bn_to_int(self._dsa_cdata.p),
+                    q=self._backend._bn_to_int(self._dsa_cdata.q),
+                    g=self._backend._bn_to_int(self._dsa_cdata.g)
+                ),
+                y=self._backend._bn_to_int(self._dsa_cdata.pub_key)
+            ),
+            x=self._backend._bn_to_int(self._dsa_cdata.priv_key)
+        )
+
+    def public_key(self):
+        dsa_cdata = self._backend._lib.DSA_new()
+        assert dsa_cdata != self._backend._ffi.NULL
+        dsa_cdata = self._backend._ffi.gc(
+            dsa_cdata, self._backend._lib.DSA_free
+        )
+        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
+        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
+        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
+        dsa_cdata.pub_key = self._backend._lib.BN_dup(self._dsa_cdata.pub_key)
+        return _DSAPublicKey(self._backend, dsa_cdata)
+
+    def parameters(self):
+        dsa_cdata = self._backend._lib.DSA_new()
+        assert dsa_cdata != self._backend._ffi.NULL
+        dsa_cdata = self._backend._ffi.gc(
+            dsa_cdata, self._backend._lib.DSA_free
+        )
+        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
+        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
+        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
+        return _DSAParameters(self._backend, dsa_cdata)
+
+
+@utils.register_interface(DSAPublicKeyWithNumbers)
+class _DSAPublicKey(object):
+    def __init__(self, backend, dsa_cdata):
+        self._backend = backend
+        self._dsa_cdata = dsa_cdata
+        self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p)
+
+    @property
+    def key_size(self):
+        return self._key_size
+
+    def verifier(self, signature, algorithm):
+        return _DSAVerificationContext(
+            self._backend, self, signature, algorithm
+        )
+
+    def public_numbers(self):
+        return dsa.DSAPublicNumbers(
+            parameter_numbers=dsa.DSAParameterNumbers(
+                p=self._backend._bn_to_int(self._dsa_cdata.p),
+                q=self._backend._bn_to_int(self._dsa_cdata.q),
+                g=self._backend._bn_to_int(self._dsa_cdata.g)
+            ),
+            y=self._backend._bn_to_int(self._dsa_cdata.pub_key)
+        )
+
+    def parameters(self):
+        dsa_cdata = self._backend._lib.DSA_new()
+        assert dsa_cdata != self._backend._ffi.NULL
+        dsa_cdata = self._backend._ffi.gc(
+            dsa_cdata, self._backend._lib.DSA_free
+        )
+        dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
+        dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
+        dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
+        return _DSAParameters(self._backend, dsa_cdata)
diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py
index 4d78679..7a8a61c 100644
--- a/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -21,31 +21,48 @@
 from cryptography.hazmat.primitives import interfaces
 
 
-def _check_dsa_parameters(modulus, subgroup_order, generator):
-    if (
-        not isinstance(modulus, six.integer_types) or
-        not isinstance(subgroup_order, six.integer_types) or
-        not isinstance(generator, six.integer_types)
-    ):
-        raise TypeError("DSA parameters must be integers.")
+def generate_parameters(key_size, backend):
+    return backend.generate_dsa_parameters(key_size)
 
-    if (utils.bit_length(modulus),
-        utils.bit_length(subgroup_order)) not in (
+
+def generate_private_key(key_size, backend):
+    return backend.generate_dsa_private_key_and_parameters(key_size)
+
+
+def _check_dsa_parameters(parameters):
+    if (utils.bit_length(parameters.p),
+        utils.bit_length(parameters.q)) not in (
             (1024, 160),
             (2048, 256),
             (3072, 256)):
-        raise ValueError("modulus and subgroup_order lengths must be "
+        raise ValueError("p and q lengths must be "
                          "one of these pairs (1024, 160) or (2048, 256) "
                          "or (3072, 256).")
 
-    if generator <= 1 or generator >= modulus:
-        raise ValueError("generator must be > 1 and < modulus.")
+    if not (1 < parameters.g < parameters.p):
+        raise ValueError("g, p don't satisfy 1 < g < p.")
+
+
+def _check_dsa_private_numbers(numbers):
+    parameters = numbers.public_numbers.parameter_numbers
+    _check_dsa_parameters(parameters)
+    if numbers.x <= 0 or numbers.x >= parameters.q:
+        raise ValueError("x must be > 0 and < q.")
+
+    if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p):
+        raise ValueError("y must be equal to (g ** x % p).")
 
 
 @utils.register_interface(interfaces.DSAParameters)
 class DSAParameters(object):
     def __init__(self, modulus, subgroup_order, generator):
-        _check_dsa_parameters(modulus, subgroup_order, generator)
+        _check_dsa_parameters(
+            DSAParameterNumbers(
+                p=modulus,
+                q=subgroup_order,
+                g=generator
+            )
+        )
 
         self._modulus = modulus
         self._subgroup_order = subgroup_order
@@ -59,7 +76,13 @@
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
-        return backend.generate_dsa_parameters(key_size)
+        parameters = backend.generate_dsa_parameters(key_size)
+        numbers = parameters.parameter_numbers()
+        return cls(
+            modulus=numbers.p,
+            subgroup_order=numbers.q,
+            generator=numbers.g
+        )
 
     @property
     def modulus(self):
@@ -89,18 +112,25 @@
 @utils.register_interface(interfaces.DSAPrivateKey)
 class DSAPrivateKey(object):
     def __init__(self, modulus, subgroup_order, generator, x, y):
-        _check_dsa_parameters(modulus, subgroup_order, generator)
         if (
             not isinstance(x, six.integer_types) or
             not isinstance(y, six.integer_types)
         ):
             raise TypeError("DSAPrivateKey arguments must be integers.")
 
-        if x <= 0 or x >= subgroup_order:
-            raise ValueError("x must be > 0 and < subgroup_order.")
-
-        if y != pow(generator, x, modulus):
-            raise ValueError("y must be equal to (generator ** x % modulus).")
+        _check_dsa_private_numbers(
+            DSAPrivateNumbers(
+                public_numbers=DSAPublicNumbers(
+                    parameter_numbers=DSAParameterNumbers(
+                        p=modulus,
+                        q=subgroup_order,
+                        g=generator
+                    ),
+                    y=y
+                ),
+                x=x
+            )
+        )
 
         self._modulus = modulus
         self._subgroup_order = subgroup_order
@@ -116,7 +146,15 @@
                 _Reasons.BACKEND_MISSING_INTERFACE
             )
 
-        return backend.generate_dsa_private_key(parameters)
+        key = backend.generate_dsa_private_key(parameters)
+        private_numbers = key.private_numbers()
+        return cls(
+            modulus=private_numbers.public_numbers.parameter_numbers.p,
+            subgroup_order=private_numbers.public_numbers.parameter_numbers.q,
+            generator=private_numbers.public_numbers.parameter_numbers.g,
+            x=private_numbers.x,
+            y=private_numbers.public_numbers.y
+        )
 
     def signer(self, algorithm, backend):
         if not isinstance(backend, DSABackend):
@@ -151,7 +189,13 @@
 @utils.register_interface(interfaces.DSAPublicKey)
 class DSAPublicKey(object):
     def __init__(self, modulus, subgroup_order, generator, y):
-        _check_dsa_parameters(modulus, subgroup_order, generator)
+        _check_dsa_parameters(
+            DSAParameterNumbers(
+                p=modulus,
+                q=subgroup_order,
+                g=generator
+            )
+        )
         if not isinstance(y, six.integer_types):
             raise TypeError("y must be an integer.")
 
@@ -210,6 +254,9 @@
     def g(self):
         return self._g
 
+    def parameters(self, backend):
+        return backend.load_dsa_parameter_numbers(self)
+
 
 class DSAPublicNumbers(object):
     def __init__(self, y, parameter_numbers):
@@ -232,6 +279,9 @@
     def parameter_numbers(self):
         return self._parameter_numbers
 
+    def public_key(self, backend):
+        return backend.load_dsa_public_numbers(self)
+
 
 class DSAPrivateNumbers(object):
     def __init__(self, x, public_numbers):
@@ -252,3 +302,6 @@
     @property
     def public_numbers(self):
         return self._public_numbers
+
+    def private_key(self, backend):
+        return backend.load_dsa_private_numbers(self)
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index ef8640c..d60f9e0 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -251,7 +251,11 @@
 
 @six.add_metaclass(abc.ABCMeta)
 class DSAParameters(object):
-    pass
+    @abc.abstractmethod
+    def generate_private_key(self):
+        """
+        Generates and returns a DSAPrivateKey.
+        """
 
 
 @six.add_metaclass(abc.ABCMeta)
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 5cbd47d..fea935c 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -421,6 +421,22 @@
             1.0.0 and the key size is larger than 1024; older OpenSSL versions
             do not support keys larger than 1024 bits.
 
+    .. method:: generate_dsa_private_key_and_parameters(key_size)
+
+        :param int key_size: The length of the modulus in bits. It should be
+            either 1024, 2048 or 3072. For keys generated in 2014 this should
+            be at least 2048.
+            Note that some applications (such as SSH) have not yet gained
+            support for larger key sizes specified in FIPS 186-3 and are still
+            restricted to only the 1024-bit keys specified in FIPS 186-2.
+
+        :return: A new instance of a
+            :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey`
+            provider.
+
+        :raises ValueError: This is raised if the key size is not supported
+            by the backend.
+
     .. method:: create_dsa_signature_ctx(private_key, algorithm)
 
         :param private_key: An instance of a
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 2167e52..095c49b 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -7,6 +7,51 @@
 
 `DSA`_ is a `public-key`_ algorithm for signing messages.
 
+.. function:: generate_private_key(key_size, backend)
+
+    .. versionadded:: 0.5
+
+    Generate a DSA private key from the given key size. This function will
+    generate a new set of parameters and key in one step.
+
+    :param int key_size: The length of the modulus in bits. It should be
+        either 1024, 2048 or 3072. For keys generated in 2014 this should
+        be `at least 2048`_ (See page 41).  Note that some applications
+        (such as SSH) have not yet gained support for larger key sizes
+        specified in FIPS 186-3 and are still restricted to only the
+        1024-bit keys specified in FIPS 186-2.
+
+    :param backend: A
+        :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+        provider.
+
+    :return: A :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey`
+        provider.
+
+.. function:: generate_parameters(key_size, backend)
+
+    .. versionadded:: 0.5
+
+    Generate DSA parameters using the provided ``backend``.
+
+    :param int key_size: The length of the modulus in bits. It should be
+        either 1024, 2048 or 3072. For keys generated in 2014 this should
+        be `at least 2048`_ (See page 41).  Note that some applications
+        (such as SSH) have not yet gained support for larger key sizes
+        specified in FIPS 186-3 and are still restricted to only the
+        1024-bit keys specified in FIPS 186-2.
+
+    :param backend: A
+        :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+        provider.
+
+    :return: A :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters`
+        provider.
+
+    :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if
+        the provided ``backend`` does not implement
+        :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+
 .. class:: DSAParameters(modulus, subgroup_order, generator)
 
     .. versionadded:: 0.4
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index 755cef4..ac47c1e 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -282,6 +282,17 @@
 
     `DSA`_ parameters.
 
+    .. method:: generate_private_key()
+
+        .. versionadded:: 0.5
+
+        Generate a DSA private key. This method can be used to generate many
+        new private keys from a single set of parameters.
+
+        :return: A
+            :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey`
+            provider.
+
 
 .. class:: DSAParametersWithNumbers
 
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index 1c266ba..531b448 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -20,7 +20,7 @@
 
 from cryptography.exceptions import (
     AlreadyFinalized, InvalidSignature, _Reasons)
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, interfaces
 from cryptography.hazmat.primitives.asymmetric import dsa
 from cryptography.utils import bit_length
 
@@ -70,10 +70,14 @@
 
 @pytest.mark.dsa
 class TestDSA(object):
-    def test_generate_dsa_parameters(self, backend):
+    def test_generate_dsa_parameters_class_method(self, backend):
         parameters = dsa.DSAParameters.generate(1024, backend)
         assert bit_length(parameters.p) == 1024
 
+    def test_generate_dsa_parameters(self, backend):
+        parameters = dsa.generate_parameters(1024, backend)
+        assert isinstance(parameters, interfaces.DSAParameters)
+
     def test_generate_invalid_dsa_parameters(self, backend):
         with pytest.raises(ValueError):
             dsa.DSAParameters.generate(1, backend)
@@ -87,17 +91,41 @@
         )
     )
     def test_generate_dsa_keys(self, vector, backend):
-        parameters = dsa.DSAParameters(modulus=vector['p'],
-                                       subgroup_order=vector['q'],
-                                       generator=vector['g'])
-        skey = dsa.DSAPrivateKey.generate(parameters, backend)
+        parameters = dsa.DSAParameterNumbers(
+            p=vector['p'],
+            q=vector['q'],
+            g=vector['g']
+        ).parameters(backend)
+        skey = parameters.generate_private_key()
+        if isinstance(skey, interfaces.DSAPrivateKeyWithNumbers):
+            numbers = skey.private_numbers()
+            skey_parameters = numbers.public_numbers.parameter_numbers
+            pkey = skey.public_key()
+            parameters = pkey.parameters()
+            parameter_numbers = parameters.parameter_numbers()
+            assert parameter_numbers.p == skey_parameters.p
+            assert parameter_numbers.q == skey_parameters.q
+            assert parameter_numbers.g == skey_parameters.g
+            assert skey_parameters.p == vector['p']
+            assert skey_parameters.q == vector['q']
+            assert skey_parameters.g == vector['g']
+            assert skey.key_size == bit_length(vector['p'])
+            assert pkey.key_size == skey.key_size
+            public_numbers = pkey.public_numbers()
+            assert numbers.public_numbers.y == public_numbers.y
+            assert numbers.public_numbers.y == pow(
+                skey_parameters.g, numbers.x, skey_parameters.p
+            )
 
-        skey_parameters = skey.parameters()
-        assert skey_parameters.p == vector['p']
-        assert skey_parameters.q == vector['q']
-        assert skey_parameters.g == vector['g']
-        assert skey.key_size == bit_length(vector['p'])
-        assert skey.y == pow(skey_parameters.g, skey.x, skey_parameters.p)
+    def test_generate_dsa_private_key_and_parameters(self, backend):
+        skey = dsa.generate_private_key(1024, backend)
+        assert skey
+        if isinstance(skey, interfaces.DSAPrivateKeyWithNumbers):
+            numbers = skey.private_numbers()
+            skey_parameters = numbers.public_numbers.parameter_numbers
+            assert numbers.public_numbers.y == pow(
+                skey_parameters.g, numbers.x, skey_parameters.p
+            )
 
     def test_invalid_parameters_argument_types(self):
         with pytest.raises(TypeError):
@@ -654,11 +682,14 @@
                 "{0} does not support the provided parameters".format(backend)
             )
 
-        public_key = dsa.DSAPublicKey(
-            vector['p'], vector['q'], vector['g'], vector['y']
-        )
+        public_key = dsa.DSAPublicNumbers(
+            parameter_numbers=dsa.DSAParameterNumbers(
+                vector['p'], vector['q'], vector['g']
+            ),
+            y=vector['y']
+        ).public_key(backend)
         sig = der_encode_dsa_signature(vector['r'], vector['s'])
-        verifier = public_key.verifier(sig, algorithm(), backend)
+        verifier = public_key.verifier(sig, algorithm())
         verifier.update(vector['msg'])
         if vector['result'] == "F":
             with pytest.raises(InvalidSignature):
@@ -728,16 +759,22 @@
                 "{0} does not support the provided parameters".format(backend)
             )
 
-        private_key = dsa.DSAPrivateKey(
-            vector['p'], vector['q'], vector['g'], vector['x'], vector['y']
-        )
-        signer = private_key.signer(algorithm(), backend)
+        private_key = dsa.DSAPrivateNumbers(
+            public_numbers=dsa.DSAPublicNumbers(
+                parameter_numbers=dsa.DSAParameterNumbers(
+                    vector['p'], vector['q'], vector['g']
+                ),
+                y=vector['y']
+            ),
+            x=vector['x']
+        ).private_key(backend)
+        signer = private_key.signer(algorithm())
         signer.update(vector['msg'])
         signature = signer.finalize()
         assert signature
 
         public_key = private_key.public_key()
-        verifier = public_key.verifier(signature, algorithm(), backend)
+        verifier = public_key.verifier(signature, algorithm())
         verifier.update(vector['msg'])
         verifier.verify()
 
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index 8a90b30..30ac4f3 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -21,7 +21,6 @@
 
 from cryptography.exceptions import _Reasons
 from cryptography.hazmat.primitives import interfaces
-from cryptography.hazmat.primitives.asymmetric import dsa
 from cryptography.hazmat.primitives.serialization import (
     load_pem_pkcs8_private_key, load_pem_traditional_openssl_private_key
 )
@@ -73,7 +72,7 @@
         )
 
         assert key
-        assert isinstance(key, dsa.DSAPrivateKey)
+        assert isinstance(key, interfaces.DSAPrivateKey)
 
     def test_key1_pem_encrypted_values(self, backend):
         pkey = load_vectors_from_file(
@@ -480,41 +479,46 @@
             )
         )
         assert key
-        assert isinstance(key, dsa.DSAPrivateKey)
+        assert isinstance(key, interfaces.DSAPrivateKey)
 
         params = key.parameters()
-        assert isinstance(params, dsa.DSAParameters)
+        assert isinstance(params, interfaces.DSAParameters)
 
-        assert key.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203", 16)
-        assert key.y == int(
-            "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1"
-            "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d"
-            "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350"
-            "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6"
-            "ab6bcce04bfdf5b6",
-            16
-        )
+        if isinstance(params, interfaces.DSAParametersWithNumbers):
+            num = key.private_numbers()
+            pub = num.public_numbers
+            parameter_numbers = pub.parameter_numbers
+            assert num.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203",
+                                16)
+            assert pub.y == int(
+                "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1"
+                "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d"
+                "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350"
+                "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6"
+                "ab6bcce04bfdf5b6",
+                16
+            )
 
-        assert params.p == int(
-            "00aa0930cc145825221caffa28ac2894196a27833de5ec21270791689420"
-            "7774a2e7b238b0d36f1b2499a2c2585083eb01432924418d867faa212dd1"
-            "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb"
-            "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05"
-            "940cabe5d2de49a167",
-            16
-        )
+            assert parameter_numbers.p == int(
+                "00aa0930cc145825221caffa28ac2894196a27833de5ec21270791689420"
+                "7774a2e7b238b0d36f1b2499a2c2585083eb01432924418d867faa212dd1"
+                "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb"
+                "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05"
+                "940cabe5d2de49a167",
+                16
+            )
 
-        assert params.q == int("00adc0e869b36f0ac013a681fdf4d4899d69820451",
-                               16)
+            assert parameter_numbers.q == int(
+                "00adc0e869b36f0ac013a681fdf4d4899d69820451", 16)
 
-        assert params.g == int(
-            "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e"
-            "e13201ff48890bbfc33d79bacaec71e7a778507bd5f1a66422e39415be03"
-            "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10"
-            "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc"
-            "68ce5cfff241cd3246",
-            16
-        )
+            assert parameter_numbers.g == int(
+                "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e"
+                "e13201ff48890bbfc33d79bacaec71e7a778507bd5f1a66422e39415be03"
+                "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10"
+                "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc"
+                "68ce5cfff241cd3246",
+                16
+            )
 
     @pytest.mark.parametrize(
         ("key_file", "password"),