Алгоритм BCRYPT_XTS_AES_ALGORITHM CNG не работает в BCryptGenerateSymmetricKey API

Я пытаюсь использовать алгоритм Windows CNG BCRYPT_XTS_AES_ALGORITHM для шифрования и расшифровки файлов. В рамках этого я написал следующий код для шифрования и дешифрования.

Когда я использую этот код с алгоритмом BCRYPT_AES_ALGORITHM, шифрование и дешифрование работают нормально. Но когда то же самое используется с BCRYPT_XTS_AES_ALGORITHM, он выдает ошибку STATUS_INVALID_PARAMETER в BCryptGenerateSymmetricKey API.

Любая помощь в этом очень ценится.

auto AesCrypt::CreateAESProviderAlgo()->void
{
    auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, BCRYPT_AES_ALGORITHM, nullptr, 0);
    //auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, BCRYPT_XTS_AES_ALGORITHM, nullptr, 0);
    if (0 != status) {
        N2S_THROW("BCryptException::Failed to get provider for BCRYPT_XTS_AES_ALGORITHM. Reason: " + GetErrorCodeAsString(status));
    }

    DWORD cbData = 0;
    DWORD cbKeyObject = 0;
    status = BCryptGetProperty(m_aesHandle, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0);
    if (0 != status) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::BCryptGetProperty return with error " + GetErrorCodeAsString(status));
    }

    m_pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
    if (nullptr == m_pbKeyObject) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::Memory allocation failed.");
    }

    status = BCryptGenerateSymmetricKey(m_aesHandle, &m_keyHandle, m_pbKeyObject, cbKeyObject, (PUCHAR)m_encrptKey.c_str(), SYMM_KEY_SIZE_SECRET, 0);
    if (0 != status) {
        N2S_THROW("CreateSymmetricKeySHA1Hash::BCryptGenerateSymmetricKey return with error " + GetErrorCodeAsString(status));
    }
}

auto AesCrypt::ProcessEncryptFile() ->void
{
    DWORD bytesToSave = 0;
    UCHAR bufFileToOpen[BLOCK_SIZE] = { 0 };
    UCHAR bufFileToSave[BLOCK_SIZE * 2] = { 0 }; // TODO: Need to alloc on heap and reuse it.

    auto toReadBytes = GetFileSize(m_workOnFile);

    for (;;) // TODO: Need to take out the duplicate code from both locations.
    {
        m_readStream.read((CHAR *)bufFileToOpen, BLOCK_SIZE);
        auto bytesRead = m_readStream.gcount();

        if (0 == bytesRead) {
            N2S_THROW("Error reading the file " + GetStringFromWstring(m_workOnFile));
        }

        toReadBytes -= bytesRead;

        if (0 != toReadBytes && bytesRead == BLOCK_SIZE) {
            GetCryptStatus(BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, (ULONG)bytesRead, &bytesToSave, 0));
            m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
            continue;
        }

        // Reading the last byte
        if (0 != BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, nullptr, 0, &bytesToSave, BCRYPT_BLOCK_PADDING)) {
            N2S_THROW("BCryptEncrypt::Error receiving the size required for the ciphertext.");
        }

        GetCryptStatus(BCryptEncrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, bytesToSave, &bytesToSave, BCRYPT_BLOCK_PADDING));
        m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
        return; // Last block done.
    }
}

auto AesCrypt::ProcessDecryptFile()->void
{
    DWORD bytesToSave = 0;
    UCHAR bufFileToOpen[BLOCK_SIZE] = { 0 };
    UCHAR bufFileToSave[BLOCK_SIZE * 2] = { 0 }; // TODO: Need to alloc on heap and reuse it.

    auto toReadBytes = GetFileSize(m_workOnFile);

    for (;;)
    {
        m_readStream.read((CHAR *)bufFileToOpen, BLOCK_SIZE);
        auto bytesRead = m_readStream.gcount();

        if (0 == bytesRead) {
            N2S_THROW("Error reading the file " + GetStringFromWstring(m_workOnFile));
        }

        toReadBytes -= bytesRead;

        if (0 != toReadBytes && bytesRead == BLOCK_SIZE) {
            GetCryptStatus(BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, (ULONG)bytesRead, &bytesToSave, 0));
            m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
            continue;
        }

        // Reading last block data
        if (0 != BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, nullptr, 0, &bytesToSave, BCRYPT_BLOCK_PADDING)) {
            N2S_THROW("BCryptEncrypt::Error receiving the size required for the ciphertext.");
        }

        GetCryptStatus(BCryptDecrypt(m_keyHandle, bufFileToOpen, (ULONG)bytesRead, nullptr, nullptr, 0, bufFileToSave, bytesToSave, &bytesToSave, BCRYPT_BLOCK_PADDING));
        m_writeStream.write((CHAR *)bufFileToSave, bytesToSave);
        return; // Last block done.
    }
}

