sync rsaenh with wine 1.1.34
[reactos.git] / reactos / dll / win32 / rsaenh / rsaenh.c
index fff6474..8234688 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright 2002 TransGaming Technologies (David Hammerton)
  * Copyright 2004 Mike McCormack for CodeWeavers
  * Copyright 2004, 2005 Michael Jung
+ * Copyright 2007 Vijay Kiran Kamuju
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,9 +34,9 @@
 #include "winbase.h"
 #include "winreg.h"
 #include "wincrypt.h"
-#include "objbase.h"
 #include "handle.h"
 #include "implglue.h"
+#include "objbase.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
 
@@ -74,8 +75,7 @@ typedef struct tagCRYPTHASH
 #define RSAENH_MAX_BLOCK_SIZE      24
 #define RSAENH_KEYSTATE_IDLE       0
 #define RSAENH_KEYSTATE_ENCRYPTING 1
-#define RSAENH_KEYSTATE_DECRYPTING 2
-#define RSAENH_KEYSTATE_MASTERKEY  3
+#define RSAENH_KEYSTATE_MASTERKEY  2
 typedef struct _RSAENH_SCHANNEL_INFO 
 {
     SCHANNEL_ALG saEncAlg;
@@ -111,6 +111,7 @@ typedef struct tagCRYPTKEY
 #define RSAENH_PERSONALITY_STRONG      1u
 #define RSAENH_PERSONALITY_ENHANCED    2u
 #define RSAENH_PERSONALITY_SCHANNEL    3u
+#define RSAENH_PERSONALITY_AES         4u
 
 #define RSAENH_MAGIC_CONTAINER         0x26384993u
 typedef struct tagKEYCONTAINER
@@ -153,9 +154,9 @@ typedef struct tagKEYCONTAINER
 /******************************************************************************
  * aProvEnumAlgsEx - Defines the capabilities of the CSP personalities.
  */
-#define RSAENH_MAX_ENUMALGS 20
+#define RSAENH_MAX_ENUMALGS 24
 #define RSAENH_PCT1_SSL2_SSL3_TLS1 (CRYPT_FLAG_PCT1|CRYPT_FLAG_SSL2|CRYPT_FLAG_SSL3|CRYPT_FLAG_TLS1)
-static const PROV_ENUMALGS_EX aProvEnumAlgsEx[4][RSAENH_MAX_ENUMALGS+1] =
+static const PROV_ENUMALGS_EX aProvEnumAlgsEx[5][RSAENH_MAX_ENUMALGS+1] =
 {
  {
   {CALG_RC2,       40, 40,   56,0,                    4,"RC2",     24,"RSA Data Security's RC2"},
@@ -228,6 +229,27 @@ static const PROV_ENUMALGS_EX aProvEnumAlgsEx[4][RSAENH_MAX_ENUMALGS+1] =
   {CALG_SCHANNEL_ENC_KEY,0,0,-1,0,                         12,"SCH ENC KEY",24,"SChannel Encryption Key"},
   {CALG_TLS1PRF,    0,  0,   -1,0,                          9,"TLS1 PRF",   28,"TLS1 Pseudo Random Function"},
   {0,               0,  0,    0,0,                          1,"",            1,""}
+ },
+ {
+  {CALG_RC2,      128, 40,  128,0,                    4,"RC2",     24,"RSA Data Security's RC2"},
+  {CALG_RC4,      128, 40,  128,0,                    4,"RC4",     24,"RSA Data Security's RC4"},
+  {CALG_DES,       56, 56,   56,0,                    4,"DES",     31,"Data Encryption Standard (DES)"},
+  {CALG_3DES_112, 112,112,  112,0,                   13,"3DES TWO KEY",19,"Two Key Triple DES"},
+  {CALG_3DES,     168,168,  168,0,                    5,"3DES",    21,"Three Key Triple DES"},
+  {CALG_AES,      128,128,  128,0,                    4,"AES",     35,"Advanced Encryption Standard (AES)"},
+  {CALG_AES_128,  128,128,  128,0,                    8,"AES-128", 39,"Advanced Encryption Standard (AES-128)"},
+  {CALG_AES_192,  192,192,  192,0,                    8,"AES-192", 39,"Advanced Encryption Standard (AES-192)"},
+  {CALG_AES_256,  256,256,  256,0,                    8,"AES-256", 39,"Advanced Encryption Standard (AES-256)"},
+  {CALG_SHA,      160,160,  160,CRYPT_FLAG_SIGNING,   6,"SHA-1",   30,"Secure Hash Algorithm (SHA-1)"},
+  {CALG_MD2,      128,128,  128,CRYPT_FLAG_SIGNING,   4,"MD2",     23,"Message Digest 2 (MD2)"},
+  {CALG_MD4,      128,128,  128,CRYPT_FLAG_SIGNING,   4,"MD4",     23,"Message Digest 4 (MD4)"},
+  {CALG_MD5,      128,128,  128,CRYPT_FLAG_SIGNING,   4,"MD5",     23,"Message Digest 5 (MD5)"},
+  {CALG_SSL3_SHAMD5,288,288,288,0,                   12,"SSL3 SHAMD5",12,"SSL3 SHAMD5"},
+  {CALG_MAC,        0,  0,    0,0,                    4,"MAC",     28,"Message Authentication Code"},
+  {CALG_RSA_SIGN,1024,384,16384,CRYPT_FLAG_SIGNING|CRYPT_FLAG_IPSEC,9,"RSA_SIGN",14,"RSA Signature"},
+  {CALG_RSA_KEYX,1024,384,16384,CRYPT_FLAG_SIGNING|CRYPT_FLAG_IPSEC,9,"RSA_KEYX",17,"RSA Key Exchange"},
+  {CALG_HMAC,       0,  0,    0,0,                    5,"HMAC",    18,"Hugo's MAC (HMAC)"},
+  {0,               0,  0,    0,0,                    1,"",         1,""}
  }
 };
 
@@ -289,24 +311,23 @@ RSAENH_CPDestroyHash(
     HCRYPTHASH hHash
 );
 
-BOOL WINAPI 
-RSAENH_CPExportKey(
-    HCRYPTPROV hProv, 
-    HCRYPTKEY hKey, 
+static BOOL crypt_export_key(
+    CRYPTKEY *pCryptKey,
     HCRYPTKEY hPubKey, 
     DWORD dwBlobType, 
     DWORD dwFlags, 
+    BOOL force,
     BYTE *pbData, 
     DWORD *pdwDataLen
 );
 
-BOOL WINAPI 
-RSAENH_CPImportKey(
+static BOOL import_key(
     HCRYPTPROV hProv, 
     CONST BYTE *pbData, 
     DWORD dwDataLen, 
     HCRYPTKEY hPubKey, 
     DWORD dwFlags, 
+    BOOL fStoreKey,
     HCRYPTKEY *phKey
 );
 
@@ -322,7 +343,7 @@ RSAENH_CPHashData(
 /******************************************************************************
  * CSP's handle table (used by all acquired key containers)
  */
-static HANDLETABLE handle_table;
+static struct handle_table handle_table;
 
 /******************************************************************************
  * DllMain (RSAENH.@)
@@ -526,7 +547,7 @@ static BOOL copy_hmac_info(PHMAC_INFO *dst, const HMAC_INFO *src) {
     if (!src) return FALSE;
     *dst = HeapAlloc(GetProcessHeap(), 0, sizeof(HMAC_INFO));
     if (!*dst) return FALSE;
-    memcpy(*dst, src, sizeof(HMAC_INFO));
+    **dst = *src;
     (*dst)->pbInnerString = NULL;
     (*dst)->pbOuterString = NULL;
     if ((*dst)->cbInnerString == 0) (*dst)->cbInnerString = RSAENH_HMAC_DEF_PAD_LEN;
@@ -635,7 +656,7 @@ static inline void update_hash(CRYPTHASH *pCryptHash, CONST BYTE *pbData, DWORD
             pbTemp = HeapAlloc(GetProcessHeap(), 0, dwDataLen);
             if (!pbTemp) return;
             memcpy(pbTemp, pbData, dwDataLen);
-            RSAENH_CPEncrypt(pCryptHash->hProv, pCryptHash->hKey, (HCRYPTHASH)NULL, FALSE, 0, 
+            RSAENH_CPEncrypt(pCryptHash->hProv, pCryptHash->hKey, 0, FALSE, 0,
                              pbTemp, &dwDataLen, dwDataLen);
             HeapFree(GetProcessHeap(), 0, pbTemp);
             break;
@@ -679,7 +700,7 @@ static inline void finalize_hash(CRYPTHASH *pCryptHash) {
 
         case CALG_MAC:
             dwDataLen = 0;
-            RSAENH_CPEncrypt(pCryptHash->hProv, pCryptHash->hKey, (HCRYPTHASH)NULL, TRUE, 0, 
+            RSAENH_CPEncrypt(pCryptHash->hProv, pCryptHash->hKey, 0, TRUE, 0,
                              pCryptHash->abHashValue, &dwDataLen, pCryptHash->dwHashSize);
             break;
 
@@ -733,7 +754,8 @@ static inline void setup_key(CRYPTKEY *pCryptKey) {
  *  hProv      [I] Handle to the provider to which the created key will belong.
  *  aiAlgid    [I] The new key shall use the crypto algorithm idenfied by aiAlgid.
  *  dwFlags    [I] Upper 16 bits give the key length.
- *                 Lower 16 bits: CRYPT_CREATE_SALT, CRYPT_NO_SALT
+ *                 Lower 16 bits: CRYPT_EXPORTABLE, CRYPT_CREATE_SALT,
+ *                 CRYPT_NO_SALT
  *  ppCryptKey [O] Pointer to the created key
  *
  * RETURNS
@@ -755,6 +777,8 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
     peaAlgidInfo = get_algid_info(hProv, aiAlgid);
     if (!peaAlgidInfo) return (HCRYPTKEY)INVALID_HANDLE_VALUE;
 
+    TRACE("alg = %s, dwKeyLen = %d\n", debugstr_a(peaAlgidInfo->szName),
+          dwKeyLen);
     /*
      * Assume the default key length, if none is specified explicitly
      */
@@ -800,13 +824,15 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
                 dwKeyLen > peaAlgidInfo->dwMaxLen || 
                 dwKeyLen < peaAlgidInfo->dwMinLen) 
             {
-                SetLastError(NTE_BAD_FLAGS);
+                TRACE("key len %d out of bounds (%d, %d)\n", dwKeyLen,
+                      peaAlgidInfo->dwMinLen, peaAlgidInfo->dwMaxLen);
+                SetLastError(NTE_BAD_DATA);
                 return (HCRYPTKEY)INVALID_HANDLE_VALUE;
             }
     }
 
-    hCryptKey = (HCRYPTKEY)new_object(&handle_table, sizeof(CRYPTKEY), RSAENH_MAGIC_KEY, 
-                                      destroy_key, (OBJECTHDR**)&pCryptKey);
+    hCryptKey = new_object(&handle_table, sizeof(CRYPTKEY), RSAENH_MAGIC_KEY,
+                           destroy_key, (OBJECTHDR**)&pCryptKey);
     if (hCryptKey != (HCRYPTKEY)INVALID_HANDLE_VALUE)
     {
         pCryptKey->aiAlgid = aiAlgid;
@@ -814,6 +840,8 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
         pCryptKey->dwModeBits = 0;
         pCryptKey->dwPermissions = CRYPT_ENCRYPT | CRYPT_DECRYPT | CRYPT_READ | CRYPT_WRITE | 
                                    CRYPT_MAC;
+        if (dwFlags & CRYPT_EXPORTABLE)
+            pCryptKey->dwPermissions |= CRYPT_EXPORT;
         pCryptKey->dwKeyLen = dwKeyLen >> 3;
         pCryptKey->dwEffectiveKeyLen = 0;
         if ((dwFlags & CRYPT_CREATE_SALT) || (dwKeyLen == 40 && !(dwFlags & CRYPT_NO_SALT))) 
@@ -844,6 +872,14 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
                 pCryptKey->dwMode = CRYPT_MODE_CBC;
                 break;
 
+            case CALG_AES:
+            case CALG_AES_128:
+            case CALG_AES_192:
+            case CALG_AES_256:
+                pCryptKey->dwBlockLen = 16;
+                pCryptKey->dwMode = CRYPT_MODE_ECB;
+                break;
+
             case CALG_RSA_KEYX:
             case CALG_RSA_SIGN:
                 pCryptKey->dwBlockLen = dwKeyLen >> 3;
@@ -857,6 +893,37 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
     return hCryptKey;
 }
 
+/******************************************************************************
+ * map_key_spec_to_key_pair_name [Internal]
+ *
+ * Returns the name of the registry value associated with a key spec.
+ *
+ * PARAMS
+ *  dwKeySpec     [I] AT_KEYEXCHANGE or AT_SIGNATURE
+ *
+ * RETURNS
+ *  Success: Name of registry value.
+ *  Failure: NULL
+ */
+static LPCSTR map_key_spec_to_key_pair_name(DWORD dwKeySpec)
+{
+    LPCSTR szValueName;
+
+    switch (dwKeySpec)
+    {
+    case AT_KEYEXCHANGE:
+        szValueName = "KeyExchangeKeyPair";
+        break;
+    case AT_SIGNATURE:
+        szValueName = "SignatureKeyPair";
+        break;
+    default:
+        WARN("invalid key spec %d\n", dwKeySpec);
+        szValueName = NULL;
+    }
+    return szValueName;
+}
+
 /******************************************************************************
  * store_key_pair [Internal]
  *
@@ -865,27 +932,29 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK
  * PARAMS
  *  hCryptKey     [I] Handle to the key to be stored
  *  hKey          [I] Registry key where the key pair is to be stored
- *  szValueName   [I] Registry value where key pair's value is to be stored
+ *  dwKeySpec     [I] AT_KEYEXCHANGE or AT_SIGNATURE
  *  dwFlags       [I] Flags for protecting the key
  */
-static void store_key_pair(HCRYPTKEY hCryptKey, HKEY hKey, LPCSTR szValueName, DWORD dwFlags)
+static void store_key_pair(HCRYPTKEY hCryptKey, HKEY hKey, DWORD dwKeySpec, DWORD dwFlags)
 {
+    LPCSTR szValueName;
     DATA_BLOB blobIn, blobOut;
     CRYPTKEY *pKey;
     DWORD dwLen;
     BYTE *pbKey;
 
+    if (!(szValueName = map_key_spec_to_key_pair_name(dwKeySpec)))
+        return;
     if (lookup_handle(&handle_table, hCryptKey, RSAENH_MAGIC_KEY,
                       (OBJECTHDR**)&pKey))
     {
-        if (RSAENH_CPExportKey(pKey->hProv, hCryptKey, 0, PRIVATEKEYBLOB, 0, 0,
-            &dwLen))
+        if (crypt_export_key(pKey, 0, PRIVATEKEYBLOB, 0, TRUE, 0, &dwLen))
         {
             pbKey = HeapAlloc(GetProcessHeap(), 0, dwLen);
             if (pbKey)
             {
-                if (RSAENH_CPExportKey(pKey->hProv, hCryptKey, 0,
-                                       PRIVATEKEYBLOB, 0, pbKey, &dwLen))
+                if (crypt_export_key(pKey, 0, PRIVATEKEYBLOB, 0, TRUE, pbKey,
+                    &dwLen))
                 {
                     blobIn.pbData = pbKey;
                     blobIn.cbData = dwLen;
@@ -901,10 +970,65 @@ static void store_key_pair(HCRYPTKEY hCryptKey, HKEY hKey, LPCSTR szValueName, D
                 HeapFree(GetProcessHeap(), 0, pbKey);
             }
         }
-        release_handle(&handle_table, hCryptKey, RSAENH_MAGIC_KEY);
     }
 }
 
+/******************************************************************************
+ * map_key_spec_to_permissions_name [Internal]
+ *
+ * Returns the name of the registry value associated with the permissions for
+ * a key spec.
+ *
+ * PARAMS
+ *  dwKeySpec     [I] AT_KEYEXCHANGE or AT_SIGNATURE
+ *
+ * RETURNS
+ *  Success: Name of registry value.
+ *  Failure: NULL
+ */
+static LPCSTR map_key_spec_to_permissions_name(DWORD dwKeySpec)
+{
+    LPCSTR szValueName;
+
+    switch (dwKeySpec)
+    {
+    case AT_KEYEXCHANGE:
+        szValueName = "KeyExchangePermissions";
+        break;
+    case AT_SIGNATURE:
+        szValueName = "SignaturePermissions";
+        break;
+    default:
+        WARN("invalid key spec %d\n", dwKeySpec);
+        szValueName = NULL;
+    }
+    return szValueName;
+}
+
+/******************************************************************************
+ * store_key_permissions [Internal]
+ *
+ * Stores a key's permissions to the registry
+ *
+ * PARAMS
+ *  hCryptKey     [I] Handle to the key whose permissions are to be stored
+ *  hKey          [I] Registry key where the key permissions are to be stored
+ *  dwKeySpec     [I] AT_KEYEXCHANGE or AT_SIGNATURE
+ */
+static void store_key_permissions(HCRYPTKEY hCryptKey, HKEY hKey, DWORD dwKeySpec)
+{
+    LPCSTR szValueName;
+    CRYPTKEY *pKey;
+
+    if (!(szValueName = map_key_spec_to_permissions_name(dwKeySpec)))
+        return;
+    if (lookup_handle(&handle_table, hCryptKey, RSAENH_MAGIC_KEY,
+                      (OBJECTHDR**)&pKey))
+        RegSetValueExA(hKey, szValueName, 0, REG_DWORD,
+                       (BYTE *)&pKey->dwPermissions,
+                       sizeof(pKey->dwPermissions));
+}
+
 /******************************************************************************
  * create_container_key [Internal]
  *
@@ -1021,13 +1145,62 @@ static void store_key_container_keys(KEYCONTAINER *pKeyContainer)
     if (create_container_key(pKeyContainer, KEY_WRITE, &hKey))
     {
         store_key_pair(pKeyContainer->hKeyExchangeKeyPair, hKey,
-                       "KeyExchangeKeyPair", dwFlags);
+                       AT_KEYEXCHANGE, dwFlags);
         store_key_pair(pKeyContainer->hSignatureKeyPair, hKey,
-                       "SignatureKeyPair", dwFlags);
+                       AT_SIGNATURE, dwFlags);
+        RegCloseKey(hKey);
+    }
+}
+
+/******************************************************************************
+ * store_key_container_permissions [Internal]
+ *
+ * Stores key container's key permissions in a persistent location.
+ *
+ * PARAMS
+ *  pKeyContainer [I] Pointer to the key container whose key permissions are to
+ *                    be saved
+ */
+static void store_key_container_permissions(KEYCONTAINER *pKeyContainer)
+{
+    HKEY hKey;
+    DWORD dwFlags;
+
+    /* On WinXP, persistent keys are stored in a file located at:
+     * $AppData$\\Microsoft\\Crypto\\RSA\\$SID$\\some_hex_string
+     */
+
+    if (pKeyContainer->dwFlags & CRYPT_MACHINE_KEYSET)
+        dwFlags = CRYPTPROTECT_LOCAL_MACHINE;
+    else
+        dwFlags = 0;
+
+    if (create_container_key(pKeyContainer, KEY_WRITE, &hKey))
+    {
+        store_key_permissions(pKeyContainer->hKeyExchangeKeyPair, hKey,
+                       AT_KEYEXCHANGE);
+        store_key_permissions(pKeyContainer->hSignatureKeyPair, hKey,
+                       AT_SIGNATURE);
         RegCloseKey(hKey);
     }
 }
 
+/******************************************************************************
+ * release_key_container_keys [Internal]
+ *
+ * Releases key container's keys.
+ *
+ * PARAMS
+ *  pKeyContainer [I] Pointer to the key container whose keys are to be released.
+ */
+static void release_key_container_keys(KEYCONTAINER *pKeyContainer)
+{
+    release_handle(&handle_table, pKeyContainer->hKeyExchangeKeyPair,
+                   RSAENH_MAGIC_KEY);
+    release_handle(&handle_table, pKeyContainer->hSignatureKeyPair,
+                   RSAENH_MAGIC_KEY);
+}
+
 /******************************************************************************
  * destroy_key_container [Internal]
  *
@@ -1041,7 +1214,11 @@ static void destroy_key_container(OBJECTHDR *pObjectHdr)
     KEYCONTAINER *pKeyContainer = (KEYCONTAINER*)pObjectHdr;
 
     if (!(pKeyContainer->dwFlags & CRYPT_VERIFYCONTEXT))
+    {
         store_key_container_keys(pKeyContainer);
+        store_key_container_permissions(pKeyContainer);
+        release_key_container_keys(pKeyContainer);
+    }
     HeapFree( GetProcessHeap(), 0, pKeyContainer );
 }
 
@@ -1064,8 +1241,8 @@ static HCRYPTPROV new_key_container(PCCH pszContainerName, DWORD dwFlags, const
     KEYCONTAINER *pKeyContainer;
     HCRYPTPROV hKeyContainer;
 
-    hKeyContainer = (HCRYPTPROV)new_object(&handle_table, sizeof(KEYCONTAINER), RSAENH_MAGIC_CONTAINER,
-                                           destroy_key_container, (OBJECTHDR**)&pKeyContainer);
+    hKeyContainer = new_object(&handle_table, sizeof(KEYCONTAINER), RSAENH_MAGIC_CONTAINER,
+                               destroy_key_container, (OBJECTHDR**)&pKeyContainer);
     if (hKeyContainer != (HCRYPTPROV)INVALID_HANDLE_VALUE)
     {
         lstrcpynA(pKeyContainer->szName, pszContainerName, MAX_PATH);
@@ -1081,6 +1258,8 @@ static HCRYPTPROV new_key_container(PCCH pszContainerName, DWORD dwFlags, const
                 pKeyContainer->dwPersonality = RSAENH_PERSONALITY_ENHANCED;
             } else if (!strcmp(pVTable->pszProvName, MS_DEF_RSA_SCHANNEL_PROV_A)) { 
                 pKeyContainer->dwPersonality = RSAENH_PERSONALITY_SCHANNEL;
+            } else if (!strcmp(pVTable->pszProvName, MS_ENH_RSA_AES_PROV_A)) {
+                pKeyContainer->dwPersonality = RSAENH_PERSONALITY_AES;
             } else {
                 pKeyContainer->dwPersonality = RSAENH_PERSONALITY_STRONG;
             }
@@ -1107,17 +1286,20 @@ static HCRYPTPROV new_key_container(PCCH pszContainerName, DWORD dwFlags, const
  * PARAMS
  *  hKeyContainer [I] Crypt provider to use to import the key
  *  hKey          [I] Registry key from which to read the key pair
- *  szValueName   [I] Registry value from which to read the key pair's value
+ *  dwKeySpec     [I] AT_KEYEXCHANGE or AT_SIGNATURE
  *  dwFlags       [I] Flags for unprotecting the key
  *  phCryptKey    [O] Returned key
  */
-static BOOL read_key_value(HCRYPTPROV hKeyContainer, HKEY hKey, LPCSTR szValueName, DWORD dwFlags, HCRYPTKEY *phCryptKey)
+static BOOL read_key_value(HCRYPTPROV hKeyContainer, HKEY hKey, DWORD dwKeySpec, DWORD dwFlags, HCRYPTKEY *phCryptKey)
 {
+    LPCSTR szValueName;
     DWORD dwValueType, dwLen;
     BYTE *pbKey;
     DATA_BLOB blobIn, blobOut;
     BOOL ret = FALSE;
 
+    if (!(szValueName = map_key_spec_to_key_pair_name(dwKeySpec)))
+        return FALSE;
     if (RegQueryValueExA(hKey, szValueName, 0, &dwValueType, NULL, &dwLen) ==
         ERROR_SUCCESS)
     {
@@ -1133,14 +1315,29 @@ static BOOL read_key_value(HCRYPTPROV hKeyContainer, HKEY hKey, LPCSTR szValueNa
                 if (CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL,
                     dwFlags, &blobOut))
                 {
-                    ret = RSAENH_CPImportKey(hKeyContainer, blobOut.pbData, blobOut.cbData, 0, 0,
-                                             phCryptKey);
+                    ret = import_key(hKeyContainer, blobOut.pbData, blobOut.cbData, 0, 0,
+                                     FALSE, phCryptKey);
                     LocalFree(blobOut.pbData);
                 }
             }
             HeapFree(GetProcessHeap(), 0, pbKey);
         }
     }
+    if (ret)
+    {
+        CRYPTKEY *pKey;
+
+        if (lookup_handle(&handle_table, *phCryptKey, RSAENH_MAGIC_KEY,
+                          (OBJECTHDR**)&pKey))
+        {
+            if ((szValueName = map_key_spec_to_permissions_name(dwKeySpec)))
+            {
+                dwLen = sizeof(pKey->dwPermissions);
+                RegQueryValueExA(hKey, szValueName, 0, NULL,
+                                 (BYTE *)&pKey->dwPermissions, &dwLen);
+            }
+        }
+    }
     return ret;
 }
 
@@ -1181,10 +1378,10 @@ static HCRYPTPROV read_key_container(PCHAR pszContainerName, DWORD dwFlags, cons
                            (OBJECTHDR**)&pKeyContainer))
             return (HCRYPTPROV)INVALID_HANDLE_VALUE;
     
-        if (read_key_value(hKeyContainer, hKey, "KeyExchangeKeyPair",
+        if (read_key_value(hKeyContainer, hKey, AT_KEYEXCHANGE,
             dwProtectFlags, &hCryptKey))
             pKeyContainer->hKeyExchangeKeyPair = hCryptKey;
-        if (read_key_value(hKeyContainer, hKey, "SignatureKeyPair",
+        if (read_key_value(hKeyContainer, hKey, AT_SIGNATURE,
             dwProtectFlags, &hCryptKey))
             pKeyContainer->hSignatureKeyPair = hCryptKey;
     }
@@ -1227,7 +1424,7 @@ static BOOL build_hash_signature(BYTE *pbSignature, DWORD dwLen, ALG_ID aiAlgid,
                           0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 } },
         { CALG_SHA, 15, { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 
                           0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 } },
-        { 0,        0,  {} }
+        { 0,        0,  { 0 } }
     };
     DWORD dwIdxOID, i, j;
 
@@ -1550,8 +1747,8 @@ BOOL WINAPI RSAENH_CPAcquireContext(HCRYPTPROV *phProv, LPSTR pszContainer,
 
         case CRYPT_VERIFYCONTEXT|CRYPT_NEWKEYSET:
         case CRYPT_VERIFYCONTEXT:
-            if (pszContainer) {
-                TRACE("pszContainer should be NULL\n");
+            if (pszContainer && *pszContainer) {
+                TRACE("pszContainer should be empty\n");
                 SetLastError(NTE_BAD_FLAGS);
                 return FALSE;
             }
@@ -1637,15 +1834,15 @@ BOOL WINAPI RSAENH_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
         }
     }
 
-    *phHash = (HCRYPTHASH)new_object(&handle_table, sizeof(CRYPTHASH), RSAENH_MAGIC_HASH,
-                                     destroy_hash, (OBJECTHDR**)&pCryptHash);
+    *phHash = new_object(&handle_table, sizeof(CRYPTHASH), RSAENH_MAGIC_HASH,
+                         destroy_hash, (OBJECTHDR**)&pCryptHash);
     if (!pCryptHash) return FALSE;
-    
+
     pCryptHash->aiAlgid = Algid;
     pCryptHash->hKey = hKey;
     pCryptHash->hProv = hProv;
     pCryptHash->dwState = RSAENH_HASHSTATE_HASHING;
-    pCryptHash->pHMACInfo = (PHMAC_INFO)NULL;
+    pCryptHash->pHMACInfo = NULL;
     pCryptHash->dwHashSize = peaAlgidInfo->dwDefaultLen >> 3;
     init_data_blob(&pCryptHash->tpPRFParams.blobLabel);
     init_data_blob(&pCryptHash->tpPRFParams.blobSeed);
@@ -1801,11 +1998,11 @@ BOOL WINAPI RSAENH_CPDuplicateHash(HCRYPTPROV hUID, HCRYPTHASH hHash, DWORD *pdw
         return FALSE;
     }
 
-    *phHash = (HCRYPTHASH)new_object(&handle_table, sizeof(CRYPTHASH), RSAENH_MAGIC_HASH, 
-                                     destroy_hash, (OBJECTHDR**)&pDestHash);
+    *phHash = new_object(&handle_table, sizeof(CRYPTHASH), RSAENH_MAGIC_HASH,
+                         destroy_hash, (OBJECTHDR**)&pDestHash);
     if (*phHash != (HCRYPTHASH)INVALID_HANDLE_VALUE)
     {
-        memcpy(pDestHash, pSrcHash, sizeof(CRYPTHASH));
+        *pDestHash = *pSrcHash;
         duplicate_hash_impl(pSrcHash->aiAlgid, &pSrcHash->context, &pDestHash->context);
         copy_hmac_info(&pDestHash->pHMACInfo, pSrcHash->pHMACInfo);
         copy_data_blob(&pDestHash->tpPRFParams.blobLabel, &pSrcHash->tpPRFParams.blobLabel);
@@ -1857,11 +2054,11 @@ BOOL WINAPI RSAENH_CPDuplicateKey(HCRYPTPROV hUID, HCRYPTKEY hKey, DWORD *pdwRes
         return FALSE;
     }
 
-    *phKey = (HCRYPTKEY)new_object(&handle_table, sizeof(CRYPTKEY), RSAENH_MAGIC_KEY, destroy_key, 
-                                   (OBJECTHDR**)&pDestKey);
+    *phKey = new_object(&handle_table, sizeof(CRYPTKEY), RSAENH_MAGIC_KEY, destroy_key,
+                        (OBJECTHDR**)&pDestKey);
     if (*phKey != (HCRYPTKEY)INVALID_HANDLE_VALUE)
     {
-        memcpy(pDestKey, pSrcKey, sizeof(CRYPTKEY));
+        *pDestKey = *pSrcKey;
         copy_data_blob(&pDestKey->siSChannelInfo.blobServerRandom,
                        &pSrcKey->siSChannelInfo.blobServerRandom);
         copy_data_blob(&pDestKey->siSChannelInfo.blobClientRandom, 
@@ -2082,9 +2279,9 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
     }
 
     if (pCryptKey->dwState == RSAENH_KEYSTATE_IDLE) 
-        pCryptKey->dwState = RSAENH_KEYSTATE_DECRYPTING;
+        pCryptKey->dwState = RSAENH_KEYSTATE_ENCRYPTING;
 
-    if (pCryptKey->dwState != RSAENH_KEYSTATE_DECRYPTING)
+    if (pCryptKey->dwState != RSAENH_KEYSTATE_ENCRYPTING)
     {
         SetLastError(NTE_BAD_DATA);
         return FALSE;
@@ -2127,7 +2324,7 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
         if (Final) {
             if (pbData[*pdwDataLen-1] &&
              pbData[*pdwDataLen-1] <= pCryptKey->dwBlockLen &&
-             pbData[*pdwDataLen-1] < *pdwDataLen) {
+             pbData[*pdwDataLen-1] <= *pdwDataLen) {
                 BOOL padOkay = TRUE;
 
                 /* check that every bad byte has the same value */
@@ -2172,17 +2369,163 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
     return TRUE;
 }
 
+static BOOL crypt_export_simple(CRYPTKEY *pCryptKey, CRYPTKEY *pPubKey,
+    DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen)
+{
+    BLOBHEADER *pBlobHeader = (BLOBHEADER*)pbData;
+    ALG_ID *pAlgid = (ALG_ID*)(pBlobHeader+1);
+    DWORD dwDataLen;
+
+    if (!(GET_ALG_CLASS(pCryptKey->aiAlgid)&(ALG_CLASS_DATA_ENCRYPT|ALG_CLASS_MSG_ENCRYPT))) {
+        SetLastError(NTE_BAD_KEY); /* FIXME: error code? */
+        return FALSE;
+    }
+
+    dwDataLen = sizeof(BLOBHEADER) + sizeof(ALG_ID) + pPubKey->dwBlockLen;
+    if (pbData) {
+        if (*pdwDataLen < dwDataLen) {
+            SetLastError(ERROR_MORE_DATA);
+            *pdwDataLen = dwDataLen;
+            return FALSE;
+        }
+
+        pBlobHeader->bType = SIMPLEBLOB;
+        pBlobHeader->bVersion = CUR_BLOB_VERSION;
+        pBlobHeader->reserved = 0;
+        pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
+
+        *pAlgid = pPubKey->aiAlgid;
+
+        if (!pad_data(pCryptKey->abKeyValue, pCryptKey->dwKeyLen, (BYTE*)(pAlgid+1),
+                      pPubKey->dwBlockLen, dwFlags))
+        {
+            return FALSE;
+        }
+
+        encrypt_block_impl(pPubKey->aiAlgid, PK_PUBLIC, &pPubKey->context, (BYTE*)(pAlgid+1),
+                           (BYTE*)(pAlgid+1), RSAENH_ENCRYPT);
+    }
+    *pdwDataLen = dwDataLen;
+    return TRUE;
+}
+
+static BOOL crypt_export_public_key(CRYPTKEY *pCryptKey, BYTE *pbData,
+    DWORD *pdwDataLen)
+{
+    BLOBHEADER *pBlobHeader = (BLOBHEADER*)pbData;
+    RSAPUBKEY *pRSAPubKey = (RSAPUBKEY*)(pBlobHeader+1);
+    DWORD dwDataLen;
+
+    if ((pCryptKey->aiAlgid != CALG_RSA_KEYX) && (pCryptKey->aiAlgid != CALG_RSA_SIGN)) {
+        SetLastError(NTE_BAD_KEY);
+        return FALSE;
+    }
+
+    dwDataLen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + pCryptKey->dwKeyLen;
+    if (pbData) {
+        if (*pdwDataLen < dwDataLen) {
+            SetLastError(ERROR_MORE_DATA);
+            *pdwDataLen = dwDataLen;
+            return FALSE;
+        }
+
+        pBlobHeader->bType = PUBLICKEYBLOB;
+        pBlobHeader->bVersion = CUR_BLOB_VERSION;
+        pBlobHeader->reserved = 0;
+        pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
+
+        pRSAPubKey->magic = RSAENH_MAGIC_RSA1;
+        pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
+
+        export_public_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context,
+                               pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
+    }
+    *pdwDataLen = dwDataLen;
+    return TRUE;
+}
+
+static BOOL crypt_export_private_key(CRYPTKEY *pCryptKey, BOOL force,
+    BYTE *pbData, DWORD *pdwDataLen)
+{
+    BLOBHEADER *pBlobHeader = (BLOBHEADER*)pbData;
+    RSAPUBKEY *pRSAPubKey = (RSAPUBKEY*)(pBlobHeader+1);
+    DWORD dwDataLen;
+
+    if ((pCryptKey->aiAlgid != CALG_RSA_KEYX) && (pCryptKey->aiAlgid != CALG_RSA_SIGN)) {
+        SetLastError(NTE_BAD_KEY);
+        return FALSE;
+    }
+    if (!force && !(pCryptKey->dwPermissions & CRYPT_EXPORT))
+    {
+        SetLastError(NTE_BAD_KEY_STATE);
+        return FALSE;
+    }
+
+    dwDataLen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+                2 * pCryptKey->dwKeyLen + 5 * ((pCryptKey->dwKeyLen + 1) >> 1);
+    if (pbData) {
+        if (*pdwDataLen < dwDataLen) {
+            SetLastError(ERROR_MORE_DATA);
+            *pdwDataLen = dwDataLen;
+            return FALSE;
+        }
+
+        pBlobHeader->bType = PRIVATEKEYBLOB;
+        pBlobHeader->bVersion = CUR_BLOB_VERSION;
+        pBlobHeader->reserved = 0;
+        pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
+
+        pRSAPubKey->magic = RSAENH_MAGIC_RSA2;
+        pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
+
+        export_private_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context,
+                                pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
+    }
+    *pdwDataLen = dwDataLen;
+    return TRUE;
+}
+
+static BOOL crypt_export_plaintext_key(CRYPTKEY *pCryptKey, BYTE *pbData,
+    DWORD *pdwDataLen)
+{
+    BLOBHEADER *pBlobHeader = (BLOBHEADER*)pbData;
+    DWORD *pKeyLen = (DWORD*)(pBlobHeader+1);
+    BYTE *pbKey = (BYTE*)(pKeyLen+1);
+    DWORD dwDataLen;
+
+    dwDataLen = sizeof(BLOBHEADER) + sizeof(DWORD) + pCryptKey->dwKeyLen;
+    if (pbData) {
+        if (*pdwDataLen < dwDataLen) {
+            SetLastError(ERROR_MORE_DATA);
+            *pdwDataLen = dwDataLen;
+            return FALSE;
+        }
+
+        pBlobHeader->bType = PLAINTEXTKEYBLOB;
+        pBlobHeader->bVersion = CUR_BLOB_VERSION;
+        pBlobHeader->reserved = 0;
+        pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
+
+        *pKeyLen = pCryptKey->dwKeyLen;
+        memcpy(pbKey, &pCryptKey->abKeyValue, pCryptKey->dwKeyLen);
+    }
+    *pdwDataLen = dwDataLen;
+    return TRUE;
+}
 /******************************************************************************
- * CPExportKey (RSAENH.@)
+ * crypt_export_key [Internal]
  *
- * Export a key into a binary large object (BLOB).
+ * Export a key into a binary large object (BLOB).  Called by CPExportKey and
+ * by store_key_pair.
  *
  * PARAMS
- *  hProv      [I]   Key container from which a key is to be exported.
- *  hKey       [I]   Key to be exported.
+ *  pCryptKey  [I]   Key to be exported.
  *  hPubKey    [I]   Key used to encrypt sensitive BLOB data.
  *  dwBlobType [I]   SIMPLEBLOB, PUBLICKEYBLOB or PRIVATEKEYBLOB.
  *  dwFlags    [I]   Currently none defined.
+ *  force      [I]   If TRUE, the key is written no matter what the key's
+ *                   permissions are.  Otherwise the key's permissions are
+ *                   checked before exporting.
  *  pbData     [O]   Pointer to a buffer where the BLOB will be written to.
  *  pdwDataLen [I/O] I: Size of buffer at pbData, O: Size of BLOB
  *
@@ -2190,30 +2533,12 @@ BOOL WINAPI RSAENH_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
  *  Success: TRUE.
  *  Failure: FALSE.
  */
-BOOL WINAPI RSAENH_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubKey, 
-                               DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen)
+static BOOL crypt_export_key(CRYPTKEY *pCryptKey, HCRYPTKEY hPubKey,
+                             DWORD dwBlobType, DWORD dwFlags, BOOL force,
+                             BYTE *pbData, DWORD *pdwDataLen)
 {
-    CRYPTKEY *pCryptKey, *pPubKey;
-    BLOBHEADER *pBlobHeader = (BLOBHEADER*)pbData;
-    RSAPUBKEY *pRSAPubKey = (RSAPUBKEY*)(pBlobHeader+1);
-    ALG_ID *pAlgid = (ALG_ID*)(pBlobHeader+1);
-    DWORD dwDataLen;
-    
-    TRACE("(hProv=%08lx, hKey=%08lx, hPubKey=%08lx, dwBlobType=%08x, dwFlags=%08x, pbData=%p,"
-          "pdwDataLen=%p)\n", hProv, hKey, hPubKey, dwBlobType, dwFlags, pbData, pdwDataLen);
+    CRYPTKEY *pPubKey;
     
-    if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
-    {
-        SetLastError(NTE_BAD_UID);
-        return FALSE;
-    }
-
-    if (!lookup_handle(&handle_table, hKey, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pCryptKey))
-    {
-        SetLastError(NTE_BAD_KEY);
-        return FALSE;
-    }
-
     if (dwFlags & CRYPT_SSL2_FALLBACK) {
         if (pCryptKey->aiAlgid != CALG_SSL2_MASTER) {
             SetLastError(NTE_BAD_KEY);
@@ -2228,38 +2553,8 @@ BOOL WINAPI RSAENH_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubK
                 SetLastError(NTE_BAD_PUBLIC_KEY); /* FIXME: error_code? */
                 return FALSE;
             }
-
-            if (!(GET_ALG_CLASS(pCryptKey->aiAlgid)&(ALG_CLASS_DATA_ENCRYPT|ALG_CLASS_MSG_ENCRYPT))) {
-                SetLastError(NTE_BAD_KEY); /* FIXME: error code? */
-                return FALSE;
-            }
-
-            dwDataLen = sizeof(BLOBHEADER) + sizeof(ALG_ID) + pPubKey->dwBlockLen;
-            if (pbData) {
-                if (*pdwDataLen < dwDataLen) {
-                    SetLastError(ERROR_MORE_DATA);
-                    *pdwDataLen = dwDataLen;
-                    return FALSE;
-                }
-
-                pBlobHeader->bType = SIMPLEBLOB;
-                pBlobHeader->bVersion = CUR_BLOB_VERSION;
-                pBlobHeader->reserved = 0;
-                pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
-
-                *pAlgid = pPubKey->aiAlgid;
-       
-                if (!pad_data(pCryptKey->abKeyValue, pCryptKey->dwKeyLen, (BYTE*)(pAlgid+1), 
-                              pPubKey->dwBlockLen, dwFlags))
-                {
-                    return FALSE;
-                }
-                
-                encrypt_block_impl(pPubKey->aiAlgid, PK_PUBLIC, &pPubKey->context, (BYTE*)(pAlgid+1), 
-                                   (BYTE*)(pAlgid+1), RSAENH_ENCRYPT); 
-            }
-            *pdwDataLen = dwDataLen;
-            return TRUE;
+            return crypt_export_simple(pCryptKey, pPubKey, dwFlags, pbData,
+                                       pdwDataLen);
             
         case PUBLICKEYBLOB:
             if (is_valid_handle(&handle_table, hPubKey, RSAENH_MAGIC_KEY)) {
@@ -2267,61 +2562,13 @@ BOOL WINAPI RSAENH_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubK
                 return FALSE;
             }
 
-            if ((pCryptKey->aiAlgid != CALG_RSA_KEYX) && (pCryptKey->aiAlgid != CALG_RSA_SIGN)) {
-                SetLastError(NTE_BAD_KEY);
-                return FALSE;
-            }
-
-            dwDataLen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + pCryptKey->dwKeyLen;
-            if (pbData) {
-                if (*pdwDataLen < dwDataLen) {
-                    SetLastError(ERROR_MORE_DATA);
-                    *pdwDataLen = dwDataLen;
-                    return FALSE;
-                }
-
-                pBlobHeader->bType = PUBLICKEYBLOB;
-                pBlobHeader->bVersion = CUR_BLOB_VERSION;
-                pBlobHeader->reserved = 0;
-                pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
-
-                pRSAPubKey->magic = RSAENH_MAGIC_RSA1; 
-                pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
-        
-                export_public_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context, 
-                                       pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
-            }
-            *pdwDataLen = dwDataLen;
-            return TRUE;
+            return crypt_export_public_key(pCryptKey, pbData, pdwDataLen);
 
         case PRIVATEKEYBLOB:
-            if ((pCryptKey->aiAlgid != CALG_RSA_KEYX) && (pCryptKey->aiAlgid != CALG_RSA_SIGN)) {
-                SetLastError(NTE_BAD_KEY);
-                return FALSE;
-            }
-    
-            dwDataLen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 
-                        2 * pCryptKey->dwKeyLen + 5 * ((pCryptKey->dwKeyLen + 1) >> 1);
-            if (pbData) {
-                if (*pdwDataLen < dwDataLen) {
-                    SetLastError(ERROR_MORE_DATA);
-                    *pdwDataLen = dwDataLen;
-                    return FALSE;
-                }
-                
-                pBlobHeader->bType = PRIVATEKEYBLOB;
-                pBlobHeader->bVersion = CUR_BLOB_VERSION;
-                pBlobHeader->reserved = 0;
-                pBlobHeader->aiKeyAlg = pCryptKey->aiAlgid;
+            return crypt_export_private_key(pCryptKey, force, pbData, pdwDataLen);
 
-                pRSAPubKey->magic = RSAENH_MAGIC_RSA2;
-                pRSAPubKey->bitlen = pCryptKey->dwKeyLen << 3;
-                
-                export_private_key_impl((BYTE*)(pRSAPubKey+1), &pCryptKey->context, 
-                                        pCryptKey->dwKeyLen, &pRSAPubKey->pubexp);
-            }
-            *pdwDataLen = dwDataLen;
-            return TRUE;
+        case PLAINTEXTKEYBLOB:
+            return crypt_export_plaintext_key(pCryptKey, pbData, pdwDataLen);
             
         default:
             SetLastError(NTE_BAD_TYPE); /* FIXME: error code? */
@@ -2330,39 +2577,370 @@ BOOL WINAPI RSAENH_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubK
 }
 
 /******************************************************************************
- * CPImportKey (RSAENH.@)
+ * CPExportKey (RSAENH.@)
  *
- * Import a BLOB'ed key into a key container.
+ * Export a key into a binary large object (BLOB).
+ *
+ * PARAMS
+ *  hProv      [I]   Key container from which a key is to be exported.
+ *  hKey       [I]   Key to be exported.
+ *  hPubKey    [I]   Key used to encrypt sensitive BLOB data.
+ *  dwBlobType [I]   SIMPLEBLOB, PUBLICKEYBLOB or PRIVATEKEYBLOB.
+ *  dwFlags    [I]   Currently none defined.
+ *  pbData     [O]   Pointer to a buffer where the BLOB will be written to.
+ *  pdwDataLen [I/O] I: Size of buffer at pbData, O: Size of BLOB
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+BOOL WINAPI RSAENH_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubKey,
+                               DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen)
+{
+    CRYPTKEY *pCryptKey;
+
+    TRACE("(hProv=%08lx, hKey=%08lx, hPubKey=%08lx, dwBlobType=%08x, dwFlags=%08x, pbData=%p,"
+          "pdwDataLen=%p)\n", hProv, hKey, hPubKey, dwBlobType, dwFlags, pbData, pdwDataLen);
+
+    if (!is_valid_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER))
+    {
+        SetLastError(NTE_BAD_UID);
+        return FALSE;
+    }
+
+    if (!lookup_handle(&handle_table, hKey, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pCryptKey))
+    {
+        SetLastError(NTE_BAD_KEY);
+        return FALSE;
+    }
+
+    return crypt_export_key(pCryptKey, hPubKey, dwBlobType, dwFlags, FALSE,
+        pbData, pdwDataLen);
+}
+
+/******************************************************************************
+ * release_and_install_key [Internal]
+ *
+ * Release an existing key, if present, and replaces it with a new one.
  *
  * PARAMS
  *  hProv     [I] Key container into which the key is to be imported.
- *  pbData    [I] Pointer to a buffer which holds the BLOB.
+ *  src       [I] Key which will replace *dest
+ *  dest      [I] Points to key to be released and replaced with src
+ *  fStoreKey [I] If TRUE, the newly installed key is stored to the registry.
+ */
+static void release_and_install_key(HCRYPTPROV hProv, HCRYPTKEY src,
+                                    HCRYPTKEY *dest, DWORD fStoreKey)
+{
+    RSAENH_CPDestroyKey(hProv, *dest);
+    copy_handle(&handle_table, src, RSAENH_MAGIC_KEY, dest);
+    if (fStoreKey)
+    {
+        KEYCONTAINER *pKeyContainer;
+
+        if (lookup_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER,
+                          (OBJECTHDR**)&pKeyContainer))
+        {
+            store_key_container_keys(pKeyContainer);
+            store_key_container_permissions(pKeyContainer);
+        }
+    }
+}
+
+/******************************************************************************
+ * import_private_key [Internal]
+ *
+ * Import a BLOB'ed private key into a key container.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the private key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the private key BLOB.
  *  dwDataLen [I] Length of data in buffer at pbData.
- *  hPubKey   [I] Key used to decrypt sensitive BLOB data.
- *  dwFlags   [I] Currently none defined.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  fStoreKey [I] If TRUE, the imported key is stored to the registry.
  *  phKey     [O] Handle to the imported key.
  *
+ *
+ * NOTES
+ *  Assumes the caller has already checked the BLOBHEADER at pbData to ensure
+ *  it's a PRIVATEKEYBLOB.
+ *
  * RETURNS
  *  Success: TRUE.
  *  Failure: FALSE.
  */
-BOOL WINAPI RSAENH_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen, 
-                               HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey)
+static BOOL import_private_key(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
+                               DWORD dwFlags, BOOL fStoreKey, HCRYPTKEY *phKey)
 {
     KEYCONTAINER *pKeyContainer;
-    CRYPTKEY *pCryptKey, *pPubKey;
+    CRYPTKEY *pCryptKey;
     CONST BLOBHEADER *pBlobHeader = (CONST BLOBHEADER*)pbData;
     CONST RSAPUBKEY *pRSAPubKey = (CONST RSAPUBKEY*)(pBlobHeader+1);
+    BOOL ret;
+
+    if (!lookup_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER,
+                       (OBJECTHDR**)&pKeyContainer))
+    {
+        SetLastError(NTE_BAD_UID);
+        return FALSE;
+    }
+
+    if ((dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY)) ||
+        (pRSAPubKey->magic != RSAENH_MAGIC_RSA2) ||
+        (dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+            (2 * pRSAPubKey->bitlen >> 3) + (5 * ((pRSAPubKey->bitlen+8)>>4))))
+    {
+        SetLastError(NTE_BAD_DATA);
+        return FALSE;
+    }
+
+    *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey);
+    if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
+    setup_key(pCryptKey);
+    ret = import_private_key_impl((CONST BYTE*)(pRSAPubKey+1), &pCryptKey->context,
+                                   pRSAPubKey->bitlen/8, pRSAPubKey->pubexp);
+    if (ret) {
+        if (dwFlags & CRYPT_EXPORTABLE)
+            pCryptKey->dwPermissions |= CRYPT_EXPORT;
+        switch (pBlobHeader->aiKeyAlg)
+        {
+        case AT_SIGNATURE:
+        case CALG_RSA_SIGN:
+            TRACE("installing signing key\n");
+            release_and_install_key(hProv, *phKey, &pKeyContainer->hSignatureKeyPair,
+                                    fStoreKey);
+            break;
+        case AT_KEYEXCHANGE:
+        case CALG_RSA_KEYX:
+            TRACE("installing key exchange key\n");
+            release_and_install_key(hProv, *phKey, &pKeyContainer->hKeyExchangeKeyPair,
+                                    fStoreKey);
+            break;
+        }
+    }
+    return ret;
+}
+
+/******************************************************************************
+ * import_public_key [Internal]
+ *
+ * Import a BLOB'ed public key into a key container.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the public key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the public key BLOB.
+ *  dwDataLen [I] Length of data in buffer at pbData.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  fStoreKey [I] If TRUE, the imported key is stored to the registry.
+ *  phKey     [O] Handle to the imported key.
+ *
+ *
+ * NOTES
+ *  Assumes the caller has already checked the BLOBHEADER at pbData to ensure
+ *  it's a PUBLICKEYBLOB.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+static BOOL import_public_key(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
+                              DWORD dwFlags, BOOL fStoreKey, HCRYPTKEY *phKey)
+{
+    KEYCONTAINER *pKeyContainer;
+    CRYPTKEY *pCryptKey;
+    CONST BLOBHEADER *pBlobHeader = (CONST BLOBHEADER*)pbData;
+    CONST RSAPUBKEY *pRSAPubKey = (CONST RSAPUBKEY*)(pBlobHeader+1);
+    ALG_ID algID;
+    BOOL ret;
+
+    if (!lookup_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER,
+                       (OBJECTHDR**)&pKeyContainer))
+    {
+        SetLastError(NTE_BAD_UID);
+        return FALSE;
+    }
+
+    if ((dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY)) ||
+        (pRSAPubKey->magic != RSAENH_MAGIC_RSA1) ||
+        (dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + (pRSAPubKey->bitlen >> 3)))
+    {
+        SetLastError(NTE_BAD_DATA);
+        return FALSE;
+    }
+
+    /* Since this is a public key blob, only the public key is
+     * available, so only signature verification is possible.
+     */
+    algID = pBlobHeader->aiKeyAlg;
+    *phKey = new_key(hProv, algID, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey);
+    if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
+    setup_key(pCryptKey);
+    ret = import_public_key_impl((CONST BYTE*)(pRSAPubKey+1), &pCryptKey->context,
+                                  pRSAPubKey->bitlen >> 3, pRSAPubKey->pubexp);
+    if (ret) {
+        if (dwFlags & CRYPT_EXPORTABLE)
+            pCryptKey->dwPermissions |= CRYPT_EXPORT;
+        switch (pBlobHeader->aiKeyAlg)
+        {
+        case AT_KEYEXCHANGE:
+        case CALG_RSA_KEYX:
+            TRACE("installing public key\n");
+            release_and_install_key(hProv, *phKey, &pKeyContainer->hKeyExchangeKeyPair,
+                                    fStoreKey);
+            break;
+        }
+    }
+    return ret;
+}
+
+/******************************************************************************
+ * import_symmetric_key [Internal]
+ *
+ * Import a BLOB'ed symmetric key into a key container.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the symmetric key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the symmetric key BLOB.
+ *  dwDataLen [I] Length of data in buffer at pbData.
+ *  hPubKey   [I] Key used to decrypt sensitive BLOB data.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  phKey     [O] Handle to the imported key.
+ *
+ *
+ * NOTES
+ *  Assumes the caller has already checked the BLOBHEADER at pbData to ensure
+ *  it's a SIMPLEBLOB.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+static BOOL import_symmetric_key(HCRYPTPROV hProv, CONST BYTE *pbData,
+                                 DWORD dwDataLen, HCRYPTKEY hPubKey,
+                                 DWORD dwFlags, HCRYPTKEY *phKey)
+{
+    CRYPTKEY *pCryptKey, *pPubKey;
+    CONST BLOBHEADER *pBlobHeader = (CONST BLOBHEADER*)pbData;
     CONST ALG_ID *pAlgid = (CONST ALG_ID*)(pBlobHeader+1);
     CONST BYTE *pbKeyStream = (CONST BYTE*)(pAlgid + 1);
-    ALG_ID algID;
     BYTE *pbDecrypted;
     DWORD dwKeyLen;
-    BOOL ret;
 
-    TRACE("(hProv=%08lx, pbData=%p, dwDataLen=%d, hPubKey=%08lx, dwFlags=%08x, phKey=%p)\n",
-        hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);
-    
+    if (!lookup_handle(&handle_table, hPubKey, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pPubKey) ||
+        pPubKey->aiAlgid != CALG_RSA_KEYX)
+    {
+        SetLastError(NTE_BAD_PUBLIC_KEY); /* FIXME: error code? */
+        return FALSE;
+    }
+
+    if (dwDataLen < sizeof(BLOBHEADER)+sizeof(ALG_ID)+pPubKey->dwBlockLen)
+    {
+        SetLastError(NTE_BAD_DATA); /* FIXME: error code */
+        return FALSE;
+    }
+
+    pbDecrypted = HeapAlloc(GetProcessHeap(), 0, pPubKey->dwBlockLen);
+    if (!pbDecrypted) return FALSE;
+    encrypt_block_impl(pPubKey->aiAlgid, PK_PRIVATE, &pPubKey->context, pbKeyStream, pbDecrypted,
+                       RSAENH_DECRYPT);
+
+    dwKeyLen = RSAENH_MAX_KEY_SIZE;
+    if (!unpad_data(pbDecrypted, pPubKey->dwBlockLen, pbDecrypted, &dwKeyLen, dwFlags)) {
+        HeapFree(GetProcessHeap(), 0, pbDecrypted);
+        return FALSE;
+    }
+
+    *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, dwKeyLen<<19, &pCryptKey);
+    if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE)
+    {
+        HeapFree(GetProcessHeap(), 0, pbDecrypted);
+        return FALSE;
+    }
+    memcpy(pCryptKey->abKeyValue, pbDecrypted, dwKeyLen);
+    HeapFree(GetProcessHeap(), 0, pbDecrypted);
+    setup_key(pCryptKey);
+    if (dwFlags & CRYPT_EXPORTABLE)
+        pCryptKey->dwPermissions |= CRYPT_EXPORT;
+    return TRUE;
+}
+
+/******************************************************************************
+ * import_plaintext_key [Internal]
+ *
+ * Import a plaintext key into a key container.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the symmetric key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the plaintext key BLOB.
+ *  dwDataLen [I] Length of data in buffer at pbData.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  phKey     [O] Handle to the imported key.
+ *
+ *
+ * NOTES
+ *  Assumes the caller has already checked the BLOBHEADER at pbData to ensure
+ *  it's a PLAINTEXTKEYBLOB.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+static BOOL import_plaintext_key(HCRYPTPROV hProv, CONST BYTE *pbData,
+                                 DWORD dwDataLen, DWORD dwFlags,
+                                 HCRYPTKEY *phKey)
+{
+    CRYPTKEY *pCryptKey;
+    CONST BLOBHEADER *pBlobHeader = (CONST BLOBHEADER*)pbData;
+    CONST DWORD *pKeyLen = (CONST DWORD *)(pBlobHeader + 1);
+    CONST BYTE *pbKeyStream = (CONST BYTE*)(pKeyLen + 1);
+
+    if (dwDataLen < sizeof(BLOBHEADER)+sizeof(DWORD)+*pKeyLen)
+    {
+        SetLastError(NTE_BAD_DATA); /* FIXME: error code */
+        return FALSE;
+    }
+
+    *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, *pKeyLen<<19, &pCryptKey);
+    if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE)
+        return FALSE;
+    memcpy(pCryptKey->abKeyValue, pbKeyStream, *pKeyLen);
+    setup_key(pCryptKey);
+    if (dwFlags & CRYPT_EXPORTABLE)
+        pCryptKey->dwPermissions |= CRYPT_EXPORT;
+    return TRUE;
+}
+
+/******************************************************************************
+ * import_key [Internal]
+ *
+ * Import a BLOB'ed key into a key container, optionally storing the key's
+ * value to the registry.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the BLOB.
+ *  dwDataLen [I] Length of data in buffer at pbData.
+ *  hPubKey   [I] Key used to decrypt sensitive BLOB data.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  fStoreKey [I] If TRUE, the imported key is stored to the registry.
+ *  phKey     [O] Handle to the imported key.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+static BOOL import_key(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
+                       HCRYPTKEY hPubKey, DWORD dwFlags, BOOL fStoreKey,
+                       HCRYPTKEY *phKey)
+{
+    KEYCONTAINER *pKeyContainer;
+    CONST BLOBHEADER *pBlobHeader = (CONST BLOBHEADER*)pbData;
+
     if (!lookup_handle(&handle_table, hProv, RSAENH_MAGIC_CONTAINER,
                        (OBJECTHDR**)&pKeyContainer)) 
     {
@@ -2374,115 +2952,34 @@ BOOL WINAPI RSAENH_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDat
         pBlobHeader->bVersion != CUR_BLOB_VERSION ||
         pBlobHeader->reserved != 0) 
     {
+        TRACE("bVersion = %d, reserved = %d\n", pBlobHeader->bVersion,
+              pBlobHeader->reserved);
         SetLastError(NTE_BAD_DATA);
         return FALSE;
     }
 
+    /* If this is a verify-only context, the key is not persisted regardless of
+     * fStoreKey's original value.
+     */
+    fStoreKey = fStoreKey && !(dwFlags & CRYPT_VERIFYCONTEXT);
+    TRACE("blob type: %x\n", pBlobHeader->bType);
     switch (pBlobHeader->bType)
     {
         case PRIVATEKEYBLOB:    
-            if ((dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY)) || 
-                (pRSAPubKey->magic != RSAENH_MAGIC_RSA2) ||
-                (dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 
-                    (2 * pRSAPubKey->bitlen >> 3) + (5 * ((pRSAPubKey->bitlen+8)>>4)))) 
-            {
-                SetLastError(NTE_BAD_DATA);
-                return FALSE;
-            }
-    
-            *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey);
-            if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE;
-            setup_key(pCryptKey);
-            ret = import_private_key_impl((CONST BYTE*)(pRSAPubKey+1), &pCryptKey->context, 
-                                           pRSAPubKey->bitlen/8, pRSAPubKey->pubexp);
-            if (ret) {
-                switch (pBlobHeader->aiKeyAlg)
-                {
-                case AT_SIGNATURE:
-                case CALG_RSA_SIGN:
-                    TRACE("installing signing key\n");
-                    RSAENH_CPDestroyKey(hProv, pKeyContainer->hSignatureKeyPair);
-                    copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
-                                &pKeyContainer->hSignatureKeyPair);
-                    break;
-                case AT_KEYEXCHANGE:
-                case CALG_RSA_KEYX:
-                    TRACE("installing key exchange key\n");
-                    RSAENH_CPDestroyKey(hProv, pKeyContainer->hKeyExchangeKeyPair);
-                    copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
-                                &pKeyContainer->hKeyExchangeKeyPair);
-                    break;
-                }
-            }
-            return ret;
+            return import_private_key(hProv, pbData, dwDataLen, dwFlags,
+                                      fStoreKey, phKey);
                 
         case PUBLICKEYBLOB:
-            if ((dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY)) || 
-                (pRSAPubKey->magic != RSAENH_MAGIC_RSA1) ||
-                (dwDataLen < sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + (pRSAPubKey->bitlen >> 3))) 
-            {
-                SetLastError(NTE_BAD_DATA);
-                return FALSE;
-            }
-    
-            /* Since this is a public key blob, only the public key is
-             * available, so only signature verification is possible.
-             */
-            algID = pBlobHeader->aiKeyAlg;
-            *phKey = new_key(hProv, algID, MAKELONG(0,pRSAPubKey->bitlen), &pCryptKey); 
-            if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE) return FALSE; 
-            setup_key(pCryptKey);
-            ret = import_public_key_impl((CONST BYTE*)(pRSAPubKey+1), &pCryptKey->context, 
-                                          pRSAPubKey->bitlen >> 3, pRSAPubKey->pubexp);
-            if (ret) {
-                switch (pBlobHeader->aiKeyAlg)
-                {
-                case AT_KEYEXCHANGE:
-                case CALG_RSA_KEYX:
-                    TRACE("installing public key\n");
-                    RSAENH_CPDestroyKey(hProv, pKeyContainer->hKeyExchangeKeyPair);
-                    copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
-                                &pKeyContainer->hKeyExchangeKeyPair);
-                    break;
-                }
-            }
-            return ret;
+            return import_public_key(hProv, pbData, dwDataLen, dwFlags,
+                                     fStoreKey, phKey);
                 
         case SIMPLEBLOB:
