DH subgroup order (q) (#3369)

* Support DH q (subgroup order)

* Change RFC5114.txt to NIST format

* Add tests for DH q

* Update docs for DH q

* Fix pep8

* Improve test covergae for DH q

* Create _dh_params_dup that copy q if DHparams_dup don't

On OpenSSL < 1.0.2 DHparams_dup don't copy q. _dh_params_dup
call DHparams_dup and if the version is smaller than 1.0.2
copy q manually

* Copy q manually on libressl

* Add to test vectors serialized RFC5114 2048 bit DH parameters with 224 bit subgroup

* Support serialization of DH with q

* Add tests for serialization of DH with q

* Support DH serialization with q only if Cryptography_HAS_EVP_PKEY_DHX is true

* Raise exception when trying to serialize DH X9.42 when not supported

* raise unsupported key type when deserilizing DH X9.42 if not supported

* pep8 fixes

* Fix test_serialization

* Add dhx_serialization_supported method to DHBacked

* document q in dh_parameters_supported

* Rename dhx_serialization_supported to dh_x942_serialization_supported
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index 15fe2d2..f9f865e 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -110,6 +110,20 @@
   ``vectors/cryptography_vectors/asymmetric/DH/dhpub.der`` contains
   are the above parameters and keys in DER format.
 
+* ``vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.pem``,
+  ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.pem`` and
+  ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.pem`` contains
+  Diffie-Hellman parameters and key respectively. The keys were
+  generated using OpenSSL following `DHKE`_ guide. When creating the
+  parameters we added the `-pkeyopt dh_rfc5114:2` option to use
+  RFC5114 2048 bit DH parameters with 224 bit subgroup.
+  ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.txt`` contains
+  all parameter in text.
+  ``vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.der``,
+  ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der`` and
+  ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der`` contains
+  are the above parameters and keys in DER format.
+
 
 X.509
 ~~~~~
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 87fc6ab..4f15d5c 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -666,14 +666,23 @@
         :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised
             when any backend specific criteria are not met.
 
-    .. method:: dh_parameters_supported(p, g)
+    .. method:: dh_parameters_supported(p, g, q=None)
 
         :param int p: The p value of the DH key.
 
         :param int g: The g value of the DH key.
 
-        :returns: ``True`` if the given values of ``p`` and ``g`` are supported
-            by this backend, otherwise ``False``.
+        :param int q: The q value of the DH key.
+
+        :returns: ``True`` if the given values of ``p``, ``g`` and ``q``
+            are supported by this backend, otherwise ``False``.
+
+    .. versionadded:: 1.8
+
+    .. method:: dh_x942_serialization_supported()
+
+        :returns: True if serialization of DH objects with
+            subgroup order (q) is supported by this backend.
 
 
 .. class:: ScryptBackend
diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst
index 73e534e..759b265 100644
--- a/docs/hazmat/primitives/asymmetric/dh.rst
+++ b/docs/hazmat/primitives/asymmetric/dh.rst
@@ -223,7 +223,7 @@
 Numbers
 ~~~~~~~
 
-.. class:: DHParameterNumbers(p, g)
+.. class:: DHParameterNumbers(p, g, q=None)
 
     .. versionadded:: 0.8
 
@@ -239,7 +239,15 @@
 
         :type: int
 
-        The generator value. Must be 2 or 5.
+        The generator value. Must be 2 or 5 (Unless q is given).
+
+    .. attribute:: q
+
+        .. versionadded:: 1.8
+
+        :type: int
+
+        p subgroup order value.
 
 .. class:: DHPrivateNumbers(x, public_numbers)
 
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index c5f2951..9ed50cc 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -361,11 +361,18 @@
         """
 
     @abc.abstractmethod
-    def dh_parameters_supported(self, p, g):
+    def dh_parameters_supported(self, p, g, q=None):
         """
         Returns whether the backend supports DH with these parameter values.
         """
 
+    @abc.abstractmethod
+    def dh_x942_serialization_supported(self):
+        """
+        Returns True if the backend supports the serialization of DH objects
+        with subgroup order (q).
+        """
+
 
 @six.add_metaclass(abc.ABCMeta)
 class ScryptBackend(object):
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 097b490..bb30c66 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -481,9 +481,18 @@
             _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
         )
 
-    def dh_parameters_supported(self, p, g):
+    def dh_parameters_supported(self, p, g, q=None):
         for b in self._filtered_backends(DHBackend):
-            return b.dh_parameters_supported(p, g)
+            return b.dh_parameters_supported(p, g, q)
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support Diffie-Hellman",
+            _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+        )
+
+    def dh_x942_serialization_supported(self):
+        for b in self._filtered_backends(DHBackend):
+            return b.dh_x942_serialization_supported()
 
         raise UnsupportedAlgorithm(
             "This backend does not support Diffie-Hellman",
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 7753816..446891d 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -23,7 +23,8 @@
 from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
 from cryptography.hazmat.backends.openssl.cmac import _CMACContext
 from cryptography.hazmat.backends.openssl.dh import (
-    _DHParameters, _DHPrivateKey, _DHPublicKey
+    _DHParameters, _DHPrivateKey, _DHPublicKey,
+    _dh_params_dup
 )
 from cryptography.hazmat.backends.openssl.dsa import (
     _DSAParameters, _DSAPrivateKey, _DSAPublicKey
@@ -99,6 +100,9 @@
         self._cipher_registry = {}
         self._register_default_ciphers()
         self.activate_osrandom_engine()
+        self._dh_types = [self._lib.EVP_PKEY_DH]
+        if self._lib.Cryptography_HAS_EVP_PKEY_DHX:
+            self._dh_types.append(self._lib.EVP_PKEY_DHX)
 
     def openssl_assert(self, ok):
         return binding._openssl_assert(self._lib, ok)
@@ -480,7 +484,7 @@
             self.openssl_assert(ec_cdata != self._ffi.NULL)
             ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
             return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
-        elif key_type == self._lib.EVP_PKEY_DH:
+        elif key_type in self._dh_types:
             dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey)
             self.openssl_assert(dh_cdata != self._ffi.NULL)
             dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
@@ -512,7 +516,7 @@
             self.openssl_assert(ec_cdata != self._ffi.NULL)
             ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
             return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
-        elif key_type == self._lib.EVP_PKEY_DH:
+        elif key_type in self._dh_types:
             dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey)
             self.openssl_assert(dh_cdata != self._ffi.NULL)
             dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
@@ -1205,6 +1209,23 @@
                 _Reasons.UNSUPPORTED_CIPHER
             )
 
+        elif errors[0][1:] in (
+            (
+                self._lib.ERR_LIB_ASN1,
+                self._lib.ASN1_F_ASN1_CHECK_TLEN,
+                self._lib.ASN1_R_WRONG_TAG
+            ),
+            (
+                self._lib.ERR_LIB_PEM,
+                self._lib.PEM_F_PEM_READ_BIO,
+                self._lib.PEM_R_NO_START_LINE
+            ),
+        ):
+            raise UnsupportedAlgorithm(
+                "Unsupported public key algorithm.",
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+            )
+
         elif any(
             error[1:] == (
                 self._lib.ERR_LIB_EVP,
@@ -1660,9 +1681,7 @@
         return evp_pkey
 
     def generate_dh_private_key(self, parameters):
-        dh_key_cdata = self._lib.DHparams_dup(parameters._dh_cdata)
-        self.openssl_assert(dh_key_cdata != self._ffi.NULL)
-        dh_key_cdata = self._ffi.gc(dh_key_cdata, self._lib.DH_free)
+        dh_key_cdata = _dh_params_dup(parameters._dh_cdata, self)
 
         res = self._lib.DH_generate_key(dh_key_cdata)
         self.openssl_assert(res == 1)
@@ -1684,10 +1703,16 @@
 
         p = self._int_to_bn(parameter_numbers.p)
         g = self._int_to_bn(parameter_numbers.g)
+
+        if parameter_numbers.q is not None:
+            q = self._int_to_bn(parameter_numbers.q)
+        else:
+            q = self._ffi.NULL
+
         pub_key = self._int_to_bn(numbers.public_numbers.y)
         priv_key = self._int_to_bn(numbers.x)
 
-        res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+        res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
         self.openssl_assert(res == 1)
 
         res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key)
@@ -1713,9 +1738,15 @@
 
         p = self._int_to_bn(parameter_numbers.p)
         g = self._int_to_bn(parameter_numbers.g)
+
+        if parameter_numbers.q is not None:
+            q = self._int_to_bn(parameter_numbers.q)
+        else:
+            q = self._ffi.NULL
+
         pub_key = self._int_to_bn(numbers.y)
 
-        res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+        res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
         self.openssl_assert(res == 1)
 
         res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL)
@@ -1733,12 +1764,17 @@
         p = self._int_to_bn(numbers.p)
         g = self._int_to_bn(numbers.g)
 
-        res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+        if numbers.q is not None:
+            q = self._int_to_bn(numbers.q)
+        else:
+            q = self._ffi.NULL
+
+        res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
         self.openssl_assert(res == 1)
 
         return _DHParameters(self, dh_cdata)
 
-    def dh_parameters_supported(self, p, g):
+    def dh_parameters_supported(self, p, g, q=None):
         dh_cdata = self._lib.DH_new()
         self.openssl_assert(dh_cdata != self._ffi.NULL)
         dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
@@ -1746,7 +1782,12 @@
         p = self._int_to_bn(p)
         g = self._int_to_bn(g)
 
-        res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+        if q is not None:
+            q = self._int_to_bn(q)
+        else:
+            q = self._ffi.NULL
+
+        res = self._lib.DH_set0_pqg(dh_cdata, p, q, g)
         self.openssl_assert(res == 1)
 
         codes = self._ffi.new("int[]", 1)
@@ -1755,6 +1796,9 @@
 
         return codes[0] == 0
 
+    def dh_x942_serialization_supported(self):
+        return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1
+
     def x509_name_bytes(self, name):
         x509_name = _encode_name_gc(self, name)
         pp = self._ffi.new("unsigned char **")
diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py
index b594d41..88c876f 100644
--- a/src/cryptography/hazmat/backends/openssl/dh.py
+++ b/src/cryptography/hazmat/backends/openssl/dh.py
@@ -5,18 +5,31 @@
 from __future__ import absolute_import, division, print_function
 
 from cryptography import utils
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric import dh
 
 
-def _dh_cdata_to_parameters(dh_cdata, backend):
+def _dh_params_dup(dh_cdata, backend):
     lib = backend._lib
     ffi = backend._ffi
 
     param_cdata = lib.DHparams_dup(dh_cdata)
     backend.openssl_assert(param_cdata != ffi.NULL)
     param_cdata = ffi.gc(param_cdata, lib.DH_free)
+    if lib.OPENSSL_VERSION_NUMBER < 0x10002000 or lib.CRYPTOGRAPHY_IS_LIBRESSL:
+        # In OpenSSL versions < 1.0.2 or libressl DHparams_dup don't copy q
+        q = ffi.new("BIGNUM **")
+        lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL)
+        q_dup = lib.BN_dup(q[0])
+        res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL)
+        backend.openssl_assert(res == 1)
 
+    return param_cdata
+
+
+def _dh_cdata_to_parameters(dh_cdata, backend):
+    param_cdata = _dh_params_dup(dh_cdata, backend)
     return _DHParameters(backend, param_cdata)
 
 
@@ -29,13 +42,18 @@
     def parameter_numbers(self):
         p = self._backend._ffi.new("BIGNUM **")
         g = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                       p, self._backend._ffi.NULL, g)
+        q = self._backend._ffi.new("BIGNUM **")
+        self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
         self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+        if q[0] == self._backend._ffi.NULL:
+            q_val = None
+        else:
+            q_val = self._backend._bn_to_int(q[0])
         return dh.DHParameterNumbers(
             p=self._backend._bn_to_int(p[0]),
-            g=self._backend._bn_to_int(g[0])
+            g=self._backend._bn_to_int(g[0]),
+            q=q_val
         )
 
     def generate_private_key(self):
@@ -78,10 +96,14 @@
     def private_numbers(self):
         p = self._backend._ffi.new("BIGNUM **")
         g = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                       p, self._backend._ffi.NULL, g)
+        q = self._backend._ffi.new("BIGNUM **")
+        self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
         self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+        if q[0] == self._backend._ffi.NULL:
+            q_val = None
+        else:
+            q_val = self._backend._bn_to_int(q[0])
         pub_key = self._backend._ffi.new("BIGNUM **")
         priv_key = self._backend._ffi.new("BIGNUM **")
         self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key)
@@ -91,7 +113,8 @@
             public_numbers=dh.DHPublicNumbers(
                 parameter_numbers=dh.DHParameterNumbers(
                     p=self._backend._bn_to_int(p[0]),
-                    g=self._backend._bn_to_int(g[0])
+                    g=self._backend._bn_to_int(g[0]),
+                    q=q_val
                 ),
                 y=self._backend._bn_to_int(pub_key[0])
             ),
@@ -126,12 +149,7 @@
             return key
 
     def public_key(self):
-        dh_cdata = self._backend._lib.DHparams_dup(self._dh_cdata)
-        self._backend.openssl_assert(dh_cdata != self._backend._ffi.NULL)
-        dh_cdata = self._backend._ffi.gc(
-            dh_cdata, self._backend._lib.DH_free
-        )
-
+        dh_cdata = _dh_params_dup(self._dh_cdata, self._backend)
         pub_key = self._backend._ffi.new("BIGNUM **")
         self._backend._lib.DH_get0_key(self._dh_cdata,
                                        pub_key, self._backend._ffi.NULL)
@@ -154,6 +172,17 @@
             raise ValueError(
                 "DH private keys support only PKCS8 serialization"
             )
+        if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
+            q = self._backend._ffi.new("BIGNUM **")
+            self._backend._lib.DH_get0_pqg(self._dh_cdata,
+                                           self._backend._ffi.NULL,
+                                           q,
+                                           self._backend._ffi.NULL)
+            if q[0] != self._backend._ffi.NULL:
+                raise UnsupportedAlgorithm(
+                    "DH X9.42 serialization is not supported",
+                    _Reasons.UNSUPPORTED_SERIALIZATION)
+
         return self._backend._private_key_bytes(
             encoding,
             format,
@@ -178,10 +207,14 @@
     def public_numbers(self):
         p = self._backend._ffi.new("BIGNUM **")
         g = self._backend._ffi.new("BIGNUM **")
-        self._backend._lib.DH_get0_pqg(self._dh_cdata,
-                                       p, self._backend._ffi.NULL, g)
+        q = self._backend._ffi.new("BIGNUM **")
+        self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
         self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
         self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+        if q[0] == self._backend._ffi.NULL:
+            q_val = None
+        else:
+            q_val = self._backend._bn_to_int(q[0])
         pub_key = self._backend._ffi.new("BIGNUM **")
         self._backend._lib.DH_get0_key(self._dh_cdata,
                                        pub_key, self._backend._ffi.NULL)
@@ -189,7 +222,8 @@
         return dh.DHPublicNumbers(
             parameter_numbers=dh.DHParameterNumbers(
                 p=self._backend._bn_to_int(p[0]),
-                g=self._backend._bn_to_int(g[0])
+                g=self._backend._bn_to_int(g[0]),
+                q=q_val
             ),
             y=self._backend._bn_to_int(pub_key[0])
         )
@@ -204,6 +238,17 @@
                 "SubjectPublicKeyInfo serialization"
             )
 
+        if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
+            q = self._backend._ffi.new("BIGNUM **")
+            self._backend._lib.DH_get0_pqg(self._dh_cdata,
+                                           self._backend._ffi.NULL,
+                                           q,
+                                           self._backend._ffi.NULL)
+            if q[0] != self._backend._ffi.NULL:
+                raise UnsupportedAlgorithm(
+                    "DH X9.42 serialization is not supported",
+                    _Reasons.UNSUPPORTED_SERIALIZATION)
+
         return self._backend._public_key_bytes(
             encoding,
             format,
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py
index ec044dd..aa60a2d 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dh.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -78,18 +78,21 @@
 
 
 class DHParameterNumbers(object):
-    def __init__(self, p, g):
+    def __init__(self, p, g, q=None):
         if (
             not isinstance(p, six.integer_types) or
             not isinstance(g, six.integer_types)
         ):
             raise TypeError("p and g must be integers")
+        if q is not None and not isinstance(q, six.integer_types):
+            raise TypeError("q must be integer or None")
 
-        if g not in (2, 5):
+        if q is None and g not in (2, 5):
             raise ValueError("DH generator must be 2 or 5")
 
         self._p = p
         self._g = g
+        self._q = q
 
     def __eq__(self, other):
         if not isinstance(other, DHParameterNumbers):
@@ -97,7 +100,8 @@
 
         return (
             self._p == other._p and
-            self._g == other._g
+            self._g == other._g and
+            self._q == other._q
         )
 
     def __ne__(self, other):
@@ -108,6 +112,7 @@
 
     p = utils.read_only_property("_p")
     g = utils.read_only_property("_g")
+    q = utils.read_only_property("_q")
 
 
 @six.add_metaclass(abc.ABCMeta)
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index bd80673..9370387 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -265,7 +265,10 @@
     def generate_dh_private_key_and_parameters(self, generator, key_size):
         pass
 
-    def dh_parameters_supported(self, p, g):
+    def dh_parameters_supported(self, p, g, q=None):
+        pass
+
+    def dh_x942_serialization_supported(self):
         pass
 
 
@@ -638,6 +641,8 @@
             backend.generate_dh_private_key_and_parameters(2, 512)
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN):
             backend.dh_parameters_supported(2, 3)
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_DIFFIE_HELLMAN):
+            backend.dh_x942_serialization_supported()
 
     def test_scrypt(self):
         backend = MultiBackend([DummyScryptBackend()])
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index ff8a42e..f561c79 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -16,13 +16,13 @@
 
 from cryptography import utils, x509
 from cryptography.exceptions import InternalError, _Reasons
-from cryptography.hazmat.backends.interfaces import RSABackend
+from cryptography.hazmat.backends.interfaces import DHBackend, RSABackend
 from cryptography.hazmat.backends.openssl.backend import (
     Backend, backend
 )
 from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve
 from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding
+from cryptography.hazmat.primitives.asymmetric import dh, dsa, ec, padding
 from cryptography.hazmat.primitives.ciphers import Cipher
 from cryptography.hazmat.primitives.ciphers.algorithms import AES
 from cryptography.hazmat.primitives.ciphers.modes import CBC
@@ -32,7 +32,9 @@
     DummyAsymmetricPadding, DummyCipherAlgorithm, DummyHashAlgorithm, DummyMode
 )
 from ...test_x509 import _load_cert
-from ...utils import load_vectors_from_file, raises_unsupported_algorithm
+from ...utils import (
+    load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm
+)
 
 
 def skip_if_libre_ssl(openssl_version):
@@ -611,3 +613,78 @@
             assert cert.subject.get_attributes_for_oid(
                 x509.ObjectIdentifier("1.2.643.3.131.1.1")
             )[0].value == "007710474375"
+
+
+@pytest.mark.skipif(
+    backend._lib.Cryptography_HAS_EVP_PKEY_DHX == 1,
+    reason="Requires OpenSSL without EVP_PKEY_DHX (1.0.2-)")
+@pytest.mark.requires_backend_interface(interface=DHBackend)
+class TestOpenSSLDHSerialization(object):
+
+    @pytest.mark.parametrize(
+        "vector",
+        load_vectors_from_file(
+            os.path.join("asymmetric", "DH", "RFC5114.txt"),
+            load_nist_vectors))
+    def test_dh_serialization_with_q_unsupported(self, backend, vector):
+        parameters = dh.DHParameterNumbers(int(vector["p"], 16),
+                                           int(vector["g"], 16),
+                                           int(vector["q"], 16))
+        public = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters)
+        private = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public)
+        private_key = private.private_key(backend)
+        public_key = private_key.public_key()
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
+            private_key.private_bytes(serialization.Encoding.PEM,
+                                      serialization.PrivateFormat.PKCS8,
+                                      serialization.NoEncryption())
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_SERIALIZATION):
+            public_key.public_bytes(
+                serialization.Encoding.PEM,
+                serialization.PublicFormat.SubjectPublicKeyInfo)
+
+    @pytest.mark.parametrize(
+        ("key_path", "loader_func"),
+        [
+            (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+                serialization.load_pem_private_key,
+            ),
+            (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+                serialization.load_der_private_key,
+            )
+        ]
+    )
+    def test_private_load_dhx_unsupported(self, key_path, loader_func,
+                                          backend):
+        key_bytes = load_vectors_from_file(
+            key_path,
+            lambda pemfile: pemfile.read(), mode="rb"
+        )
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
+            loader_func(key_bytes, None, backend)
+
+    @pytest.mark.parametrize(
+        ("key_path", "loader_func"),
+        [
+            (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+                serialization.load_pem_public_key,
+            ),
+            (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+                serialization.load_der_public_key,
+            )
+        ]
+    )
+    def test_public_load_dhx_unsupported(self, key_path, loader_func,
+                                         backend):
+        key_bytes = load_vectors_from_file(
+            key_path,
+            lambda pemfile: pemfile.read(), mode="rb"
+        )
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
+            loader_func(key_bytes, backend)
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
index 1086630..5b35fe5 100644
--- a/tests/hazmat/primitives/test_dh.py
+++ b/tests/hazmat/primitives/test_dh.py
@@ -18,6 +18,13 @@
 from ...utils import load_nist_vectors, load_vectors_from_file
 
 
+def _skip_dhx_unsupported(backend):
+    if not backend.dh_x942_serialization_supported():
+        pytest.skip(
+            "DH x9.42 serialization is not supported"
+        )
+
+
 def test_dh_parameternumbers():
     params = dh.DHParameterNumbers(
         65537, 2
@@ -46,6 +53,19 @@
             65537, 7
         )
 
+    params = dh.DHParameterNumbers(
+        65537, 7, 1245
+    )
+
+    assert params.p == 65537
+    assert params.g == 7
+    assert params.q == 1245
+
+    with pytest.raises(TypeError):
+        dh.DHParameterNumbers(
+            65537, 2, "hello"
+        )
+
 
 def test_dh_numbers():
     params = dh.DHParameterNumbers(
@@ -89,7 +109,11 @@
 
 def test_dh_parameter_numbers_equality():
     assert dh.DHParameterNumbers(65537, 2) == dh.DHParameterNumbers(65537, 2)
+    assert dh.DHParameterNumbers(65537, 7, 12345) == dh.DHParameterNumbers(
+        65537, 7, 12345)
     assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2)
+    assert dh.DHParameterNumbers(65537, 2, 123) != dh.DHParameterNumbers(
+        65537, 2, 456)
     assert dh.DHParameterNumbers(65537, 5) != dh.DHParameterNumbers(65537, 2)
     assert dh.DHParameterNumbers(65537, 2) != object()
 
@@ -132,15 +156,35 @@
         assert backend.dh_parameters_supported(23, 5)
         assert not backend.dh_parameters_supported(23, 18)
 
-    def test_convert_to_numbers(self, backend):
-        parameters = backend.generate_dh_private_key_and_parameters(2, 512)
+    @pytest.mark.parametrize(
+        "vector",
+        load_vectors_from_file(
+            os.path.join("asymmetric", "DH", "RFC5114.txt"),
+            load_nist_vectors))
+    def test_dh_parameters_supported_with_q(self, backend, vector):
+        assert backend.dh_parameters_supported(int(vector["p"], 16),
+                                               int(vector["g"], 16),
+                                               int(vector["q"], 16))
 
-        private = parameters.private_numbers()
+    @pytest.mark.parametrize("with_q", [False, True])
+    def test_convert_to_numbers(self, backend, with_q):
+        if with_q:
+            vector = load_vectors_from_file(
+                os.path.join("asymmetric", "DH", "RFC5114.txt"),
+                load_nist_vectors)[0]
+            p = int(vector["p"], 16)
+            g = int(vector["g"], 16)
+            q = int(vector["q"], 16)
+        else:
+            parameters = backend.generate_dh_private_key_and_parameters(2, 512)
 
-        p = private.public_numbers.parameter_numbers.p
-        g = private.public_numbers.parameter_numbers.g
+            private = parameters.private_numbers()
 
-        params = dh.DHParameterNumbers(p, g)
+            p = private.public_numbers.parameter_numbers.p
+            g = private.public_numbers.parameter_numbers.g
+            q = None
+
+        params = dh.DHParameterNumbers(p, g, q)
         public = dh.DHPublicNumbers(1, params)
         private = dh.DHPrivateNumbers(2, public)
 
@@ -163,11 +207,22 @@
         with pytest.raises(ValueError):
             private.private_key(backend)
 
-    def test_generate_dh(self, backend):
-        generator = 2
-        key_size = 512
+    @pytest.mark.parametrize("with_q", [False, True])
+    def test_generate_dh(self, backend, with_q):
+        if with_q:
+            vector = load_vectors_from_file(
+                os.path.join("asymmetric", "DH", "RFC5114.txt"),
+                load_nist_vectors)[0]
+            p = int(vector["p"], 16)
+            g = int(vector["g"], 16)
+            q = int(vector["q"], 16)
+            parameters = dh.DHParameterNumbers(p, g, q).parameters(backend)
+            key_size = 1024
+        else:
+            generator = 2
+            key_size = 512
 
-        parameters = dh.generate_parameters(generator, key_size, backend)
+            parameters = dh.generate_parameters(generator, key_size, backend)
         assert isinstance(parameters, dh.DHParameters)
 
         key = parameters.generate_private_key()
@@ -290,6 +345,27 @@
 
         assert int_from_bytes(symkey, 'big') == int(vector["k"], 16)
 
+    @pytest.mark.parametrize(
+        "vector",
+        load_vectors_from_file(
+            os.path.join("asymmetric", "DH", "RFC5114.txt"),
+            load_nist_vectors))
+    def test_dh_vectors_with_q(self, backend, vector):
+        parameters = dh.DHParameterNumbers(int(vector["p"], 16),
+                                           int(vector["g"], 16),
+                                           int(vector["q"], 16))
+        public1 = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters)
+        private1 = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public1)
+        public2 = dh.DHPublicNumbers(int(vector["ystatiut"], 16), parameters)
+        private2 = dh.DHPrivateNumbers(int(vector["xstatiut"], 16), public2)
+        key1 = private1.private_key(backend)
+        key2 = private2.private_key(backend)
+        symkey1 = key1.exchange(public2.public_key(backend))
+        symkey2 = key2.exchange(public1.public_key(backend))
+
+        assert int_from_bytes(symkey1, 'big') == int(vector["z"], 16)
+        assert int_from_bytes(symkey2, 'big') == int(vector["z"], 16)
+
 
 @pytest.mark.requires_backend_interface(interface=DHBackend)
 @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
@@ -332,11 +408,20 @@
                 os.path.join("asymmetric", "DH", "dhkey.der"),
                 serialization.load_der_private_key,
                 serialization.Encoding.DER,
+            ), (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+                serialization.load_pem_private_key,
+                serialization.Encoding.PEM,
+            ), (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+                serialization.load_der_private_key,
+                serialization.Encoding.DER,
             )
         ]
     )
     def test_private_bytes_match(self, key_path, loader_func,
                                  encoding, backend):
+        _skip_dhx_unsupported(backend)
         key_bytes = load_vectors_from_file(
             key_path,
             lambda pemfile: pemfile.read(), mode="rb"
@@ -349,34 +434,48 @@
         assert serialized == key_bytes
 
     @pytest.mark.parametrize(
-        ("key_path", "loader_func"),
+        ("key_path", "loader_func", "vec_path"),
         [
             (
                 os.path.join("asymmetric", "DH", "dhkey.pem"),
                 serialization.load_pem_private_key,
+                os.path.join("asymmetric", "DH", "dhkey.txt")
             ), (
                 os.path.join("asymmetric", "DH", "dhkey.der"),
                 serialization.load_der_private_key,
+                os.path.join("asymmetric", "DH", "dhkey.txt")
+            ), (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"),
+                serialization.load_pem_private_key,
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt")
+            ), (
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"),
+                serialization.load_der_private_key,
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt")
             )
         ]
     )
     def test_private_bytes_values(self, key_path, loader_func,
-                                  backend):
+                                  vec_path, backend):
+        _skip_dhx_unsupported(backend)
         key_bytes = load_vectors_from_file(
             key_path,
             lambda pemfile: pemfile.read(), mode="rb"
         )
-        vec = load_vectors_from_file(
-            os.path.join("asymmetric", "DH", "dhkey.txt"),
-            load_nist_vectors)[0]
+        vec = load_vectors_from_file(vec_path, load_nist_vectors)[0]
         key = loader_func(key_bytes, None, backend)
         private_numbers = key.private_numbers()
         assert private_numbers.x == int(vec["x"], 16)
         assert private_numbers.public_numbers.y == int(vec["y"], 16)
         assert private_numbers.public_numbers.parameter_numbers.g == int(
-            vec["g"])
+            vec["g"], 16)
         assert private_numbers.public_numbers.parameter_numbers.p == int(
             vec["p"], 16)
+        if "q" in vec:
+            assert private_numbers.public_numbers.parameter_numbers.q == int(
+                vec["q"], 16)
+        else:
+            assert private_numbers.public_numbers.parameter_numbers.q is None
 
     def test_private_bytes_traditional_openssl_invalid(self, backend):
         parameters = dh.generate_parameters(2, 512, backend)
@@ -469,11 +568,20 @@
                 os.path.join("asymmetric", "DH", "dhpub.der"),
                 serialization.load_der_public_key,
                 serialization.Encoding.DER,
+            ), (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+                serialization.load_pem_public_key,
+                serialization.Encoding.PEM,
+            ), (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+                serialization.load_der_public_key,
+                serialization.Encoding.DER,
             )
         ]
     )
     def test_public_bytes_match(self, key_path, loader_func,
                                 encoding, backend):
+        _skip_dhx_unsupported(backend)
         key_bytes = load_vectors_from_file(
             key_path,
             lambda pemfile: pemfile.read(), mode="rb"
@@ -486,31 +594,44 @@
         assert serialized == key_bytes
 
     @pytest.mark.parametrize(
-        ("key_path", "loader_func"),
+        ("key_path", "loader_func", "vec_path"),
         [
             (
                 os.path.join("asymmetric", "DH", "dhpub.pem"),
                 serialization.load_pem_public_key,
+                os.path.join("asymmetric", "DH", "dhkey.txt"),
             ), (
                 os.path.join("asymmetric", "DH", "dhpub.der"),
                 serialization.load_der_public_key,
+                os.path.join("asymmetric", "DH", "dhkey.txt"),
+            ), (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"),
+                serialization.load_pem_public_key,
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
+            ), (
+                os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"),
+                serialization.load_der_public_key,
+                os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"),
             )
         ]
     )
     def test_public_bytes_values(self, key_path, loader_func,
-                                 backend):
+                                 vec_path, backend):
+        _skip_dhx_unsupported(backend)
         key_bytes = load_vectors_from_file(
             key_path,
             lambda pemfile: pemfile.read(), mode="rb"
         )
-        vec = load_vectors_from_file(
-            os.path.join("asymmetric", "DH", "dhkey.txt"),
-            load_nist_vectors)[0]
+        vec = load_vectors_from_file(vec_path, load_nist_vectors)[0]
         pub_key = loader_func(key_bytes, backend)
         public_numbers = pub_key.public_numbers()
         assert public_numbers.y == int(vec["y"], 16)
-        assert public_numbers.parameter_numbers.g == int(vec["g"])
+        assert public_numbers.parameter_numbers.g == int(vec["g"], 16)
         assert public_numbers.parameter_numbers.p == int(vec["p"], 16)
+        if "q" in vec:
+            assert public_numbers.parameter_numbers.q == int(vec["q"], 16)
+        else:
+            assert public_numbers.parameter_numbers.q is None
 
     def test_public_bytes_invalid_encoding(self, backend):
         parameters = dh.generate_parameters(2, 512, backend)
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index dad056c..bc16b5f 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -236,10 +236,12 @@
         """).encode()
         bad_der = base64.b64decode(b"".join(key_data.splitlines()))
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(bad_der, None, backend)
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(
                 bad_der, b"this password will not be used", backend
             )
