dssenh/tests: Added key exchange tests for the DSSENH cryptographic service provider.
diff --git a/dlls/dssenh/tests/dssenh.c b/dlls/dssenh/tests/dssenh.c
index a0ba903..9d29a0f 100644
--- a/dlls/dssenh/tests/dssenh.c
+++ b/dlls/dssenh/tests/dssenh.c
@@ -992,6 +992,401 @@
     ok(result, "Failed to release CSP provider.\n");
 }
 
+struct keyExchange_test {
+    ALG_ID algid;
+    const BYTE *primeNum;
+    int primeLen;
+    const BYTE *generatorNum;
+    int generatorLen;
+};
+
+/* AT_KEYEXCHANGE interprets to CALG_DH_SF by the DSSENH cryptographic service provider */
+static const BYTE primeAT_KEYEXCHANGE[] = {
+0x65,0x28,0x24,0xd8,0xbe,0x3f,0x95,0x93, 0x3c,0x4d,0x1b,0x51,0xe1,0x89,0x9a,0x90,
+0x5e,0xa0,0x6c,0x25,0xf0,0xb5,0x93,0x98, 0xba,0x76,0x9d,0x54,0x78,0xf6,0xdc,0x04,
+0xe1,0xd0,0x87,0x8f,0xa0,0xe4,0x2f,0xb4, 0x09,0x78,0x24,0xbf,0xc8,0x7f,0x59,0xf4,
+0x07,0xee,0x07,0x20,0x1b,0x2d,0x54,0x2a, 0xb5,0xb4,0x8f,0x8c,0x33,0x73,0xec,0xaf
+};
+
+static const BYTE generatorAT_KEYEXCHANGE[] = {
+0xdc,0x62,0x20,0xe7,0x36,0xa2,0xa6,0xef, 0x77,0x91,0xa8,0xa3,0x6d,0x60,0x70,0x0d,
+0x1d,0x79,0xb1,0xbe,0xa8,0x87,0x69,0x39, 0x29,0xaa,0x54,0x27,0x05,0xe6,0x6f,0xa5,
+0xde,0x82,0x00,0x5d,0x87,0x1f,0x84,0xf7, 0x40,0xec,0x6f,0x15,0x64,0x02,0x0d,0xb8,
+0x50,0x48,0x94,0x66,0xb2,0x7d,0xbd,0xf2, 0x66,0xf8,0x40,0x62,0x94,0xbf,0x24,0x3b
+};
+
+static const BYTE primeCALG_DH_EPHEM[] = {
+0x17,0x99,0xa9,0x79,0x31,0xb9,0x05,0xdd, 0x7f,0xf0,0x02,0x11,0x4d,0x0d,0xc3,0x81,
+0x8b,0x41,0x50,0x41,0x5f,0x07,0xe6,0x8d, 0x02,0xf9,0xaa,0x86,0x2a,0x07,0x07,0xea,
+0x0a,0x75,0xed,0x96,0xa7,0x85,0xee,0xac, 0xb1,0x71,0xbd,0x57,0x48,0xbd,0x41,0x0b,
+0xde,0x34,0xe2,0xba,0x5b,0x55,0x64,0x77, 0x84,0xfa,0x96,0xd1,0xaf,0x79,0x49,0x9d
+};
+
+static const BYTE generatorCALG_DH_EPHEM[] = {
+0xc7,0x64,0x56,0xde,0xf7,0xb4,0xd3,0xd8, 0xa2,0xd4,0x12,0x2d,0x54,0x5c,0x54,0xc8,
+0x04,0x11,0x88,0x14,0x6c,0x9f,0x88,0x41, 0x82,0x93,0x32,0xb1,0x82,0x84,0x5b,0x07,
+0x55,0x13,0x82,0x7a,0x64,0x7b,0x12,0x09, 0xe2,0xa0,0x28,0x51,0xf4,0x7a,0xd9,0x26,
+0x86,0x95,0x5f,0xc0,0x9a,0x25,0xc2,0x7e, 0x91,0x14,0xdd,0x3c,0x4e,0x86,0x4f,0x6f
+};
+
+static const BYTE primeCALG_DH_SF[] = {
+0x85,0xb8,0xa5,0x4a,0xcf,0x2b,0x7c,0x61, 0xb2,0x06,0x93,0x8a,0x87,0x37,0x58,0xb0,
+0x8d,0xc7,0x2a,0xa7,0x7f,0x0d,0x74,0xf9, 0x3a,0x7e,0xbc,0xab,0x3a,0x54,0xe4,0x11,
+0x69,0x6f,0xcd,0xea,0xad,0x32,0x44,0x4f, 0xee,0x54,0x69,0x8c,0x9c,0x3b,0x87,0x27,
+0x36,0x70,0x06,0xf3,0x4e,0xde,0x7f,0x9d, 0xa6,0xf2,0xad,0x43,0x90,0xdd,0xb5,0x9b
+};
+
+static const BYTE generatorCALG_DH_SF[] = {
+0xea,0xdc,0xe0,0xbb,0x60,0x26,0xc6,0xb3, 0x93,0x6f,0x61,0xe6,0x7e,0xe2,0xee,0xd6,
+0xdb,0x3d,0xca,0xa8,0x31,0x46,0x8f,0x5d, 0xb4,0xaa,0x83,0xd3,0x52,0x10,0xcd,0xfb,
+0xfd,0xfc,0x14,0x89,0x0c,0xde,0xcf,0x54, 0x1d,0x05,0x8c,0xbe,0x4a,0xe4,0x37,0xb4,
+0xc0,0x15,0x75,0xc5,0xa2,0xfc,0x99,0xfc, 0xad,0x63,0xcb,0x7c,0xb8,0x20,0xdc,0x2b
+};
+
+static const struct keyExchange_test baseDSSkey_data[] = {
+    /* Cannot exchange keys using the base DSS provider, except on WinNT4 */
+    {AT_KEYEXCHANGE, primeAT_KEYEXCHANGE, sizeof(primeAT_KEYEXCHANGE), generatorAT_KEYEXCHANGE,
+        sizeof(generatorAT_KEYEXCHANGE)},
+    {CALG_DH_EPHEM, primeCALG_DH_EPHEM, sizeof(primeCALG_DH_EPHEM), generatorCALG_DH_EPHEM,
+        sizeof(generatorCALG_DH_EPHEM)},
+    {CALG_DH_SF, primeCALG_DH_SF, sizeof(generatorCALG_DH_SF), generatorCALG_DH_SF,
+        sizeof(generatorCALG_DH_SF)}
+};
+
+static const struct keyExchange_test dssDHkey_data[] = {
+    {AT_KEYEXCHANGE, primeAT_KEYEXCHANGE, sizeof(primeAT_KEYEXCHANGE), generatorAT_KEYEXCHANGE,
+        sizeof(generatorAT_KEYEXCHANGE)},
+    {CALG_DH_EPHEM, primeCALG_DH_EPHEM, sizeof(primeCALG_DH_EPHEM), generatorCALG_DH_EPHEM,
+        sizeof(generatorCALG_DH_EPHEM)},
+    {CALG_DH_SF, primeCALG_DH_SF, sizeof(generatorCALG_DH_SF), generatorCALG_DH_SF,
+        sizeof(generatorCALG_DH_SF)}
+};
+
+static void test_keyExchange_baseDSS(HCRYPTPROV hProv, const struct keyExchange_test *tests, int testLen)
+{
+    HCRYPTKEY privKey1 = 0, privKey2 = 0;
+    HCRYPTKEY sessionKey1 = 0, sessionKey2 = 0;
+    const char plainText[] = "Testing shared key.";
+    unsigned char pbData1[36];
+    unsigned char pbData2[36];
+    BYTE pubKeyBuffer1[512], pubKeyBuffer2[512];
+    DWORD pubKeyLen1, pubKeyLen2, dataLen;
+    DATA_BLOB Prime, Generator;
+    int plainLen = sizeof(plainText), i;
+    ALG_ID algid;
+    BOOL result;
+
+    for(i = 0; i < testLen; i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        /* Create the data blobs and the prime and generator */
+        Prime.cbData = tests[i].primeLen;
+        Prime.pbData = (BYTE *)tests[i].primeNum;
+        Generator.cbData = tests[i].generatorLen;
+        Generator.pbData = (BYTE *)tests[i].generatorNum;
+
+        /* Generate key exchange keys for user1 and user2 */
+        result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1);
+        ok((!result && GetLastError() ==  NTE_BAD_ALGID) || broken(result), /* WinNT4 */
+            "Expected NTE_BAD_ALGID, got %x\n", GetLastError());
+
+        result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey2);
+        ok((!result && GetLastError() ==  NTE_BAD_ALGID) || broken(result), /* WinNT4 */
+            "Expected NTE_BAD_ALGID, got %x\n", GetLastError());
+
+        /* Set the prime and generator values, which are agreed upon */
+        result = CryptSetKeyParam(privKey1, KP_P, (BYTE *)&Prime, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptSetKeyParam(privKey2, KP_P, (BYTE *)&Prime, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptSetKeyParam(privKey1, KP_G, (BYTE *)&Generator, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptSetKeyParam(privKey2, KP_G, (BYTE *)&Generator, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        /* Generate the secret value for user1 and user2 */
+        result = CryptSetKeyParam(privKey1, KP_X, NULL, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptSetKeyParam(privKey2, KP_X, NULL, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        /* Acquire required size for the public keys */
+        result = CryptExportKey(privKey1, 0, PUBLICKEYBLOB, 0, NULL, &pubKeyLen1);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptExportKey(privKey2, 0, PUBLICKEYBLOB, 0, NULL, &pubKeyLen2);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        /* Export public key which will be calculated into the shared key */
+        result = CryptExportKey(privKey1, 0, PUBLICKEYBLOB, 0, pubKeyBuffer1, &pubKeyLen1);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptExportKey(privKey2, 0, PUBLICKEYBLOB, 0, pubKeyBuffer2, &pubKeyLen2);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        /* Import the public key and convert it into a shared key */
+        result = CryptImportKey(hProv, pubKeyBuffer2, pubKeyLen2, privKey1, 0, &sessionKey1);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) ||
+            broken(!result && GetLastError() ==  NTE_BAD_TYPE) || /* Win2K-W2K8, Win7.64 */
+            broken(!result && GetLastError() ==  NTE_BAD_ALGID) || /* W7SP164 (32 bit dssenh) */
+            broken(result), /* WinNT4 */
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError());
+
+        result = CryptImportKey(hProv, pubKeyBuffer1, pubKeyLen1, privKey2, 0, &sessionKey2);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) ||
+            broken(!result && GetLastError() ==  NTE_BAD_TYPE) || /* Win2K-W2K8, Win7.64 */
+            broken(!result && GetLastError() ==  NTE_BAD_ALGID) || /* W7SP164 (32 bit dssenh) */
+            broken(result), /* WinNT4 */
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError());
+
+        /* Set the shared key parameters to matching type */
+        algid = CALG_RC4;
+        result = CryptSetKeyParam(sessionKey1, KP_ALGID, (BYTE *)&algid, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        algid = CALG_RC4;
+        result = CryptSetKeyParam(sessionKey2, KP_ALGID, (BYTE *)&algid, 0);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        /* Encrypt some data and verify we are getting the same output */
+        memcpy(pbData1, plainText, plainLen);
+        dataLen = plainLen;
+
+        result = CryptEncrypt(sessionKey1, 0, TRUE, 0, pbData1, &dataLen, 36);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptDecrypt(sessionKey2, 0, TRUE, 0, pbData1, &dataLen);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        ok(!memcmp(pbData1, (BYTE *)plainText, sizeof(plainText)), "Incorrect decrypted data.\n");
+
+        memcpy(pbData2, plainText, plainLen);
+        dataLen = plainLen;
+
+        result = CryptEncrypt(sessionKey2, 0, TRUE, 0, pbData2, &dataLen, 36);
+       ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptDecrypt(sessionKey1, 0, TRUE, 0, pbData2, &dataLen);
+       ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        ok(!memcmp(pbData1, pbData2, dataLen), "Decrypted data is not identical.\n");
+
+        /* Destory all user keys */
+        result = CryptDestroyKey(sessionKey1);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptDestroyKey(sessionKey2);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptDestroyKey(privKey1);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+
+        result = CryptDestroyKey(privKey2);
+        ok((!result && GetLastError() ==  ERROR_INVALID_PARAMETER) || broken(result),
+            "Expected ERROR_INVALID_PARAMETER, got %x\n", GetLastError()); /* WinNT4 */
+    }
+}
+
+static void test_keyExchange_dssDH(HCRYPTPROV hProv, const struct keyExchange_test *tests, int testLen)
+{
+    HCRYPTKEY privKey1 = 0, privKey2 = 0;
+    HCRYPTKEY sessionKey1 = 0, sessionKey2 = 0;
+    const char plainText[] = "Testing shared key.";
+    unsigned char pbData1[36];
+    unsigned char pbData2[36];
+    BYTE pubKeyBuffer1[512], pubKeyBuffer2[512];
+    DWORD pubKeyLen1, pubKeyLen2, dataLen;
+    DATA_BLOB Prime, Generator;
+    int plainLen = sizeof(plainText), i;
+    ALG_ID algid;
+    BOOL result;
+
+    for(i = 0; i < testLen; i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        /* Create the data blobs and the prime and generator */
+        Prime.cbData = tests[i].primeLen;
+        Prime.pbData = (BYTE *)tests[i].primeNum;
+        Generator.cbData = tests[i].generatorLen;
+        Generator.pbData = (BYTE *)tests[i].generatorNum;
+
+        /* Generate key exchange keys for user1 and user2 */
+        result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1);
+        ok(result, "Failed to generate a key for user1, got %x\n", GetLastError());
+
+        result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey2);
+        ok(result, "Failed to generate a key for user2, got %x\n", GetLastError());
+
+        /* Set the prime and generator values, which are agreed upon */
+        result = CryptSetKeyParam(privKey1, KP_P, (BYTE *)&Prime, 0);
+        ok(result, "Failed to set prime for user 1's key, got %x\n", GetLastError());
+
+        result = CryptSetKeyParam(privKey2, KP_P, (BYTE *)&Prime, 0);
+        ok(result, "Failed to set prime for user 2's key, got %x\n", GetLastError());
+
+        result = CryptSetKeyParam(privKey1, KP_G, (BYTE *)&Generator, 0);
+        ok(result, "Failed to set generator for user 1's key, got %x\n", GetLastError());
+
+        result = CryptSetKeyParam(privKey2, KP_G, (BYTE *)&Generator, 0);
+        ok(result, "Failed to set generator for user 2's key, got %x\n", GetLastError());
+
+        /* Generate the secret value for user1 and user2 */
+        result = CryptSetKeyParam(privKey1, KP_X, NULL, 0);
+        ok(result || broken(!result && GetLastError() == ERROR_INVALID_PARAMETER),
+            "Failed to set secret value for user 1, got %x\n", GetLastError());/* Win2k & WinXP */
+
+        result = CryptSetKeyParam(privKey2, KP_X, NULL, 0);
+        ok(result || broken(!result && GetLastError() == ERROR_INVALID_PARAMETER),
+            "Failed to set secret value for user 2, got %x\n", GetLastError());/* Win2k & WinXP */
+
+        /* Acquire required size for the public keys */
+        result = CryptExportKey(privKey1, 0, PUBLICKEYBLOB, 0, NULL, &pubKeyLen1);
+        ok(result, "Failed to acquire public key length for user 1, got %x\n", GetLastError());
+
+        result = CryptExportKey(privKey2, 0, PUBLICKEYBLOB, 0, NULL, &pubKeyLen2);
+        ok(result, "Failed to acquire public key length for user 2, got %x\n", GetLastError());
+
+        /* Export public key which will be calculated into the shared key */
+        result = CryptExportKey(privKey1, 0, PUBLICKEYBLOB, 0, pubKeyBuffer1, &pubKeyLen1);
+        ok(result, "Failed to export public key for user 1, got %x\n", GetLastError());
+
+        result = CryptExportKey(privKey2, 0, PUBLICKEYBLOB, 0, pubKeyBuffer2, &pubKeyLen2);
+        ok(result, "Failed to export public key for user 2, got %x\n", GetLastError());
+
+        /* Import the public key and convert it into a shared key */
+        result = CryptImportKey(hProv, pubKeyBuffer2, pubKeyLen2, privKey1, 0, &sessionKey1);
+        ok(result, "Failed to import key for user 1, got %x\n", GetLastError());
+
+        result = CryptImportKey(hProv, pubKeyBuffer1, pubKeyLen1, privKey2, 0, &sessionKey2);
+        ok(result, "Failed to import key for user 2, got %x\n", GetLastError());
+
+        /* Set the shared key parameters to matching cipher type */
+        algid = CALG_RC4;
+        result = CryptSetKeyParam(sessionKey1, KP_ALGID, (BYTE *)&algid, 0);
+        ok(result, "Failed to set session key for user 1, got %x\n", GetLastError());
+
+        algid = CALG_RC4;
+        result = CryptSetKeyParam(sessionKey2, KP_ALGID, (BYTE *)&algid, 0);
+        ok(result, "Failed to set session key for user 2, got %x\n", GetLastError());
+
+        /* Encrypt some data and verify we are getting the correct output */
+        memcpy(pbData1, plainText, plainLen);
+        dataLen = plainLen;
+
+        result = CryptEncrypt(sessionKey1, 0, TRUE, 0, pbData1, &dataLen, 36);
+        ok(result, "Failed to encrypt data, got %x.\n", GetLastError());
+
+        result = CryptDecrypt(sessionKey2, 0, TRUE, 0, pbData1, &dataLen);
+        ok(result, "Failed to decrypt data, got %x.\n", GetLastError());
+
+        ok(!memcmp(pbData1, (BYTE *)plainText, sizeof(plainText)), "Incorrect decrypted data.\n");
+
+        memcpy(pbData2, plainText, plainLen);
+        dataLen = plainLen;
+
+        result = CryptEncrypt(sessionKey2, 0, TRUE, 0, pbData2, &dataLen, 36);
+        ok(result, "Failed to encrypt data, got %x.\n", GetLastError());
+
+        result = CryptDecrypt(sessionKey1, 0, TRUE, 0, pbData2, &dataLen);
+        ok(result, "Failed to decrypt data, got %x.\n", GetLastError());
+
+        ok(!memcmp(pbData1, pbData2, dataLen), "Decrypted data is not identical.\n");
+
+        /* Destory all user keys */
+        result = CryptDestroyKey(sessionKey1);
+        ok(result, "Failed to destroy session key 1, got %x\n", GetLastError());
+
+        result = CryptDestroyKey(sessionKey2);
+        ok(result, "Failed to destroy session key 2, got %x\n", GetLastError());
+
+        result = CryptDestroyKey(privKey1);
+        ok(result, "Failed to destroy key private key 1, got %x\n", GetLastError());
+
+        result = CryptDestroyKey(privKey2);
+        ok(result, "Failed to destroy key private key 2, got %x\n", GetLastError());
+    }
+}
+
+static void test_key_exchange(void)
+{
+    HCRYPTPROV hProv = 0;
+    BOOL result;
+
+    /* acquire base dss provider */
+    result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DSS_PROV_A, PROV_DSS, CRYPT_VERIFYCONTEXT);
+    if(!result)
+    {
+        skip("DSSENH is currently not available, skipping shared key tests.\n");
+        return;
+    }
+    ok(result, "Failed to acquire CSP.\n");
+
+    test_keyExchange_baseDSS(hProv, baseDSSkey_data, TESTLEN(baseDSSkey_data));
+
+    result = CryptReleaseContext(hProv, 0);
+    ok(result, "Failed to release CSP provider.\n");
+
+    /* acquire diffie hellman dss provider */
+    result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DSS_DH_PROV, PROV_DSS_DH,
+        CRYPT_VERIFYCONTEXT);
+    ok(result, "Failed to acquire CSP.\n");
+
+    test_keyExchange_dssDH(hProv,  dssDHkey_data, TESTLEN(dssDHkey_data));
+
+    result = CryptReleaseContext(hProv, 0);
+    ok(result, "Failed to release CSP provider.\n");
+
+    /* acquire enhanced dss provider */
+    result = CryptAcquireContextA(&hProv, NULL, MS_ENH_DSS_DH_PROV, PROV_DSS_DH,
+        CRYPT_VERIFYCONTEXT);
+    if(!result && GetLastError() == NTE_KEYSET_NOT_DEF)
+    {
+        win_skip("DSSENH and Schannel provider is broken on WinNT4, skipping shared key tests.\n");
+        return;
+    }
+    ok(result, "Failed to acquire CSP.\n");
+
+    test_keyExchange_dssDH(hProv, dssDHkey_data, TESTLEN(dssDHkey_data));
+
+    result = CryptReleaseContext(hProv, 0);
+    ok(result, "Failed to release CSP provider.\n");
+
+    /* acquire schannel dss provider */
+    result = CryptAcquireContextA(&hProv, NULL, MS_DEF_DH_SCHANNEL_PROV, PROV_DH_SCHANNEL,
+        CRYPT_VERIFYCONTEXT);
+    ok(result, "Failed to acquire CSP.\n");
+
+    test_keyExchange_dssDH(hProv, dssDHkey_data, TESTLEN(dssDHkey_data));
+
+    result = CryptReleaseContext(hProv, 0);
+    ok(result, "Failed to release CSP provider.\n");
+}
+
 START_TEST(dssenh)
 {
     test_acquire_context();
@@ -1000,4 +1395,5 @@
     test_data_encryption(encrypt_data, TESTLEN(encrypt_data));
     test_cipher_modes(ciphermode_data, TESTLEN(ciphermode_data));
     test_verify_signature();
+    test_key_exchange();
 }