-            if (!lookup_handle(&handle_table, hPubKey, RSAENH_MAGIC_KEY, (OBJECTHDR**)&pPubKey) ||
-                pPubKey->aiAlgid != CALG_RSA_KEYX) 
-            {
-                SetLastError(NTE_BAD_PUBLIC_KEY); /* FIXME: error code? */
-                return FALSE;
-            }
-
-            if (dwDataLen < sizeof(BLOBHEADER)+sizeof(ALG_ID)+pPubKey->dwBlockLen) 
-            {
-                SetLastError(NTE_BAD_DATA); /* FIXME: error code */
-                return FALSE;
-            }
+            return import_symmetric_key(hProv, pbData, dwDataLen, hPubKey,
+                                        dwFlags, phKey);
 
-            pbDecrypted = HeapAlloc(GetProcessHeap(), 0, pPubKey->dwBlockLen);
-            if (!pbDecrypted) return FALSE;
-            encrypt_block_impl(pPubKey->aiAlgid, PK_PRIVATE, &pPubKey->context, pbKeyStream, pbDecrypted, 
-                               RSAENH_DECRYPT);
-
-            dwKeyLen = RSAENH_MAX_KEY_SIZE;
-            if (!unpad_data(pbDecrypted, pPubKey->dwBlockLen, pbDecrypted, &dwKeyLen, dwFlags)) {
-                HeapFree(GetProcessHeap(), 0, pbDecrypted);
-                return FALSE;
-            }
-            
-            *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, dwKeyLen<<19, &pCryptKey);
-            if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE)
-            {
-                HeapFree(GetProcessHeap(), 0, pbDecrypted);
-                return FALSE;
-            }
-            memcpy(pCryptKey->abKeyValue, pbDecrypted, dwKeyLen);
-            HeapFree(GetProcessHeap(), 0, pbDecrypted);
-            setup_key(pCryptKey);
-            return TRUE;
+        case PLAINTEXTKEYBLOB:
+            return import_plaintext_key(hProv, pbData, dwDataLen, dwFlags,
+                                        phKey);
 
         default:
             SetLastError(NTE_BAD_TYPE); /* FIXME: error code? */