@@ -575,12 +577,14 @@
     def test_wrong_private_format(self, backend):
         key_data = b"---- NOT A KEY ----\n"
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(
                 key_data, None, backend
             )
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(
                 key_data, b"this password will not be used", backend
             )
@@ -588,7 +592,8 @@
     def test_wrong_public_format(self, backend):
         key_data = b"---- NOT A KEY ----\n"
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_public_key(key_data, backend)
 
     def test_corrupt_traditional_format(self, backend):
@@ -720,12 +725,14 @@
 
         password = b"this password is wrong"
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(
                 key_data, None, backend
             )
 
-        with pytest.raises(ValueError):
+        with raises_unsupported_algorithm(
+                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM):
             load_pem_private_key(
                 key_data, password, backend
             )
diff --git a/vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt b/vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt
index bb8e238..08e9c12 100644
--- a/vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt
+++ b/vectors/cryptography_vectors/asymmetric/DH/RFC5114.txt
@@ -1,4 +1,5 @@
 [A.1.  1024-bit MODP Group with 160-bit Prime Order Subgroup]
+COUNT=0
 P = B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371
 Q = F518AA8781A8DF278ABA4E7D64B7CB9D49462353
 G = A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5
@@ -14,6 +15,7 @@
 Result = P (0 - Correct)
 
 [A.2.2048-bitMODPGroupwith224-bitPrimeOrderSubgroup]
