【问题标题】:Why, after using 'CryptSetHashParam', can I no longer add data to my MD5 hash object?为什么在使用 'CryptSetHashParam' 后,我不能再向我的 MD5 哈希对象添加数据?
【发布时间】:2010-02-09 10:46:09
【问题描述】:

我正在尝试使用 Microsoft 'Crypt...' 函数从添加到散列对象的数据中生成 MD5 散列密钥。我还尝试使用“CryptSetHashParam”将哈希对象设置为特定的哈希值,然后再向其中添加数据。

根据 Microsoft 文档(如果我的解释正确),您应该能够通过创建原始对象的重复哈希来做到这一点,使用“CryptGetHashParam”函数检索哈希大小,然后使用“CryptSetHashParam”在原始对象上相应地设置哈希值。我知道在使用 'CryptGetHashParam' 后,您无法将其他数据添加到哈希对象(这就是我认为您需要创建副本的原因),但我无法将数据添加到原始哈希对象或副本使用“CryptGetHashParam”(如预期)或“CryptSetHashParam”(我没想到)后的哈希对象。

以下是我正在编写的类的代码摘录以及我如何使用类函数的示例:

我运行代码后得到的结果是:

“AddDataToHash 函数失败 - 错误代码:2148073484。”,翻译为:“哈希在指定状态下无效。”。

我尝试了许多不同的方法来尝试使其按预期工作,但结果始终相同。我承认我做错了什么,但我看不出我做错了什么。请问有什么想法吗?

类构造函数初始化。

CAuthentication::CAuthentication()

{

    m_dwLastError = ERROR_SUCCESS;

    m_hCryptProv = NULL;

    m_hHash = NULL;

    m_hDuplicateHash = NULL;

    if(!CryptAcquireContext(&m_hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))

    {
        m_dwLastError = GetLastError();

        if (m_dwLastError == 0x80090016 )
        {
            if(!CryptAcquireContext(&m_hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) 
            {
                 m_dwLastError = GetLastError();

                 m_hCryptProv = NULL;
            }
         }
    }

    if(!CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash))
    {
        m_dwLastError = GetLastError();

        m_hHash = NULL;
    }
}

用于设置散列对象的散列值的函数。

bool CAuthentication::SetHashKeyString(char* pszKeyBuffer)

{

    bool bHashStringSet = false;

    DWORD dwHashSize = 0;
    DWORD dwHashLen = sizeof(DWORD);

    BYTE byHash[DIGITAL_SIGNATURE_LENGTH / 2]={0};

    if(pszKeyBuffer != NULL && strlen(pszKeyBuffer) == DIGITAL_SIGNATURE_LENGTH)
    {
        if(CryptDuplicateHash(m_hHash, NULL, 0, &m_hDuplicateHash))
        {
            if(CryptGetHashParam(m_hDuplicateHash, HP_HASHSIZE, reinterpret_cast<BYTE*>(&dwHashSize), &dwHashLen, 0))
            {        
                if (dwHashSize == DIGITAL_SIGNATURE_LENGTH / 2)
                {
                    char*pPtr = pszKeyBuffer;

                    ULONG ulTempVal = 0;

                    for(ULONG ulIdx = 0; ulIdx < dwHashSize; ulIdx++)
                    {
                        sscanf(pPtr, "%02X", &ulTempVal);

                        byHash[ulIdx] = static_cast<BYTE>(ulTempVal);

                        pPtr+= 2;
                    }

                    if(CryptSetHashParam(m_hHash, HP_HASHVAL, &byHash[0], 0)) 
                    {
                        bHashStringSet = true;
                    }
                    else
                    {
                        pszKeyBuffer = "";
                        m_dwLastError = GetLastError();
                    }
                }
            }
            else
            {
                m_dwLastError = GetLastError();
            }
        }
        else
        {
            m_dwLastError = GetLastError();
        }
    }

    if(m_hDuplicateHash != NULL)
    {
        CryptDestroyHash(m_hDuplicateHash);
    }  

    return bHashStringSet;
}

用于添加散列数据的函数。

