c - 如何导出使用 CryptoAPI 派生的 AES key

标签 c encryption cryptography aes cryptoapi

我想使用 Windows CryptoAPI 函数进行 AES 加密。

如您所知,API 有 a lot of functions创建、散列和更改输入的 key ;它派生出 key ,您就可以得到它的句柄。

我的问题是我想知道派生 key 是什么?

#include <Windows.h>
#include <stdio.h>

int main()
{
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    HCRYPTHASH hHash = 0;
    DWORD dwCount = 5;
    BYTE  rgData[512] = {0x01, 0x02, 0x03, 0x04, 0x05};
    LPWSTR wszPassword = L"pass";
    DWORD cbPassword = (wcslen(wszPassword)+1)*sizeof(WCHAR);

    if(!CryptAcquireContext(
        &hProv, 
        NULL,  
        MS_ENH_RSA_AES_PROV, 
        PROV_RSA_AES, 
        CRYPT_VERIFYCONTEXT))
    {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto Cleanup;
    }

    if(!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) 
    { 
        printf("Error %x during CryptCreateHash!\n", GetLastError());
        goto Cleanup;
    } 

    if(!CryptHashData(hHash, (PBYTE)wszPassword, cbPassword, 0)) 
    { 
        printf("Error %x during CryptHashData!\n", GetLastError());
        goto Cleanup;
    } 

    if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey)) 
    { 
        printf("Error %x during CryptDeriveKey!\n", GetLastError());
        goto Cleanup;
    }

    for(DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ",rgData[i]);
    }
    printf("\n");

    if (!CryptEncrypt(
        hKey,
        0,
        TRUE,
        0,
        rgData,
        &dwCount,
        sizeof(rgData))) 
    {
        printf("Error %x during CryptEncrypt!\n", GetLastError());
        goto Cleanup;
    }

    for(DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ",rgData[i]);
    }
    printf("\n");

Cleanup:
    if(hKey) 
    {
        CryptDestroyKey(hKey);
    }
    if(hHash) 
    {
        CryptDestroyHash(hHash);
    }
    if(hProv) 
    {
        CryptReleaseContext(hProv, 0);
    }
    return 0;
}

最佳答案

因为您已通过将 CRYPT_EXPORTABLE 传递给 CryptDeriveKey function 使派生 key 可导出, 可以使用 CryptExportKey function导出派生的 key Material 。

如果您使用的第三方 CSP 不允许 key 导出,即使使用 CRYPT_EXPORTABLE 标志也是异常(exception),在这种情况下请参阅 Maarten Bodewes' answer在 CSP 之外自己重放 key 推导步骤。

如果您遵循 CryptExportKey MSDN link您会看到有一个示例函数显示了如何使用该函数以普通方式导出 key Material 。如果需要,也可以使用另一个 key 导出 key wrapped(即由另一个 key 加密),方法是将 key 句柄传递给 hExpKey(第二个参数) CryptExportKey。将 NULL 传递给此参数以普通形式导出。

我已经更新了您的示例程序(如下)以使用上面 MSDN 链接中的示例代码导出 key ,并使用 CryptBinaryToString将 key Material 打印为 base64 字符串的函数。我已经导出为 PLAINTEXTKEYBLOB blob 类型,它使用数据结构 BLOBHEADER|key length|key material 所以还需要做一些工作来从 blob 中提取我们感兴趣的原始 key Material :

#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "crypt32.lib")

