英文:
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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论