@@ -2490,6 +2987,39 @@ BOOL WINAPI RSAENH_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDat
     }
 }
 
+/******************************************************************************
+ * CPImportKey (RSAENH.@)
+ *
+ * Import a BLOB'ed key into a key container.
+ *
+ * PARAMS
+ *  hProv     [I] Key container into which the key is to be imported.
+ *  pbData    [I] Pointer to a buffer which holds the BLOB.
+ *  dwDataLen [I] Length of data in buffer at pbData.
+ *  hPubKey   [I] Key used to decrypt sensitive BLOB data.
+ *  dwFlags   [I] One of:
+ *                CRYPT_EXPORTABLE: the imported key is marked exportable
+ *  phKey     [O] Handle to the imported key.
+ *
+ * RETURNS
+ *  Success: TRUE.
+ *  Failure: FALSE.
+ */
+BOOL WINAPI RSAENH_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen,
+                               HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey)
+{
+    TRACE("(hProv=%08lx, pbData=%p, dwDataLen=%d, hPubKey=%08lx, dwFlags=%08x, phKey=%p)\n",
+        hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);
+
+    if (dwFlags & CRYPT_IPSEC_HMAC_KEY)
+    {
+        FIXME("unimplemented for CRYPT_IPSEC_HMAC_KEY\n");
+        SetLastError(NTE_BAD_FLAGS);
+        return FALSE;
+    }
+    return import_key(hProv, pbData, dwDataLen, hPubKey, dwFlags, TRUE, phKey);
+}
+
 /******************************************************************************
  * CPGenKey (RSAENH.@)
  *
@@ -2535,11 +3065,9 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
             if (pCryptKey) { 
                 new_key_impl(pCryptKey->aiAlgid, &pCryptKey->context, pCryptKey->dwKeyLen);
                 setup_key(pCryptKey);
-                if (Algid == AT_SIGNATURE) {
-                    RSAENH_CPDestroyKey(hProv, pKeyContainer->hSignatureKeyPair);
-                    copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
-                                &pKeyContainer->hSignatureKeyPair);
-                }
+                RSAENH_CPDestroyKey(hProv, pKeyContainer->hSignatureKeyPair);
+                copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
+                            &pKeyContainer->hSignatureKeyPair);
             }
             break;
 
@@ -2549,11 +3077,9 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
             if (pCryptKey) { 
                 new_key_impl(pCryptKey->aiAlgid, &pCryptKey->context, pCryptKey->dwKeyLen);
                 setup_key(pCryptKey);
-                if (Algid == AT_KEYEXCHANGE) {
-                    RSAENH_CPDestroyKey(hProv, pKeyContainer->hKeyExchangeKeyPair);
-                    copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
-                                &pKeyContainer->hKeyExchangeKeyPair);
-                }
+                RSAENH_CPDestroyKey(hProv, pKeyContainer->hKeyExchangeKeyPair);
+                copy_handle(&handle_table, *phKey, RSAENH_MAGIC_KEY,
+                            &pKeyContainer->hKeyExchangeKeyPair);
             }
             break;
             
@@ -2562,6 +3088,10 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP
         case CALG_DES:
         case CALG_3DES_112:
         case CALG_3DES:
+        case CALG_AES:
+        case CALG_AES_128:
+        case CALG_AES_192:
+        case CALG_AES_256:
         case CALG_PCT1_MASTER:
         case CALG_SSL2_MASTER:
         case CALG_SSL3_MASTER:
@@ -2701,8 +3231,8 @@ BOOL WINAPI RSAENH_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
                 finalize_hash(pCryptHash);
                 pCryptHash->dwState = RSAENH_HASHSTATE_FINISHED;
             }
-            
-            return copy_param(pbData, pdwDataLen, (CONST BYTE*)pCryptHash->abHashValue, 
+
+            return copy_param(pbData, pdwDataLen, pCryptHash->abHashValue,
                               pCryptHash->dwHashSize);
 
         default:
@@ -2761,6 +3291,14 @@ BOOL WINAPI RSAENH_CPSetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
     }
     
     switch (dwParam) {
+        case KP_PADDING:
+            /* The MS providers only support PKCS5_PADDING */
+            if (*(DWORD *)pbData != PKCS5_PADDING) {
+                SetLastError(NTE_BAD_DATA);
+                return FALSE;
+            }
+            return TRUE;
+
         case KP_MODE:
             pCryptKey->dwMode = *(DWORD*)pbData;
             return TRUE;
