Python中有重复的Windows加密服务提供程序结果/Pycryp

2024-04-24 07:14:52 发布

您现在位置:Python中文网/ 问答频道 /正文

编辑和更新

2013年3月24日:
在转换为utf-16之后,我从Python输出的散列与c++中的散列匹配,并在命中任何“e”或“m”字节之前停止。但是解密的结果不匹配。我知道我的SHA1散列值是20字节=160位,而RC4密钥的长度可以从40位到2048位不等,所以我可能需要在WinCrypt中模拟一些默认的salt。CryptGetKeyParam KP_长度或KP_SALT

2013年3月24日:
CryptGetKeyParam KP_LENGTH告诉我我的密钥长度是128位。我给它输入一个160位的哈希值。所以也许它只是丢弃最后的32位…或4个字节。正在测试。在

2013年3月24日: 是的,就是这样。如果我在python中丢弃SHA1散列的最后4个字节…我得到相同的解密结果。在

快速信息:

我有一个c++程序来解密一个数据块。它使用Windows Crytographic服务提供程序,因此只能在Windows上运行。我希望它能与其他平台一起工作。在

方法概述:

在Windows Crypto API中 一个字节的ASCII编码密码被转换成一个宽字符表示,然后用SHA1散列,以生成RC4流密码的密钥。在

在Python PyCrypto中 ASCII编码的字节字符串被解码为python字符串。它基于经验上观察到的字节被截断,这会导致mbctowcs停止在c++中的转换。然后,这个被截断的字符串以utf-16进行enocoded,在字符之间有效地填充了0x00字节。这个新的截断、填充的字节字符串被传递到SHA1散列,摘要的前128位传递给PyCrypto RC4对象。在

问题[已解决]
使用python3.xw/PyCrypto似乎无法得到相同的结果

<强> C++代码骨架:< /强>

HCRYPTPROV hProv      = 0x00;
HCRYPTHASH hHash      = 0x00;
HCRYPTKEY  hKey       = 0x00;
wchar_t    sBuf[256]  = {0};

CryptAcquireContextW(&hProv, L"FileContainer", L"Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18u, 0);

CryptCreateHash(hProv, 0x8004u, 0, 0, &hHash);
//0x8004u is SHA1 flag

int len = mbstowcs(sBuf, iRec->desc, sizeof(sBuf));
//iRec is my "Record" class
//iRec->desc is 33 bytes within header of my encrypted file
//this will be used to create the hash key. (So this is the password)

CryptHashData(hHash, (const BYTE*)sBuf, len, 0);

CryptDeriveKey(hProv, 0x6801, hHash, 0, &hKey);

DWORD dataLen = iRec->compLen;  
//iRec->compLen is the length of encrypted datablock
//it's also compressed that's why it's called compLen

CryptDecrypt(hKey, 0, 0, 0, (BYTE*)iRec->decrypt, &dataLen);
// iRec is my record that i'm decrypting
// iRec->decrypt is where I store the decrypted data
//&dataLen is how long the encrypted data block is.
//I get this from file header info

Python代码框架:

^{pr2}$

疑似[编辑:确认]原因
1mbstowcs对密码的转换导致将“原始数据”馈送到SHA1散列中的结果与python和c++中的不一样。mbstowcs正在停止0x65和0x6D字节的转换。原始数据以仅包含原始33字节密码的一部分的宽字符编码结束。在

  1. RC4可以有可变长度的键。在Enhanced Win Crypt服务提供程序中,默认长度为128位。不指定密钥长度是取“原始数据”的160位SHA1摘要的前128位

我是如何调查的 编辑:根据我自己的实验和@RolandSmith的建议,我现在知道我的问题之一是mbctowcs的行为方式出乎我的意料。它似乎停止了对“e”(0x65)和“m”(0x6d)的sBuf写入(可能是其他)。因此,我的描述中的passoword“Monkey”(Ascii编码的字节)看起来像sBuf中的“M o n k”,因为mbstowcs在e处停止,并在系统上基于2字节wchar typedef的字节之间放置0x00。我通过将转换结果写入一个文本文件来发现这一点。在

BYTE pbHash[256];  //buffer we will store the hash digest in 
DWORD dwHashLen;  //store the length of the hash
DWORD dwCount;
dwCount = sizeof(DWORD);  //how big is a dword on this system?


//see above "len" is the return value from mbstowcs that tells how
//many multibyte characters were converted from the original
//iRec->desc an placed into sBuf.  In some cases it's 3, 7, 9
//and always seems to stop on "e" or "m"

fstream outFile4("C:/desc_mbstowcs.txt", ios::out | ios::trunc | ios::binary);
outFile4.write((const CHAR*)sBuf, int(len));
outFile4.close();

//now get the hash size from CryptGetHashParam
//an get the acutal hash from the hash object hHash
//write it to a file.
if(CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwCount, 0)) {
  if(CryptGetHashParam(hHash, 0x0002, pbHash, &dwHashLen,0)){

    fstream outFile3("C:/test_hash.txt", ios::out | ios::trunc | ios::binary);
    outFile3.write((const CHAR*)pbHash, int(dwHashLen));
    outFile3.close();
  }
}

参考文献:
宽字符会根据环境定义产生问题
Difference in Windows Cryptography Service between VC++ 6.0 and VS 2008

将utf-8转换为utf-16字符串
Python - converting wide-char strings from a binary file to Python unicode strings

PyCrypto RC4示例
https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.ARC4-module.html

Hashing a string with Sha256

http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/aa375599(v=vs.85).aspx


Tags: the字符串from字节is密钥hashsha1
1条回答
网友
1楼 · 发布于 2024-04-24 07:14:52

您可以用一个小的测试程序(用C语言)测试wchar_t的大小:

#include <stdio.h> /* for printf */
#include <stddef.h> /* for wchar_t */

int main(int argc, char *argv[]) {
    printf("The size of wchar_t is %ld bytes.\n", sizeof(wchar_t));
    return 0;
}
<>你也可以使用C++代码中的^ {CD2>}调用,如^ { CD3}}和在{“CD4}}”中的哈希结果,如果你可以从终端运行C++程序。否则,请使用fprintf()将它们转储到文件中。在

<>为了更好地模仿C++程序的行为,你甚至可以使用^{}来调用python代码中的^ {< CD7>}。在

编辑:您写道:

One problem is definitely with mbctowcs. It seems that it's transferring an unpredictable (to me) number of bytes into my buffer to be hashed.

请记住,mbctowcs返回转换后的宽字符数。换句话说,多字节编码中的一个33字节的缓冲区 可以包含从5(UTF-8 6字节序列)到33个字符的任何内容,具体取决于所使用的编码。在

Edit2:您正在使用0作为dwFlagsdwFlags参数。根据它的documentation,上面的16位应该包含密钥长度。您应该检查CryptDeriveKey的返回值,看看调用是否成功。在

Edit3:您可以在Python中测试mbctowcs(我在这里使用的是IPython):

^{pr2}$

请注意,在Windows中,您可能应该使用libc = cdll.msvcrt,而不是{}。在

相关问题 更多 >