我必须使用CNG (Cryptography API: Next Generation)函数为Windows Embedded Compact 2013编译现有的C代码。此代码使用BCryptDeriveKeyPBKDF2,而Windows Embedded Compact 2013中不提供此代码。
这意味着我需要替换下面的函数,以实现RFC 2898 5.2节中定义的PBKDF2密钥派生算法,但不使用BCryptDeriveKeyPBKDF2。
我发现一些使用CryptoAPI函数here的C代码,但是如果可能,我不想使用第二个已弃用的API。
BOOL pbkdf2(
PUCHAR pbPassword, ULONG cbPassword,
PUCHAR pbSalt, ULONG cbSalt,
ULONGLONG cIterations,
PUCHAR pbDerivedKey, ULONG cbDerivedKey)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlgorithm;
status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (BCRYPT_SUCCESS(status))
{
status = BCryptDeriveKeyPBKDF2(hAlgorithm, pbPassword, cbPassword, pbSalt, cbSalt, cIterations, pbDerivedKey, cbDerivedKey, 0);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
return BCRYPT_SUCCESS(status);
}
答案 0 :(得分:0)
您可以使用CNG原语(例如BCryptCreateHash)来实现算法。最重要的是在original function中使用标志BCRYPT_ALG_HANDLE_HMAC_FLAG:
void pbkdf2()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
std::vector<BYTE> pass = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
std::vector<BYTE> salt = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
std::vector<BYTE> derived_key(32);
std::vector<BYTE> dig(32);
byte t[] = { 0x00, 0x00, 0x00, 0x01 };
DWORD itcount = 10000;
SECURITY_STATUS status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM,
nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptCreateHash(hAlg, &hHash, nullptr, 0, pass.data(), pass.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, salt.data(), salt.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, t, 4, 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptFinishHash(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
derived_key = dig;
BCryptDestroyHash(hHash);
for (DWORD i = 1; i < itcount; ++i)
{
status = BCryptCreateHash(hAlg, &hHash, nullptr, 0, pass.data(), pass.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptFinishHash(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
BCryptDestroyHash(hHash);
for (DWORD j = 0; j < dig.size(); ++j) {
derived_key[j] ^= dig[j];
}
}
Exit:
if (hHash) {
BCryptDestroyHash(hHash);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return;
}
编辑:阐明t []的含义。
根据{{3}}(5.2):
对于派生键的每个块,应用定义的函数F 下面是密码P,盐S,迭代次数c和 计算块的块索引:
T_1 = F (P, S, c, 1) , T_2 = F (P, S, c, 2) , ... T_l = F (P, S, c, l) , where the function F is defined as the exclusive-or sum of the first c iterates of the underlying pseudorandom function PRF applied to the password P and the concatenation of the salt S and the block index i: F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c where U_1 = PRF (P, S || INT (i)) , U_2 = PRF (P, U_1) , ... U_c = PRF (P, U_{c-1}) . Here, INT (i) is a four-octet encoding of the integer i, most significant octet first.
所以,在我的代码t []中-是整数1的四个八位字节编码(对于第一次迭代),最高有效八位字节在前。
答案 1 :(得分:0)
我拿了this code,将已弃用的wincrypt调用转换为新的CNG API,对其进行了重构,并删除了我不需要的东西。
尽管我不明白代码在做什么,但看起来它产生的结果与我使用BCryptDeriveKeyPBKDF2的问题中的函数相同。
#define NOCRYPT
#include <windows.h>
#include <bcrypt.h>
#include <math.h>
#include <assert.h>
#define DIGEST_SIZE 20
#define BLOCK_SIZE 64
typedef struct
{
BCRYPT_ALG_HANDLE hAlgorithm;
BCRYPT_HASH_HANDLE hInnerHash;
BCRYPT_HASH_HANDLE hOuterHash;
} PRF_CTX;
static void hmacFree(PRF_CTX* pContext)
{
if (pContext->hOuterHash) BCryptDestroyHash(pContext->hOuterHash);
if (pContext->hInnerHash) BCryptDestroyHash(pContext->hInnerHash);
if (pContext->hAlgorithm) BCryptCloseAlgorithmProvider(pContext->hAlgorithm, 0);
}
static BOOL hmacPrecomputeDigest(BCRYPT_HASH_HANDLE hHash, PUCHAR pbPassword, DWORD cbPassword, BYTE mask)
{
BYTE buffer[BLOCK_SIZE];
DWORD i;
assert(cbPassword <= BLOCK_SIZE);
memset (buffer, mask, sizeof(buffer));
for (i = 0; i < cbPassword; ++i)
{
buffer[i] = (char) (pbPassword[i] ^ mask);
}
return BCRYPT_SUCCESS(BCryptHashData(hHash, buffer, sizeof(buffer), 0));
}
static BOOL hmacInit(PRF_CTX* pContext, PUCHAR pbPassword, DWORD cbPassword)
{
BCRYPT_HASH_HANDLE hHash = NULL;
BOOL bStatus = FALSE;
BYTE key[DIGEST_SIZE];
if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&pContext->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, 0)) ||
!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hInnerHash, NULL, 0, NULL, 0, 0)) ||
!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hOuterHash, NULL, 0, NULL, 0, 0)))
{
goto hmacInit_end;
}
if (cbPassword > BLOCK_SIZE)
{
ULONG cbResult;
if (!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &hHash, NULL, 0, NULL, 0, 0)) ||
!BCRYPT_SUCCESS(BCryptHashData(hHash, pbPassword, cbPassword, 0)) ||
!BCRYPT_SUCCESS(BCryptGetProperty(hHash, BCRYPT_HASH_LENGTH, (PUCHAR)&cbPassword, sizeof(cbPassword), &cbResult, 0)) ||
!BCRYPT_SUCCESS(BCryptFinishHash(hHash, key, cbPassword, 0)))
{
goto hmacInit_end;
}
pbPassword = key;
}
bStatus =
hmacPrecomputeDigest(pContext->hInnerHash, pbPassword, cbPassword, 0x36) &&
hmacPrecomputeDigest(pContext->hOuterHash, pbPassword, cbPassword, 0x5C);
hmacInit_end:
if (hHash) BCryptDestroyHash(hHash);
if (bStatus == FALSE) hmacFree(pContext);
return bStatus;
}
static BOOL hmacCalculateInternal(BCRYPT_HASH_HANDLE hHashTemplate, PUCHAR pbData, DWORD cbData, PUCHAR pbOutput, DWORD cbOutput)
{
BOOL success = FALSE;
BCRYPT_HASH_HANDLE hHash = NULL;
if (BCRYPT_SUCCESS(BCryptDuplicateHash(hHashTemplate, &hHash, NULL, 0, 0)))
{
success =
BCRYPT_SUCCESS(BCryptHashData(hHash, pbData, cbData, 0)) &&
BCRYPT_SUCCESS(BCryptFinishHash(hHash, pbOutput, cbOutput, 0));
BCryptDestroyHash(hHash);
}
return success;
}
static BOOL hmacCalculate(PRF_CTX* pContext, PUCHAR pbData, DWORD cbData, PUCHAR pbDigest)
{
return
hmacCalculateInternal(pContext->hInnerHash, pbData, cbData, pbDigest, DIGEST_SIZE) &&
hmacCalculateInternal(pContext->hOuterHash, pbDigest, DIGEST_SIZE, pbDigest, DIGEST_SIZE);
}
static void xor(LPBYTE ptr1, LPBYTE ptr2, DWORD dwLen)
{
while (dwLen--)
*ptr1++ ^= *ptr2++;
}
BOOL pbkdf2(
PUCHAR pbPassword, ULONG cbPassword,
PUCHAR pbSalt, ULONG cbSalt,
DWORD cIterations,
PUCHAR pbDerivedKey, ULONG cbDerivedKey)
{
BOOL bStatus = FALSE;
DWORD l, r, dwULen, i, j;
BYTE Ti[DIGEST_SIZE];
BYTE V[DIGEST_SIZE];
LPBYTE U = malloc(max((cbSalt + 4), DIGEST_SIZE));
PRF_CTX prfCtx = { 0 };
assert(pbPassword != NULL && cbPassword != 0 && pbSalt != NULL && cbSalt != 0);
assert(cIterations > 0 && pbDerivedKey > 0 && cbDerivedKey > 0);
if (!hmacInit(&prfCtx, pbPassword, cbPassword))
{
goto PBKDF2_end;
}
l = (DWORD) ceil((double) cbDerivedKey / (double) DIGEST_SIZE);
r = cbDerivedKey - (l - 1) * DIGEST_SIZE;
for (i = 1; i <= l; i++)
{
ZeroMemory(Ti, DIGEST_SIZE);
for (j = 0; j < cIterations; j++)
{
if (j == 0)
{
// construct first input for PRF
memcpy(U, pbSalt, cbSalt);
U[cbSalt] = (BYTE) ((i & 0xFF000000) >> 24);
U[cbSalt + 1] = (BYTE) ((i & 0x00FF0000) >> 16);
U[cbSalt + 2] = (BYTE) ((i & 0x0000FF00) >> 8);
U[cbSalt + 3] = (BYTE) ((i & 0x000000FF));
dwULen = cbSalt + 4;
}
else
{
memcpy(U, V, DIGEST_SIZE);
dwULen = DIGEST_SIZE;
}
if (!hmacCalculate(&prfCtx, U, dwULen, V))
{
goto PBKDF2_end;
}
xor(Ti, V, DIGEST_SIZE);
}
if (i != l)
{
memcpy(&pbDerivedKey[(i-1) * DIGEST_SIZE], Ti, DIGEST_SIZE);
}
else
{
// Take only the first r bytes
memcpy(&pbDerivedKey[(i-1) * DIGEST_SIZE], Ti, r);
}
}
bStatus = TRUE;
PBKDF2_end:
hmacFree(&prfCtx);
free(U);
return bStatus;
}