@@ -2770,14 +3308,49 @@ BOOL WINAPI RSAENH_CPSetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
             return TRUE;
 
         case KP_PERMISSIONS:
-            pCryptKey->dwPermissions = *(DWORD*)pbData;
+        {
+            DWORD perms = *(DWORD *)pbData;
+
+            if ((perms & CRYPT_EXPORT) &&
+                !(pCryptKey->dwPermissions & CRYPT_EXPORT))
+            {
+                SetLastError(NTE_BAD_DATA);
+                return FALSE;
+            }
+            else if (!(perms & CRYPT_EXPORT) &&
+                (pCryptKey->dwPermissions & CRYPT_EXPORT))
+            {
+                /* Clearing the export permission appears to be ignored,
+                 * see tests.
+                 */
+                perms |= CRYPT_EXPORT;
+            }
+            pCryptKey->dwPermissions = perms;
             return TRUE;
+        }
 
         case KP_IV:
             memcpy(pCryptKey->abInitVector, pbData, pCryptKey->dwBlockLen);
             setup_key(pCryptKey);
             return TRUE;
 
+        case KP_SALT_EX:
+        {
+            CRYPT_INTEGER_BLOB *blob = (CRYPT_INTEGER_BLOB *)pbData;
+
+            /* salt length can't be greater than 184 bits = 24 bytes */
+            if (blob->cbData > 24)
+            {
+                SetLastError(NTE_BAD_DATA);
+                return FALSE;
+            }
+            memcpy(pCryptKey->abKeyValue + pCryptKey->dwKeyLen, blob->pbData,
+                   blob->cbData);
+            pCryptKey->dwSaltLen = blob->cbData;
+            setup_key(pCryptKey);
+            return TRUE;
+        }
+
         case KP_EFFECTIVE_KEYLEN:
             switch (pCryptKey->aiAlgid) {
                 case CALG_RC2:
@@ -2864,7 +3437,7 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
                                  DWORD *pdwDataLen, DWORD dwFlags)
 {
     CRYPTKEY *pCryptKey;
-    DWORD dwBitLen;
+    DWORD dwValue;
         
     TRACE("(hProv=%08lx, hKey=%08lx, dwParam=%08x, pbData=%p, pdwDataLen=%p dwFlags=%08x)\n",
           hProv, hKey, dwParam, pbData, pdwDataLen, dwFlags);
@@ -2889,27 +3462,31 @@ BOOL WINAPI RSAENH_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam
     switch (dwParam) 
     {
         case KP_IV:
-            return copy_param(pbData, pdwDataLen, (CONST BYTE*)pCryptKey->abInitVector, 
+            return copy_param(pbData, pdwDataLen, pCryptKey->abInitVector,
                               pCryptKey->dwBlockLen);
         
         case KP_SALT:
             return copy_param(pbData, pdwDataLen, 
-                    (CONST BYTE*)&pCryptKey->abKeyValue[pCryptKey->dwKeyLen], pCryptKey->dwSaltLen);
-        
+                    &pCryptKey->abKeyValue[pCryptKey->dwKeyLen], pCryptKey->dwSaltLen);
+
+        case KP_PADDING:
+            dwValue = PKCS5_PADDING;
+            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwValue, sizeof(DWORD));
+
         case KP_KEYLEN:
-            dwBitLen = pCryptKey->dwKeyLen << 3;
-            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwBitLen, sizeof(DWORD));
+            dwValue = pCryptKey->dwKeyLen << 3;
+            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwValue, sizeof(DWORD));
         
         case KP_EFFECTIVE_KEYLEN:
             if (pCryptKey->dwEffectiveKeyLen)