BOOL GetExportedKey(
    HCRYPTKEY hKey,
    DWORD dwBlobType,
    LPBYTE *ppbKeyBlob,
    LPDWORD pdwBlobLen)
{
    DWORD dwBlobLength;
    *ppbKeyBlob = NULL;
    *pdwBlobLen = 0;

    // Export the public key. Here the public key is exported to a 
    // PUBLICKEYBLOB. This BLOB can be written to a file and
    // sent to another user.

    if (CryptExportKey(
        hKey,
        NULL,
        dwBlobType,
        0,
        NULL,
        &dwBlobLength))
    {
        printf("Size of the BLOB for the public key determined. \n");
    }
    else
    {
        printf("Error computing BLOB length.\n");
        return FALSE;
    }

    // Allocate memory for the pbKeyBlob.
    if (*ppbKeyBlob = (LPBYTE)malloc(dwBlobLength))
    {
        printf("Memory has been allocated for the BLOB. \n");
    }
    else
    {
        printf("Out of memory. \n");
        return FALSE;
    }

    // Do the actual exporting into the key BLOB.
    if (CryptExportKey(
        hKey,
        NULL,
        dwBlobType,
        0,
        *ppbKeyBlob,
        &dwBlobLength))
    {
        printf("Contents have been written to the BLOB. \n");
        *pdwBlobLen = dwBlobLength;
    }
    else
    {
        printf("Error exporting key.\n");
        free(*ppbKeyBlob);
        *ppbKeyBlob = NULL;

        return FALSE;
    }

    return TRUE;
}

int main()
{
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    HCRYPTHASH hHash = 0;
    DWORD dwCount = 5;
    LPBYTE keyBlob = NULL;
    DWORD keyBlobLength;
    LPSTR keyBlobBase64 = NULL;
    DWORD base64Length = 0;
    BYTE  rgData[512] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
    LPWSTR wszPassword = L"pass";
    DWORD cbPassword = (wcslen(wszPassword) + 1)*sizeof(WCHAR);

    if (!CryptAcquireContext(
        &hProv,
        NULL,
        MS_ENH_RSA_AES_PROV,
        PROV_RSA_AES,
        CRYPT_VERIFYCONTEXT))
    {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
    {
        printf("Error %x during CryptCreateHash!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptHashData(hHash, (PBYTE)wszPassword, cbPassword, 0))
    {
        printf("Error %x during CryptHashData!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey))
    {
        printf("Error %x during CryptDeriveKey!\n", GetLastError());
        goto Cleanup;
    }

    if (!GetExportedKey(hKey, PLAINTEXTKEYBLOB, &keyBlob, &keyBlobLength))
    {
        printf("Error %x during GetExportedKey!\n", GetLastError());
        goto Cleanup;
    }

    while (1)
    {
        // PLAINTEXTKEYBLOB: BLOBHEADER|DWORD key length|Key material|
        DWORD keyMaterialLength;
        LPBYTE keyMaterial;

        keyMaterialLength = *(DWORD*)(keyBlob + sizeof(BLOBHEADER));
        keyMaterial = (keyBlob + sizeof(BLOBHEADER) + sizeof(DWORD));

        if (!CryptBinaryToStringA(keyMaterial, keyMaterialLength, CRYPT_STRING_BASE64, keyBlobBase64, &base64Length))
        {
            printf("Error %x during GetExportedKey!\n", GetLastError());
            goto Cleanup;
        }

        if (keyBlobBase64)
        {
            printf("%d-bit key blob: %s\n", keyMaterialLength * 8, keyBlobBase64);
            break;
        }
        else
        {
            keyBlobBase64 = malloc(base64Length);
        }
    }

    for (DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ", rgData[i]);
    }
    printf("\n");

    if (!CryptEncrypt(
        hKey,
        0,
        TRUE,
        0,
        rgData,
        &dwCount,
        sizeof(rgData)))
    {
        printf("Error %x during CryptEncrypt!\n", GetLastError());
        goto Cleanup;
    }

    for (DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ", rgData[i]);
    }
    printf("\n");

Cleanup:
    free(keyBlob);
    free(keyBlobBase64);
    if (hKey)
    {
        CryptDestroyKey(hKey);
    }
    if (hHash)
    {
        CryptDestroyHash(hHash);
    }
    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
    }
    return 0;
}

关于c - 如何导出使用 CryptoAPI 派生的 AES key ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29586097/

相关文章:

iis - C_GetSlotList 从 IIS 但不是从 IIS Express 调用时失败

java - 如何验证 header 中包含 "crit"值的 JWSObject?

c++ - 在 Win32 (c++) 的另一个进程中写入文本框

java - DES 加密明文与密码长度

c++ - 计算墙

Java:从小程序为 AES256 修补客户端安全策略

cocoa - 如何在文件 cocoa 中存储盐和IV?

python - 在 Python 中加密字符串

c - 关于 OpenMP 的简单问题

c - 如何使用 while 循环