【问题标题】:CryptEncrypt AES 256 fails at encrypting last blockCryptEncrypt AES 256 在加密最后一个块时失败
【发布时间】:2012-02-23 20:30:59
【问题描述】:

首先,如果我在这里遗漏了什么,我深表歉意。这是我对 Windows CryptAPI 的第一次尝试,虽然我已经浏览了 google、MSDN、MSDN 的示例等。但我无法弄清楚为什么会出现这个问题。 我有以下代码,它应该复制在特定地址找到的操作码(字节块),加密它们,然后将它们写回。它绝不是一个完整的代码,它几乎没有错误检查。我正在使用 AES256。

bool CryptoClass::encrypt(DWORD address, DWORD len)
{
    DWORD dwBlockLen = 0; 
    DWORD dwBufferLen = 0; 
    DWORD dwCount = 0; 
    PBYTE pbBuffer = NULL;    
    dwBlockLen = AES_BLOCK_SIZE - AES_BLOCK_SIZE % ENCRYPT_BLOCK_SIZE; 
    dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE; 
    if(pbBuffer = (BYTE *)malloc(dwBufferLen))
    {
        bool EOB = FALSE;
        while ( dwCount <= len) )
        {
            memcpy((void*)pbBuffer,(void*)address,dwBlockLen);              
            if ( (len - dwCount) < dwBlockLen) EOB = TRUE;              

            if(CryptEncrypt(hKey,NULL,EOB,0,pbBuffer,&dwBlockLen,dwBufferLen))
            {
                memcpy((void*)address,(void*)pbBuffer,dwBlockLen);
                address += dwBlockLen;                              
                dwCount += dwBlockLen;                              
            }
            else
            {
                error = GetLastError();
                MessageBoxA(NULL,"problem","error",MB_OK);
            }
        }
        free(pbBuffer);
       return true;
    }
    else return false;
}

据我了解,AES 可以加密 16 字节的块,因此 AES_BLOCK_SIZE 定义为 16。 此外,我的 ENCRYPT_BLOCK_SIZE 设置为 8。我基本上是在复制我在 MSDN 上找到的一个示例,并针对 AES256 进行调整并用于内存而不是文件。 然而,在我的 while 循环中,只要它到达缓冲区的末尾,即 FINAL 设置为 TRUE,CryptEncrypt 就会失败。我尝试了许多不同的方法来做到这一点,但总是失败。 是因为最后的缓冲区小于 16bytes 吗? 有人可以帮忙吗,在加密方面我是个菜鸟。 谢谢 编辑:GetLastError 返回:0x000000ea

【问题讨论】:

  • ENCRYPT_BLOCK_SIZE 到底是做什么的?你有指向所用示例的指针吗?

标签: c++ winapi encryption


【解决方案1】:

您收到错误 ERROR_MORE_DATA。翻译成:

如果为 pbData 分配的缓冲区不足以容纳加密数据,GetLastError 将返回 ERROR_MORE_DATA 并将所需的缓冲区大小(以字节为单位)存储在 pdwDataLen 指向的 DWORD 值中。

如果该方法使用 CBC 加密(Microsoft 并不在其 API 中指定模式或填充算法),则应执行以下算法来计算缓冲区的输出大小:

buffer_size = (plain_size_bytes / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;

换句话说,如果 plain_size_bytes 是 N * AES_BLOCK_SIZE 你仍然需要一个完整的填充块。这是因为你总是需要执行填充,否则 unpadding 算法无法区分你的纯文本和填充字节。

当然,您也可以简单地创建一个 plain_size_bytes + AES_BLOCK_SIZE 的缓冲区,并使用 pdwDataLen 值来获取实际长度。

编辑:如果您分别加密每个纯文本块,并且密码使用带有填充的 CBC 或 ECB,那么由于上述原因,最后一个纯文本块可能需要 2 个完整块

【讨论】:

  • 谢谢,确实如此。我只是使用了更大的缓冲区和块大小并解决了它。显然我还必须: CryptSetKeyParam(hKey, KP_MODE,(BYTE*)&dwMode, NULL);这设置了欧洲央行模式。谢谢你的解释:)
  • ECB 模式加密在几乎所有情况下都是不安全的。它可以使用与 CBC 相同的填充机制(大部分时间使用 PKCS#7 填充),因此答案对两者都有效。使用 CBC 而不是 ECB,并在密文前加上随机 IV 以确保安全(r)。
  • 我花了 3 天时间试图弄清楚为什么使用 ECB 时密文无法解密。切换到 CBC 它在第一次尝试时工作。我希望我早点读过你的评论,这样就省去了我的麻烦……
【解决方案2】:
while ( address < (address + len) )

我的神秘答案是“考虑一下——你不会一直考虑它,但是...”

【讨论】:

  • 是的,我刚刚意识到,太累了,无法发现这一点。谢谢 !然而,这并不能解决为什么 CryptEncrypt 在最后一个块失败的问题。
  • 它可能已经解释了为什么 CryptEncrypt 在最后一个块之后失败,但在您编辑问题之后就没有了。
【解决方案3】:
if ( (len - dwCount) < dwBlockLen) EOB = TRUE;

也许应该是这样的

if ( (len - dwCount) < dwBlockLen)
{
    EOB = TRUE;
    dwBlockLen = ENCRYPT_BLOCK_SIZE;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-06
    • 2018-05-17
    相关资源
    最近更新 更多