-                dwBitLen = pCryptKey->dwEffectiveKeyLen;
+                dwValue = pCryptKey->dwEffectiveKeyLen;
             else
-                dwBitLen = pCryptKey->dwKeyLen << 3;
-            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwBitLen, sizeof(DWORD));
+                dwValue = pCryptKey->dwKeyLen << 3;
+            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwValue, sizeof(DWORD));
 
         case KP_BLOCKLEN:
-            dwBitLen = pCryptKey->dwBlockLen << 3;
-            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwBitLen, sizeof(DWORD));
+            dwValue = pCryptKey->dwBlockLen << 3;
+            return copy_param(pbData, pdwDataLen, (CONST BYTE*)&dwValue, sizeof(DWORD));
     
         case KP_MODE:
             return copy_param(pbData, pdwDataLen, (CONST BYTE*)&pCryptKey->dwMode, sizeof(DWORD));
@@ -2962,11 +3539,11 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
     DWORD dwTemp;
     HKEY hKey;
    
-    /* This is for dwParam 41, which does not seem to be documented
-     * on MSDN. IE6 SP1 asks for it in the 'About' dialog, however.
+    /* This is for dwParam PP_CRYPT_COUNT_KEY_USE.
+     * IE6 SP1 asks for it in the 'About' dialog.
      * Returning this BLOB seems to satisfy IE. The marked 0x00 seem 
      * to be 'don't care's. If you know anything more specific about
-     * provider parameter 41, please report to wine-devel@winehq.org */
+     * this provider parameter, please report to wine-devel@winehq.org */
     static CONST BYTE abWTF[96] = { 
         0xb0, 0x25,     0x63,     0x86, 0x9c, 0xab,     0xb6,     0x37, 
         0xe8, 0x82, /**/0x00,/**/ 0x72, 0x06, 0xb2, /**/0x00,/**/ 0x3b, 
@@ -3110,7 +3687,7 @@ BOOL WINAPI RSAENH_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData,
                                   sizeof(PROV_ENUMALGS_EX));
             }
 