bool CAuthentication::AddDataToHash(BYTE* pbyHashBuffer, ULONG ulLength)

{

    bool bHashDataAdded = false;

    if(CryptHashData(m_hHash, pbyHashBuffer, ulLength, 0))
    {
        bHashDataAdded = true;
    }
    else
    {
        m_dwLastError = GetLastError();
    }

    return bHashDataAdded;
}

主函数类用法:

CAuthentication auth;

.....

auth.SetHashKeyString("0DD72A4F2B5FD48EF70B775BEDBCA14C");

.....

if(!auth.AddDataToHash(pbyHashBuffer, ulDataLen))

{

    TRACE("CryptHashData function failed - Errorcode: %lu.\n", auth.GetAuthError());
}

【问题讨论】:

    标签: c++ cryptography md5 hash


    【解决方案1】:

    你不能这样做,因为它没有任何意义。带有 HP_HASHVAL 选项的 CryptGetHashParam 最终确定了散列,因此无法向其中添加数据。如果你想“分叉”散列,以便在某个时候完成它并向其中添加数据,则必须在完成之前复制散列对象。然后将数据添加到其中一个哈希对象并最终确定另一个。例如,如果您想在数据流的每 1024 个字节后记录一个累积散列,您可以这样做。您不应该在您继续向其中添加数据的哈希对象上调用 CryptSetHashParam。

    带有 HP_HASHVAL 选项的 CryptSetHashParam 是一种用于克服 CryptoAPI 限制的残酷破解。 CryptoAPI 只会对哈希对象进行签名,因此如果您想对一些可能在 CAPI 之外进行哈希处理或生成的数据进行签名,您必须将其“塞入”哈希对象中。

    编辑:
    根据您的评论,我认为您正在寻找一种序列化哈希对象的方法。我找不到任何证据表明 CryptoAPI 支持这一点。但是,还有其他选择,它们基本上是我上面的“1024 字节”示例的变体。如果您正在对一系列文件进行哈希处理,您可以简单地计算并保存每个文件的哈希值。如果您确实需要将其归结为一个值,则可以计算修改后的哈希,其中您为文件 i 哈希的第一条数据是文件 0、1、2 的最终哈希。 ..,-1。所以:
    H<sub>-1</sub> = <i>empty</i>,
    H<sub>i</sub> = MD5 (H<sub>i-1</sub> || file<sub>i</sub>)

    随着您的进行,您可以保存上次成功计算的H<sub>i</sub> 值。如果出现中断,您可以在文件i+1 处重新启动。请注意,与任何消息摘要一样,上述内容对顺序和内容都是完全敏感的。这是在动态变化的文件系统上要考虑的事情。如果在散列操作期间可以添加或更改文件,则散列值的含义将受到影响。它可能会变得毫无意义。您可能希望确定您正在散列的文件的序列 内容在整个散列期间都被冻结。

    【讨论】:

    • 我想我应该解释一下为什么我要尝试使用预定义的起始哈希值初始化哈希对象以突出我要克服的问题。我正在散列的数据来自可能包含数百个文件的 NAS。在对每个文件中的数据进行哈希处理后,我希望在本地保留 MD5 哈希,以便如果哈希应用程序遇到中断并重新启动,则在中断点使用哈希值初始化一个新的哈希对象,从而避免需要再次从 NAS 的开头开始散列。有没有办法做到这一点?
    • 如果没有更多的文件被添加到 NAS 并且对所有文件中包含的所有数据进行了 MD5 散列而不中断,如果所有数据都被散列,得到的散列值不会不同再次,但这次遇到了中断。如果我正确地解释了之前所说的内容,那么您建议将在中断之前计算的散列值在下一个文件数据之前输入到散列对象中。这不会导致在散列数据上计算散列,从而产生与不间断散列不同的最终散列值吗?
    • 您跟踪最后一个成功散列的文件。当哈希文件 i+1 中间发生中断时,您只需丢弃部分结果并重新开始,重新计算 MD5(H_i || file_{i+1}) = H_{i+1}。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 2012-05-02
    相关资源
    最近更新 更多