英文:
How to decrypt an AES-256 encrypted zip file using Crypto API
问题
我理解你在尝试在Delphi中使用TZipFile和Microsoft Crypto来解压AES-256加密的文件,但是出现了坏数据错误。根据你提供的代码,问题可能出在以下几个方面:
-
缺少盐值 (Salt Value): 根据Winzip文档,AES-256加密需要一个16字节的盐值,通常是文件的前16字节。你可能需要修改代码以包括这个盐值。
-
缺少1000次迭代的密钥生成:根据要求,需要进行1000次迭代的密钥生成。你可能需要查找相关的Crypto API函数来实现这一点。
-
Crypto API的正确使用:确保你正确地使用了Crypto API函数,例如
CryptAcquireContext、CryptCreateHash、CryptHashData、CryptDeriveKey、CryptDecrypt等。 -
错误处理:检查每个Crypto API函数调用是否返回了错误,如果有错误,应该正确地处理它们。
请注意,在修改代码之前,务必备份你的数据,以免意外丢失。希望这能帮到你解决问题。
英文:
I am attempting to decompression AES-256 encrypted files in Delphi using TZipFile and Microsoft Crypto. It always returns a bad data error and returns gibberish.
Background...
I am using the standard TZipFile class to open the zip, and have registered my compression handler as follows:
const zcAESEncrypted = 99;
RegisterCompressionHandler( TZipCompression(zcAESEncrypted), nil, DecompressStream );
Which uses the following method:
function DecompressStream(InStream: TStream; const ZipFile: TZipFile; const Item: TZipHeader): TStream;
var
LStream : TStream;
isEncrypted: Boolean;
begin
isEncrypted := (Item.Flag and 1) = 1;
if isEncrypted and (ZipFile is TEncryptedZipFile) and ( Item.CompressionMethod = zcAESEncrypted ) then
LStream := AESDecryptStream(InStream, TEncryptedZipFile(ZipFile).Password, Item.CompressedSize )
else
LStream := InStream;
Result := TZDecompressionStream.Create(LStream, -15, LStream <> InStream);
end;
The code for the AES decryption is as follows:
function AESDecryptStream(InStream: TStream; const Password: string; FileSize: Integer): TStream;
const
AES_KEY_SIZE = 16 ;
IN_CHUNK_SIZE = AES_KEY_SIZE * 10; // a buffer must be a multiple of the key size
var
chunkSize: Integer;
hProv: HCRYPTPROV;
hHash: HCRYPTHASH;
hKey: HCRYPTKEY;
lpBuffer: PByte;
len, keySize: Int64;
fsOut : TFileStream;
keyStr: WideString;
isFinal: BOOL;
outLen: Integer;
readTotalSize: Integer;
begin
keyStr := WideString(Password);
len := lstrlenW( PWideChar( keyStr ));
keySize := len * sizeof(WideChar); // size in bytes
// Acquire a CryptoAPI context
if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) then
RaiseLastOSError;
// Create a hash object
if not CryptCreateHash(hProv, CALG_SHA_256, 0, 0, @hHash) then
RaiseLastOSError;
// Hash the encrypted data
if not CryptHashData(hHash, @keyStr, keySize, 0) then
RaiseLastOSError;
// Create a key object from the password
if not CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, @hKey) then
RaiseLastOSError;
Result := TMemoryStream.Create();
fsOut := TFileStream.Create( 'C:\Temp\Stream_Out.txt', fmCreate); // For debugging only
lpBuffer := AllocMem(IN_CHUNK_SIZE);
isFinal := FALSE;
readTotalSize := 0;
while isFinal = False do
begin
chunkSize := imin( IN_CHUNK_SIZE, FileSize - readTotalSize );
outLen := InStream.ReadData( lpBuffer, chunkSize );
if outLen = 0 then
break;
readTotalSize := readTotalSize + outLen;
if readTotalSize >= FileSize then
isFinal := TRUE;
if not CryptDecrypt(hKey, 0, isFinal, 0, lpBuffer, @outLen) then
RaiseLastOSError; // Project Project1.exe raised exception class EOSError with message 'System Error. Code: -2146893819. Bad Data.'
fsOut.Write( lpBuffer, outLen);
Result.Write( lpBuffer, outLen );
end;
FreeMem( lpBuffer );
fsOut.Free;
// Release the hash object
CryptDestroyHash(hHash);
// Release the key object
CryptDestroyKey(hKey);
// Release the CryptoAPI context
CryptReleaseContext(hProv, 0);
end;
This code is based on:
https://gist.github.com/hasherezade/2860d94910c5c5fb776edadf57f0bef6
Which, while it seems a bit dubious, was the only example I could find and does claim to work (with AES-128).
However, the code raises an error on the CryptDecrypt() function:
> Project Project1.exe raised exception class EOSError with message 'System Error. Code: -2146893819. Bad Data.'
Based on the requirements laid out in the Winzip documentation:
https://www.winzip.com/en/support/aes-encryption/
There are some obvious failings:
-
It lacks a salt value (16 bytes for AES-256), which I take to be the first 16 bytes of the file
-
It does not perform 1000 iterations of the key generation
However, I cannot see how these changes should be implemented using the Crypto API. Potentially, this is not even possible? Any suggestions?
答案1
得分: 2
For AES-256,AES_KEY_SIZE需要是32。你提供的代码使用的是AES-128,它需要一个长度为16的密钥。
此外,你对CryptHashData()的调用是错误的。你需要去掉@,并使用PWideChar进行转换:
CryptHashData(hHash, PWideChar(keyStr), keySize, 0);
另外,并非错误,但你应该使用UnicodeString而不是WideString。由于string是UnicodeString,你根本不需要keyStr变量,直接使用Password即可:
CryptHashData(hHash, PWideChar(Password), ByteLength(Password), 0);
英文:
For AES-256, AES_KEY_SIZE needs to be 32. The code you linked to is using AES-128, which uses a key size of 16.
Also, your call to CryptHashData() is wrong. You need to drop the @ and use a PWideChar cast instead:
CryptHashData(hHash, PWideChar(keyStr), keySize, 0);
Also, not an error, but you really should be using UnicodeString instead of WideString. And since string is UnicodeString, you don't need the keyStr variable at all, just use the Password as-is:
CryptHashData(hHash, PWideChar(Password), ByteLength(Password), 0);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论