-        case 41: /* Undocumented. Asked for by IE About dialog */
+        case PP_CRYPT_COUNT_KEY_USE: /* Asked for by IE About dialog */
             return copy_param(pbData, pdwDataLen, abWTF, sizeof(abWTF));
 
         default:
@@ -3492,7 +4069,7 @@ BOOL WINAPI RSAENH_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwPa
 {
     CRYPTHASH *pCryptHash;
     CRYPTKEY *pCryptKey;
-    int i;
+    DWORD i;
 
     TRACE("(hProv=%08lx, hHash=%08lx, dwParam=%08x, pbData=%p, dwFlags=%08x)\n",
            hProv, hHash, dwParam, pbData, dwFlags);
@@ -3757,7 +4334,7 @@ cleanup:
     return res;
 }
 
-static const WCHAR szProviderKeys[4][97] = {
+static const WCHAR szProviderKeys[6][116] = {
     {   'S','o','f','t','w','a','r','e','\\',
         'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
         'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
@@ -3781,9 +4358,20 @@ static const WCHAR szProviderKeys[4][97] = {
         'C','r','y','p','t','o','g','r','a','p','h','y','\\','D','e','f','a','u','l','t','s','\\',
         'P','r','o','v','i','d','e','r','\\','M','i','c','r','o','s','o','f','t',' ',
         'R','S','A',' ','S','C','h','a','n','n','e','l',' ',
-        'C','r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r','o','v','i','d','e','r',0 }
+        'C','r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r','o','v','i','d','e','r',0 },
+    {   'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+        'C','r','y','p','t','o','g','r','a','p','h','y','\\','D','e','f','a','u','l','t','s','\\',
+        'P','r','o','v','i','d','e','r','\\','M','i','c','r','o','s','o','f','t',' ',
+        'E','n','h','a','n','c','e','d',' ','R','S','A',' ','a','n','d',' ','A','E','S',' ',
+        'C','r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r','o','v','i','d','e','r',0 },
+    {   'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+        'C','r','y','p','t','o','g','r','a','p','h','y','\\','D','e','f','a','u','l','t','s','\\',
+        'P','r','o','v','i','d','e','r','\\','M','i','c','r','o','s','o','f','t',' ',
+        'E','n','h','a','n','c','e','d',' ','R','S','A',' ','a','n','d',' ','A','E','S',' ',
+        'C','r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r','o','v','i','d','e','r',
+        ' ','(','P','r','o','t','o','t','y','p','e',')',0 }
 };
-static const WCHAR szDefaultKeys[2][65] = {
+static const WCHAR szDefaultKeys[3][65] = {
     {   'S','o','f','t','w','a','r','e','\\',
         'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
         'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
@@ -3791,7 +4379,11 @@ static const WCHAR szDefaultKeys[2][65] = {
     {   'S','o','f','t','w','a','r','e','\\',
         'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
         'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
-        'i','d','e','r',' ','T','y','p','e','s','\\','T','y','p','e',' ','0','1','2',0 }
+        'i','d','e','r',' ','T','y','p','e','s','\\','T','y','p','e',' ','0','1','2',0 },
+    {   'S','o','f','t','w','a','r','e','\\',
+        'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
+        'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
+        'i','d','e','r',' ','T','y','p','e','s','\\','T','y','p','e',' ','0','2','4',0 }
 };
 
 
@@ -3823,7 +4415,7 @@ HRESULT WINAPI DllRegisterServer(void)
     long apiRet;
     int i;
 
-    for (i=0; i<4; i++) {
+    for (i=0; i<6; i++) {
         apiRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szProviderKeys[i], 0, NULL,
             REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &dp);
 
@@ -3835,8 +4427,22 @@ HRESULT WINAPI DllRegisterServer(void)
                 static const WCHAR szRSABase[] = { 'r','s','a','e','n','h','.','d','l','l',0 };
                 static const WCHAR szType[] = { 'T','y','p','e',0 };
                 static const WCHAR szSignature[] = { 'S','i','g','n','a','t','u','r','e',0 };
-                DWORD type = (i == 3) ? PROV_RSA_SCHANNEL : PROV_RSA_FULL;
-                DWORD sign = 0xdeadbeef;
+                DWORD type, sign;
+
+                switch(i)
+                {
+                    case 3:
+                        type=PROV_RSA_SCHANNEL;
+                        break;
+                    case 4:
+                    case 5:
+                        type=PROV_RSA_AES;
+                        break;
+                    default:
+                        type=PROV_RSA_FULL;
+                        break;
+                }
+                sign = 0xdeadbeef;
                 RegSetValueExW(key, szImagePath, 0, REG_SZ, (const BYTE *)szRSABase,
                                (lstrlenW(szRSABase) + 1) * sizeof(WCHAR));
                 RegSetValueExW(key, szType, 0, REG_DWORD, (LPBYTE)&type, sizeof(type));
@@ -3846,30 +4452,36 @@ HRESULT WINAPI DllRegisterServer(void)
         }
     }
     
-    for (i=0; i<2; i++) {
-        apiRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szDefaultKeys[i], 0, NULL, 
+    for (i=0; i<3; i++) {
+        apiRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szDefaultKeys[i], 0, NULL,
                                  REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &dp);
         if (apiRet == ERROR_SUCCESS)
         {
             if (dp == REG_CREATED_NEW_KEY)
             {
                 static const WCHAR szName[] = { 'N','a','m','e',0 };
-                static const WCHAR szRSAName[2][46] = {
-                  { 'M','i','c','r','o','s','o','f','t',' ', 'B','a','s','e',' ',
+                static const WCHAR szRSAName[3][54] = {
+                  { 'M','i','c','r','o','s','o','f','t',' ',
+                    'E','n','h','a','n','c','e','d',' ',
                     'C','r','y','p','t','o','g','r','a','p','h','i','c',' ', 
                     'P','r','o','v','i','d','e','r',' ','v','1','.','0',0 },
                   { 'M','i','c','r','o','s','o','f','t',' ','R','S','A',' ',
                     'S','C','h','a','n','n','e','l',' ',
                     'C','r','y','p','t','o','g','r','a','p','h','i','c',' ',
+                    'P','r','o','v','i','d','e','r',0 },
+                  { 'M','i','c','r','o','s','o','f','t',' ','E','n','h','a','n','c','e','d',' ',
+                    'R','S','A',' ','a','n','d',' ','A','E','S',' ',
+                    'C','r','y','p','t','o','g','r','a','p','h','i','c',' ',
                     'P','r','o','v','i','d','e','r',0 } };
                 static const WCHAR szTypeName[] = { 'T','y','p','e','N','a','m','e',0 };
-                static const WCHAR szRSATypeName[2][38] = { 
+                static const WCHAR szRSATypeName[3][38] = {
                   { 'R','S','A',' ','F','u','l','l',' ',
                        '(','S','i','g','n','a','t','u','r','e',' ','a','n','d',' ',
                     'K','e','y',' ','E','x','c','h','a','n','g','e',')',0 },
-                  { 'R','S','A',' ','S','C','h','a','n','n','e','l',0 } };
+                  { 'R','S','A',' ','S','C','h','a','n','n','e','l',0 },
+                  { 'R','S','A',' ','F','u','l','l',' ','a','n','d',' ','A','E','S',0 } };
 
-                RegSetValueExW(key, szName, 0, REG_SZ, 
+                RegSetValueExW(key, szName, 0, REG_SZ,
                                 (const BYTE *)szRSAName[i], lstrlenW(szRSAName[i])*sizeof(WCHAR)+sizeof(WCHAR));
                 RegSetValueExW(key, szTypeName, 0, REG_SZ, 
                                 (const BYTE *)szRSATypeName[i], lstrlenW(szRSATypeName[i])*sizeof(WCHAR)+sizeof(WCHAR));
@@ -3900,7 +4512,10 @@ HRESULT WINAPI DllUnregisterServer(void)
     RegDeleteKeyW(HKEY_LOCAL_MACHINE, szProviderKeys[1]);
     RegDeleteKeyW(HKEY_LOCAL_MACHINE, szProviderKeys[2]);
     RegDeleteKeyW(HKEY_LOCAL_MACHINE, szProviderKeys[3]);
+    RegDeleteKeyW(HKEY_LOCAL_MACHINE, szProviderKeys[4]);
+    RegDeleteKeyW(HKEY_LOCAL_MACHINE, szProviderKeys[5]);
     RegDeleteKeyW(HKEY_LOCAL_MACHINE, szDefaultKeys[0]);
     RegDeleteKeyW(HKEY_LOCAL_MACHINE, szDefaultKeys[1]);
+    RegDeleteKeyW(HKEY_LOCAL_MACHINE, szDefaultKeys[2]);
     return S_OK;
 }