person Dev    schedule 23.07.2018    source источник
comment
Если я правильно помню, макрос BCRYPT_SUCCESS оценивает true как неотрицательное. Таким образом, вы можете попробовать заменить 0 != status макросом !BCRYPT_SUCCESS(status), а затем посмотреть, что произойдет. Кроме того, вы также можете взглянуть на этот ответ =› Есть ли примеры выполнения шифрования AES-XTS? с использованием СПГ?   -  person sandthorn    schedule 23.07.2018
comment
Я попробовал макрос BCRYPT_SUCCESS, но столкнулся с той же проблемой. Я уже пробовал данную ссылку, но я не мог получить много информации по этой ссылке. Любая другая помощь очень ценится.   -  person Dev    schedule 24.07.2018
comment
STATUS_INVALID_PARAMETER означает, что вы можете ввести неверные типы переменных. Убедитесь, что ваш m_keyHandle имеет тип BCRYPT_KEY_HANDLE, а SYMM_KEY_SIZE_SECRET имеет тип ULONG и меньше или равен m_encrptKey.size(). Я попытался скомпилировать Win10SDK v10.0.16299.0, и функция BCryptGenerateSymmetricKey возвращает BCRYPT_SUCCESS status =› godbolt. document =› BCryptGenerateSymmetricKey   -  person sandthorn    schedule 31.07.2018
comment
Может быть, вам нужно использовать другого провайдера? У другого человека были проблемы с BCRYPT_AES_GMAC_ALGORITHM, но я так и не понял почему. Также см. Как использовать AES-GMAC с секретом в BCrypt? и Как определить, какой из 23 параметров является STATUS_INVALID_PARAMETER?   -  person jww    schedule 19.08.2019


Ответы (1)


В приведенном выше методе AesCrypt::CreateAESProviderAlgo убедитесь, что SYMM_KEY_SIZE_SECRET равно 32 и что соответствующая строка также имеет длину 32 байта.

Копирование и вставка вашей функции в мою тестовую программу сработало для меня, когда SYMM_KEY_SIZE_SECRET было указано как 32. Это не сработало, когда я указал SYMM_KEY_SIZE_SECRET значение 16.

Кроме того, этот алгоритм поддерживается только в Windows 10 и выше.

Дополнительная информация:

XTS — это вариант AES с двумя ключами. Также требуется размер блока сообщения (размер сектора) и номер сектора.

Я использовал BCryptGenerateSymmetricKey для создания ключа. Я взял входную «строку» либо из 32 байтов (2 16-байтовых ключа), либо из 64 байтов (2 32-байтовых ключа), чтобы сгенерировать ключи из «строки». Никакие другие значения не принимались.

Размер блока сообщения задается параметром BCRYPT_MESSAGE_BLOCK_LENGTH в дескрипторе ключа. Он не обязательно должен быть целым числом, кратным длине блока AES — в этом разница между XTS и XEX.

Номер сектора предоставляется как значение IV в API-интерфейсах шифрования/дешифрования. Для меня была принята только длина 8 для IV.

