我试图使用TZipFile
和Microsoft Crypto在 Delphi 中解压缩AES-256加密文件。它总是返回一个坏数据错误并返回乱码。
背景...
我使用标准的TZipFile
类打开zip,并注册了压缩处理程序,如下所示:
const zcAESEncrypted = 99;
RegisterCompressionHandler( TZipCompression(zcAESEncrypted), nil, DecompressStream );
它使用以下方法:
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;
AES解密的代码如下:
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;
此代码基于:
https://gist.github.com/hasherezade/2860d94910c5c5fb776edadf57f0bef6
虽然这看起来有点可疑,但这是我能找到的唯一一个例子,而且确实声称可以工作(与AES-128)。
但是,代码在CryptDecrypt()
函数上引发了一个错误:
Project Project1.exe引发异常类EOSError,并显示消息“系统错误。代码:-2146893819,数据错误。”
根据Winzip文档中列出的要求:
https://www.winzip.com/en/support/aes-encryption/
有一些明显的缺点:
- 它缺少salt值(AES-256为16字节),我将其作为文件的前16个字节
- 它不执行密钥生成的1000次迭代
然而,我看不出这些变化应该如何使用加密API实现。潜在的,这甚至是不可能的?有什么建议吗?
1条答案
按热度按时间ct2axkht1#
对于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);