【问题标题】:How do I use AES-GMAC with a secret in BCrypt?如何在 BCrypt 中使用带有秘密的 AES-GMAC?
【发布时间】:2019-12-18 18:13:12
【问题描述】:

我正在尝试将 AES-GMAC 与 BCrypt(如果有帮助的话,Microsoft CNG)一起使用,但文档似乎与我的预期相矛盾。

RFC 4543 声明 AES-GMAC 需要一个密钥(这是我所期待的),但 BCryptCreateHash function 声明除非提供 BCRYPT_ALG_HANDLE_HMAC,否则不会使用 pbSecret

我尝试在BcryptOpenAlgorithmProvider 上使用BCRYPT_ALG_HANDLE_HMAC,正如我所料,当使用BCRYPT_AES_GMAC_ALGORITHMBCRYPT_ALG_HANDLE_HMAC 时,我得到了NOT_SUPPORTED

我期待的过程是这样的:

BCryptOpenAlgorithmProvider(*with no HMAC flag*)
BCryptCreateHash(*including secret*)
BCryptHashData()
BCryptFinaliseHash()

我是不是误会了什么?

【问题讨论】:

    标签: c++ windows cryptography aes-gcm cng


    【解决方案1】:

    我尝试在BcryptOpenAlgorithmProvider 上使用BCRYPT_ALG_HANDLE_HMAC,正如我所料,当使用BCRYPT_AES_GMAC_ALGORITHMBCRYPT_ALG_HANDLE_HMAC 时,我得到了NOT_SUPPORTED

    BCRYPT_AES_GMAC_ALGORITHM 似乎坏了。不要使用它。

    通过BCRYPT_AES_ALGORITHM 使用 AES/GCM。不要加密任何数据。仅对 aad 进行身份验证。生成的标签是 AAD 上的 GMAC。


    如何在 BCrypt 中使用 AES-GMAC 和密钥?

    创建 GMAC 的步骤是:

    • 获取 AES 算法
    • 设置 GCM 模式
    • 输入密码
    • 设置随机数
    • 验证广告

    “aad”“额外认证的数据”。它不同于传统上加密的纯文本数据。

    以下是 Bcrypt 和 Crypto++ 对 aad 的 MAC 程序。由于您不加密数据,因此对 BCryptEncrypt 的调用如下所示:

    status = BCryptEncrypt(
        hKey,
        NULL, 0,
        (PVOID)&aadInfo,
        NULL, 0,
        NULL, 0,
        &ulWritten,
        0
    );
    

    注意SP800-38D 指定最大随机数为 264-1,但 Microsoft 将随机数限制为 12 个字节。如果您尝试使用 16 字节,即 AES 块大小,则结果为 0xc000000dSTATUS_INVALID_PARAMETER。 Microsoft 文档未能记录这些限制。


    这是 Microsoft Bcrypt 程序。

    #include <Windows.h>
    #include <bcrypt.h>
    
    #include <iostream>
    #include <iomanip>
    #include <sstream>
    #include <string>
    #include <memory>
    #include <stdexcept>
    
    #pragma comment (lib, "bcrypt.lib")
    
    std::string NtStatusToString(const CHAR* operation, NTSTATUS status)
    {
        std::ostringstream oss;
        oss << operation << ", 0x" << std::hex << status;
    
        switch(status)
        {
            case 0xc0000000:
                oss << " (STATUS_SUCCESS)";
                break;
            case 0xC0000008:
                oss << " (STATUS_INVALID_HANDLE)";
                break;
            case 0xc000000d:
                oss << " (STATUS_INVALID_PARAMETER)";
                break;
            case 0xc00000bb:
                oss << " (STATUS_NOT_SUPPORTED)";
                break;
            case 0xC0000225:
                oss << " (STATUS_NOT_FOUND)";
                break;
        }
        return oss.str();
    }
    
    std::string ArrayToHexString(const UCHAR arr[], size_t size)
    {
        std::ostringstream oss;
        for (size_t i=0; i<size; ++i)
        {
            oss << std::hex << std::setw(2) << std::setfill('0');
            oss << (unsigned int)arr[i];
        }
        return oss.str();
    }
    
    int main(int argc, char* argv[])
    {
        BCRYPT_ALG_HANDLE hAlgorithm = 0;
        BCRYPT_KEY_HANDLE hKey = 0;
        UCHAR* pbKeyObject = 0;
        ULONG cbKeyObjectLength = 0;
    
        // SP800-38D specifies max nonce of 2^64-1, but
        // Microsoft limits the nonce to 12 bytes.
    
        UCHAR key[16] = {0};
        UCHAR  iv[12] = {0};
        UCHAR tag[16] = {0};
    
        // The data to be GMAC'd. It is not encrypted.
        std::string aad("Not so secret additionally authenticated data");
    
        try
        {
            NTSTATUS status = 0;
            ULONG ulWritten = 0;
    
            ////////////////////////////////////////
    
            status = BCryptOpenAlgorithmProvider(
                &hAlgorithm,
                BCRYPT_AES_ALGORITHM,
                NULL, 0
            );
    
            if (!BCRYPT_SUCCESS(status))
                throw std::runtime_error(
                    NtStatusToString("BCryptOpenAlgorithmProvider", status));
    
            status = BCryptSetProperty(
                hAlgorithm,
                BCRYPT_CHAINING_MODE,
                (UCHAR*)BCRYPT_CHAIN_MODE_GCM,
                sizeof(BCRYPT_CHAIN_MODE_GCM),
                0
            );
    
            if (!BCRYPT_SUCCESS(status))
                throw std::runtime_error(
                    NtStatusToString("BCryptSetProperty (BCRYPT_CHAINING_MODE)", status));
    
            ////////////////////////////////////////
    
            status = BCryptGetProperty(
                hAlgorithm,
                BCRYPT_OBJECT_LENGTH,
                (PUCHAR)&cbKeyObjectLength,
                sizeof(cbKeyObjectLength),
                &ulWritten,
                0
            );
    
            if (!BCRYPT_SUCCESS(status))
                throw std::runtime_error(
                    NtStatusToString("BCryptGetProperty (BCRYPT_OBJECT_LENGTH)", status));
    
            pbKeyObject = new UCHAR[cbKeyObjectLength];
    
            if (!pbKeyObject)
                throw std::runtime_error("pbKeyObject");
    
            ////////////////////////////////////////
    
            status = BCryptGenerateSymmetricKey(
                hAlgorithm,
                &hKey,
                pbKeyObject,
                cbKeyObjectLength,
                key,
                sizeof(key),
                0
            );
    
            if (!BCRYPT_SUCCESS(status))
                throw std::runtime_error(
                    NtStatusToString("BCryptGenerateSymmetricKey", status));
    
            ////////////////////////////////////////
    
            BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aadInfo;
            BCRYPT_INIT_AUTH_MODE_INFO(aadInfo);
    
            aadInfo.pbNonce = iv;
            aadInfo.cbNonce = sizeof(iv);
    
            // Awful API design; non-const pointer.
            aadInfo.pbAuthData = reinterpret_cast<UCHAR*>(&aad[0]);
            aadInfo.cbAuthData = static_cast<ULONG>(aad.size());
            aadInfo.cbAAD = static_cast<ULONG>(aad.size());
    
            aadInfo.pbTag = tag;
            aadInfo.cbTag = sizeof(tag);
    
            ////////////////////////////////////////
    
            status = BCryptEncrypt(
                hKey,
                NULL, 0,
                (PVOID)&aadInfo,
                NULL, 0,
                NULL, 0,
                &ulWritten,
                0
            );
    
            if (!BCRYPT_SUCCESS(status))
                throw std::runtime_error(
                    NtStatusToString("BCryptEncrypt", status));
    
            std::cout << "Message: " << aad << std::endl;
            std::cout << "GMAC: " << ArrayToHexString(tag, sizeof(tag));
        }
        catch (const std::exception& ex)
        {
            std::cerr << "Exception: " << ex.what() << std::endl;
        }
    
        if (hKey)
            BCryptDestroyKey(hKey);
    
        if (hAlgorithm)
            BCryptCloseAlgorithmProvider(hAlgorithm, 0);
    
        // Destroy after handles
        if (pbKeyObject)
            delete [] pbKeyObject;
    
        return 0;
    }
    

    这是结果。

    >cl.exe /DWINVER=0x0600 /TP /GR /EHsc bcrypt-gmac.cpp /link
    Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24210 for x64
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    bcrypt-gmac.cpp
    Microsoft (R) Incremental Linker Version 14.00.24210.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:bcrypt-gmac.exe
    bcrypt-gmac.obj
    
    >.\bcrypt-gmac.exe
    Message: Not so secret additionally authenticated data
    GMAC: 3a1158d288cd796899f0366cdf594020
    

    这是Crypto++ 的代码。您可以在BotanOpenSSL 中执行相同操作。

    #include "cryptlib.h"
    #include "filters.h"
    #include "files.h"
    #include "hex.h"
    #include "aes.h"
    #include "gcm.h"
    
    #include <iostream>
    #include <string>
    
    int main(int argc, char* argv[])
    {
        using namespace CryptoPP;
    
        byte key[16] = {0};
        byte  iv[12] = {0};
        byte tag[16] = {0};
    
        std::string aad("Not so secret additionally authenticated data");
    
        try
        {
            GCM< AES >::Encryption enc;
            enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
    
            // AuthenticatedEncryptionFilter defines two
            //   channels: DEFAULT_CHANNEL and AAD_CHANNEL
            //   DEFAULT_CHANNEL is encrypted and authenticated,
            //   AAD_CHANNEL is authenticated.
            AuthenticatedEncryptionFilter ef(enc,
                new ArraySink(tag, sizeof(tag)),
                false, 16 /* Tag size, MAC_AT_END */
            ); // AuthenticatedEncryptionFilter
    
            // Authenticated data *must* be pushed before
            //  Confidential/Authenticated data. Otherwise
            //  we must catch the BadState exception
            ef.ChannelPut(AAD_CHANNEL, (const byte*)aad.data(), aad.size());
            ef.ChannelMessageEnd(AAD_CHANNEL);
    
            // Confidential data comes after authenticated data.
            // This is a limitation due to CCM mode, not GCM mode.
            //ef.ChannelPut(DEFAULT_CHANNEL, pdata.data(), pdata.size());
            //ef.ChannelMessageEnd(DEFAULT_CHANNEL);
    
            // Signal end of message
            ef.MessageEnd();
    
            std::cout << "Message: " << aad << std::endl;
            std::cout << "GMAC: ";
            StringSource(tag, sizeof(tag), true, new HexEncoder(new FileSink(std::cout)));
            std::cout << std::endl;
        }
        catch(CryptoPP::Exception& ex)
        {
            std::cerr << "Exception: " << ex.what() << std::endl;
        }
    
        return 0;
    }
    

    这是结果。

    $ g++ test.cxx ./libcryptopp.a -o test.exe
    
    $ ./test.exe
    Message: Not so secret additionally authenticated data
    GMAC: 3A1158D288CD796899F0366CDF594020
    

    【讨论】:

    • 请注意,NIST[1] 建议使用 Windows API 限制:For IVs, it is recommended that implementations restrict support to the length of 96 bits, to promote interoperability, efficiency, and simplicity of design. [1]nvlpubs.nist.gov/nistpubs/Legacy/SP/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多