Фрагмент рабочего кода:

    BCRYPT_ALG_HANDLE m_aesHandle;
    auto status = BCryptOpenAlgorithmProvider(&m_aesHandle, /* BCRYPT_XTS_AES_ALGORITHM */ L"XTS-AES", nullptr, 0);
    if (0 != status)
    {
        csMsg.Format(TEXT("\r\nBCryptException::Failed to get provider for BCRYPT_XTS_AES_ALGORITHM. Error: %d"), status);
        csText += csMsg;
    }
    else
    {
        csMsg.Format(TEXT("\r\nBCryptOpenAlgorithmProvider XTS SUCCESS!"));
        csText += csMsg;

        DWORD cbData = 0;
        DWORD cbKeyObject = 0;
        status = BCryptGetProperty(m_aesHandle, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0);
        if (0 != status)
        {
            csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_OBJECT_LENGTH return error %d (0x%x)"), status, status);
            csText += csMsg;
        }
        else
        {
            csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_OBJECT_LENGTH returned object length %u"), cbKeyObject);
            csText += csMsg;

            PBYTE m_pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
            if (nullptr == m_pbKeyObject)
            {
                csMsg.Format(TEXT("\r\nMemory allocation failed."));
                csText += csMsg;
            }
            else
            {
                BCRYPT_KEY_HANDLE m_keyHandle;
                CHAR String[] = "1234567890abcdef01234567890abcdef1234567890abcdef01234567890abcdef";
                static ULONG uStringLen = 32;
                BYTE Dummy[128] = { 0 };

                status = BCryptGenerateSymmetricKey(m_aesHandle, &m_keyHandle, m_pbKeyObject, cbKeyObject, (PUCHAR)String, uStringLen, 0);
                if (0 != status)
                {
                    csMsg.Format(TEXT("\r\nBCryptGenerateSymmetricKey return with error %d (0x%x)"), status, status);
                    csText += csMsg;
                }
                else
                {
                    csMsg.Format(TEXT("\r\nBCryptGenerateSymmetricKey SUCCESS!"));
                    csText += csMsg;

                    DWORD cbKey = 0;
                    status = BCryptGetProperty(m_keyHandle, BCRYPT_KEY_LENGTH, (PBYTE)&cbKey, sizeof(DWORD), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_KEY_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_KEY_LENGTH returned key length %u"), cbKey);
                        csText += csMsg;
                    }

                    DWORD cbBlock = 0;
                    status = BCryptGetProperty(m_aesHandle, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlock, sizeof(DWORD), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_BLOCK_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptGetProperty BCRYPT_BLOCK_LENGTH returned key block length %u"), cbBlock);
                        csText += csMsg;
                    }

                    DWORD cbMessage = 512;
                    status = BCryptSetProperty(m_keyHandle, /* BCRYPT_MESSAGE_BLOCK_LENGTH */ L"MessageBlockLength", (PBYTE)&cbMessage, sizeof(DWORD), 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptSetProperty BCRYPT_MESSAGE_BLOCK_LENGTH return error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptSetProperty BCRYPT_MESSAGE_BLOCK_LENGTH SUCCESS message length %u"), cbMessage);
                        csText += csMsg;
                    }

                    BYTE PlaintextBuf[512], CipherTextBuf[512], DecryptedTextBuf[512];
                    int i;

                    for (i = 0; i < _countof(PlaintextBuf); i++)
                    {
                        PlaintextBuf[i] = String[i % _countof(String)];
                    }

                    BYTE IV[] = 
                    { 
                         0,  1,  2,  3,  4,  5,  6, 7, 
                         8,  9, 10, 11, 12, 13, 14, 15, 
                        16, 17, 18, 19, 20, 21, 22, 23, 
                        24, 25, 26, 27, 28, 29, 30, 31, 
                    };
                    static DWORD dwIVLen = 8;

                    status = BCryptEncrypt(m_keyHandle, PlaintextBuf, sizeof(PlaintextBuf), nullptr, 
                        IV, dwIVLen, CipherTextBuf, sizeof(CipherTextBuf), &cbData, 0);
                    if (0 != status)
                    {
                        csMsg.Format(TEXT("\r\nBCryptEncrypt return with error %d (0x%x)"), status, status);
                        csText += csMsg;
                    }
                    else
                    {
                        csMsg.Format(TEXT("\r\nBCryptEncrypt SUCCESS!"));
                        csText += csMsg;

                        status = BCryptDecrypt(m_keyHandle, CipherTextBuf, sizeof(CipherTextBuf), nullptr,
                            IV, dwIVLen, DecryptedTextBuf, sizeof(DecryptedTextBuf), &cbData, 0);
                        if (0 != status)
                        {
                            csMsg.Format(TEXT("\r\nBCryptDecrypt return with error %d (0x%x)"), status, status);
                            csText += csMsg;
                        }
                        else
                        {
                            csMsg.Format(TEXT("\r\nBCryptDecrypt SUCCESS!"));
                            csText += csMsg;

                            bool bGood = true;
                            for (i = 0; i < _countof(PlaintextBuf); i++)
                            {
                                if (PlaintextBuf[i] != DecryptedTextBuf[i])
                                {
                                    bGood = false;
                                    break;
                                }
                            }

                            if (bGood)
                                csMsg.Format(TEXT("\r\nCrypt Verification SUCCESS!"));
                            else
                                csMsg.Format(TEXT("\r\nCrypt Verification FAILURE, pos=%u!"), i);
                            csText += csMsg;
                        }
                    }

                    BCryptDestroyKey(m_keyHandle);
                }

                HeapFree(GetProcessHeap(), 0, m_pbKeyObject);
            }
        }

        BCryptCloseAlgorithmProvider(m_aesHandle, 0);
    }
person David C    schedule 05.09.2019