+COUNT=1
 P = AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC2129037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708B3BF8A317091883681286130BC8985DB1602E714415D9330278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486DCDF93ACC44328387315D75E198C641A480CD86A1B9E587E8BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71CF9DE5384E71B81C0AC4DFFE0C10E64F
 Q = 801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB
 G = AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFAAB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7C17669101999024AF4D027275AC1348BB8A762D0521BC98AE247150422EA1ED409939D54DA7460CDB5F6C6B250717CBEF180EB34118E98D119529A45D6F834566E3025E316A330EFBB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC017981BC087F2A7065B384B890D3191F2BFA
@@ -29,6 +31,7 @@
 Result = P (0 - Correct)
 
 [A.3.  2048-bit MODP Group with 256-bit Prime Order Subgroup]
+COUNT=2
 P = 87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597
 Q = 8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3
 G = 3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der
new file mode 100644
index 0000000..4db7c95
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der
Binary files differ
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.pem b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.pem
new file mode 100644
index 0000000..3356432
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICXQIBADCCAjYGByqGSM4+AgEwggIpAoIBAQCtEH4ekSOp0NZg+qeVWcUfog1k
+5Wg7n9G1SxWXth0Kdeb6FB35Wlbbr5o8QHuh3xXrPWiKMJwYDh3muFoSdKCmbT+B
+Uq1qwhKQN8nt79pN+Nkej+9VtzlLetW30LbBIgfJ+Y0R7TTb9sa6CyyLvCe+agDg
+oLnElwizv4oxcJGINoEoYTC8iYXbFgLnFEFdkzAngnPH3jHv3HMQ9xIf1aB0FZh9
+mtwKSG3N+TrMRDKDhzFddeGYxkGkgM2Gobnlh+i+YOacySiyucUhcuQTBC6bI/EL
+Dhbnl2PJtT3PS6gKKeP7c8FrjnW5fvNj4v+jH3HPneU4TnG4HArE3/4MEOZPAoIB
+AQCsQDLvTy2a453zC1yP/axQbN6+e4mZjK90hmoIz+T/46aCSk4Quabw3ZIfAacM
+Svqrc513AMKfUsV9sXxiCoZSvl6QAajWatfBdmkQGZkCSvTQJydawTSLuKdi0FIb
+yYriRxUEIuoe1AmTnVTadGDNtfbGslBxfL7xgOs0EY6Y0RlSmkXW+DRWbjAl4xaj
+MO+7d6hvDBqxWwUa49QoyPistwqBNxULjusQ4YPt0Zlj3dniY+R3BYnvaqIef18v
+84G1OczjQJ0TzVZq+7SNbAGRgeG8/pSzAmnt/nL+m2qkvXtaDxxxz/9MGcQY4fbs
+AXmBvAh/KnBls4S4kNMZHyv6Ah0AgBwNNMWNk/6ZcXcQH4BTWkc4zry/OJqZs2Nx
+6wQeAhxEYPzCd3Q1ssq2dHEOtoeo6KO1ZVs6WS2LFf3x
+-----END PRIVATE KEY-----
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.txt b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.txt
new file mode 100644
index 0000000..e199989
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.txt
@@ -0,0 +1,7 @@
+
+COUNT = 0
+P = AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC2129037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708B3BF8A317091883681286130BC8985DB1602E714415D9330278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486DCDF93ACC44328387315D75E198C641A480CD86A1B9E587E8BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71CF9DE5384E71B81C0AC4DFFE0C10E64F
+G = AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFAAB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7C17669101999024AF4D027275AC1348BB8A762D0521BC98AE247150422EA1ED409939D54DA7460CDB5F6C6B250717CBEF180EB34118E98D119529A45D6F834566E3025E316A330EFBB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC017981BC087F2A7065B384B890D3191F2BFA
+Q = 801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB
+Y = 0082c165bb576243ecf46d58c3d1501616955fca0320fa95ea11d2e6c1b9cf217676720dc1c08c85bf20c4d232b60a29a1e51c7b773bc645014587c525c86151b30d75486ec7b6c98efb5f74955b83116d01d0af1232af89213c2de574369d701aba9357300b920d3d8b98252d46c46952c16a5f33554b38317809c7b9add4701f5c158c1b7035e9fe39366ececb90d2896b78c523c4a577287ef5ba7a2663ed58aa20b5ec66e30f316610dfaa38583e495ab6af771c284387e660edbef4edb872e2e80e1d244ee95622e76d028e61c1e887c2aa792717362139f4dd26eafd49b2366eeb2350b01fe1b56022a2809e379559c37b375ba01c4eaacc14fd1b247837
+X = 4460fcc2777435b2cab674710eb687a8e8a3b5655b3a592d8b15fdf1
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.der b/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.der
new file mode 100644
index 0000000..666eb9a
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.der
Binary files differ
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.pem b/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.pem
new file mode 100644
index 0000000..8887cb1
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhp_rfc5114_2.pem
@@ -0,0 +1,14 @@
+-----BEGIN X9.42 DH PARAMETERS-----
+MIICKQKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW
+26+aPEB7od8V6z1oijCcGA4d5rhaEnSgpm0/gVKtasISkDfJ7e/aTfjZHo/vVbc5
+S3rVt9C2wSIHyfmNEe002/bGugssi7wnvmoA4KC5xJcIs7+KMXCRiDaBKGEwvImF
+2xYC5xRBXZMwJ4Jzx94x79xzEPcSH9WgdBWYfZrcCkhtzfk6zEQyg4cxXXXhmMZB
+pIDNhqG55YfovmDmnMkosrnFIXLkEwQumyPxCw4W55djybU9z0uoCinj+3PBa451
+uX7zY+L/ox9xz53lOE5xuBwKxN/+DBDmTwKCAQEArEAy708tmuOd8wtcj/2sUGze
+vnuJmYyvdIZqCM/k/+OmgkpOELmm8N2SHwGnDEr6q3OddwDCn1LFfbF8YgqGUr5e
+kAGo1mrXwXZpEBmZAkr00CcnWsE0i7inYtBSG8mK4kcVBCLqHtQJk51U2nRgzbX2
+xrJQcXy+8YDrNBGOmNEZUppF1vg0Vm4wJeMWozDvu3eobwwasVsFGuPUKMj4rLcK
+gTcVC47rEOGD7dGZY93Z4mPkdwWJ72qiHn9fL/OBtTnM40CdE81Wavu0jWwBkYHh
+vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+gId
+AIAcDTTFjZP+mXF3EB+AU1pHOM68vziambNjces=
+-----END X9.42 DH PARAMETERS-----
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der b/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der
new file mode 100644
index 0000000..ba8aec9
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der
Binary files differ
diff --git a/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.pem b/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.pem
new file mode 100644
index 0000000..94a7170
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.pem
@@ -0,0 +1,20 @@
+-----BEGIN PUBLIC KEY-----
+MIIDRDCCAjYGByqGSM4+AgEwggIpAoIBAQCtEH4ekSOp0NZg+qeVWcUfog1k5Wg7
+n9G1SxWXth0Kdeb6FB35Wlbbr5o8QHuh3xXrPWiKMJwYDh3muFoSdKCmbT+BUq1q
+whKQN8nt79pN+Nkej+9VtzlLetW30LbBIgfJ+Y0R7TTb9sa6CyyLvCe+agDgoLnE
+lwizv4oxcJGINoEoYTC8iYXbFgLnFEFdkzAngnPH3jHv3HMQ9xIf1aB0FZh9mtwK
+SG3N+TrMRDKDhzFddeGYxkGkgM2Gobnlh+i+YOacySiyucUhcuQTBC6bI/ELDhbn
+l2PJtT3PS6gKKeP7c8FrjnW5fvNj4v+jH3HPneU4TnG4HArE3/4MEOZPAoIBAQCs
+QDLvTy2a453zC1yP/axQbN6+e4mZjK90hmoIz+T/46aCSk4Quabw3ZIfAacMSvqr
+c513AMKfUsV9sXxiCoZSvl6QAajWatfBdmkQGZkCSvTQJydawTSLuKdi0FIbyYri
+RxUEIuoe1AmTnVTadGDNtfbGslBxfL7xgOs0EY6Y0RlSmkXW+DRWbjAl4xajMO+7
+d6hvDBqxWwUa49QoyPistwqBNxULjusQ4YPt0Zlj3dniY+R3BYnvaqIef18v84G1
+OczjQJ0TzVZq+7SNbAGRgeG8/pSzAmnt/nL+m2qkvXtaDxxxz/9MGcQY4fbsAXmB
+vAh/KnBls4S4kNMZHyv6Ah0AgBwNNMWNk/6ZcXcQH4BTWkc4zry/OJqZs2Nx6wOC
+AQYAAoIBAQCCwWW7V2JD7PRtWMPRUBYWlV/KAyD6leoR0ubBuc8hdnZyDcHAjIW/
+IMTSMrYKKaHlHHt3O8ZFAUWHxSXIYVGzDXVIbse2yY77X3SVW4MRbQHQrxIyr4kh
+PC3ldDadcBq6k1cwC5INPYuYJS1GxGlSwWpfM1VLODF4Cce5rdRwH1wVjBtwNen+
+OTZuzsuQ0olreMUjxKV3KH71unomY+1YqiC17GbjDzFmEN+qOFg+SVq2r3ccKEOH
+5mDtvvTtuHLi6A4dJE7pViLnbQKOYcHoh8KqeScXNiE59N0m6v1JsjZu6yNQsB/h
+tWAiooCeN5VZw3s3W6AcTqrMFP0bJHg3
+-----END PUBLIC KEY-----