RAD Studio 12 Athens如何解压加密Zip文件?求完整代码示例
加密Zip文件提取完整实现示例
Embarcadero官方的System.Zip.TZipFile.Extract示例仅提供了基础框架,缺失加密类型判断与具体解密逻辑。以下补充完整的C++和Delphi实现代码,覆盖传统ZipCrypto与AES-256两种主流Zip加密方式。
C++ 完整实现
主调用代码
#include <System.Zip.hpp> #include <System.Cryptography.hpp> #include <System.SysUtils.hpp> #include <memory> // 存储Zip密码(可根据实际场景动态传入) UnicodeString g_ZipPassword = L"YourSecurePassword"; void ExtractEncryptedZip(const UnicodeString& ZipPath, const UnicodeString& ExtractDir) { std::unique_ptr<TZipFile> myZipFile(new TZipFile); myZipFile->OnCreateDecompressStream = &OnCreateDecompressStream; myZipFile->Open(ZipPath, zmRead); myZipFile->ExtractAll(ExtractDir); myZipFile->Close(); }
解密流创建回调完整实现
TStream* __fastcall OnCreateDecompressStream(TStream* const InStream, TZipFile* const ZipFile, const TZipHeader& Item, bool IsEncrypted) { if (!IsEncrypted) return InStream; try { // 通过TZipHeader的EncryptionMethod字段判断加密类型 switch (Item.EncryptionMethod) { // 传统ZipCrypto加密(PKWARE标准) case 0: return CreateZipCryptoDecryptStream(InStream, g_ZipPassword, Item); // AES-256加密(WinZip兼容格式) case 99: return CreateAESDecryptStream(InStream, g_ZipPassword, Item); default: throw Exception(L"不支持的加密类型"); } } catch (Exception& e) { ShowMessage(L"解密失败:" + e.Message); return InStream; } }
ZipCrypto解密流实现
TStream* CreateZipCryptoDecryptStream(TStream* InStream, const UnicodeString& Password, const TZipHeader& Item) { class TZipCryptoDecryptStream : public TCustomMemoryStream { private: TStream* FSourceStream; bool FOwnsSource; uint8_t FKeys[3]; void UpdateKeys(uint8_t Byte); uint8_t DecryptByte(uint8_t Byte); public: __fastcall TZipCryptoDecryptStream(TStream* Source, bool OwnsSource, const UnicodeString& Password); __fastcall ~TZipCryptoDecryptStream(); virtual int __fastcall Read(void *Buffer, int Count); }; // ZipCrypto核心算法实现 __fastcall TZipCryptoDecryptStream::TZipCryptoDecryptStream(TStream* Source, bool OwnsSource, const UnicodeString& Password) : FSourceStream(Source), FOwnsSource(OwnsSource) { // 初始化密钥 FKeys[0] = 0x12; FKeys[1] = 0x34; FKeys[2] = 0x56; for (int i = 0; i < Password.Length(); i++) { UpdateKeys(static_cast<uint8_t>(Password[i])); } // 跳过加密文件头的12字节验证位 uint8_t Dummy[12]; FSourceStream->Read(Dummy, 12); for (int i = 0; i < 12; i++) { DecryptByte(Dummy[i]); } } __fastcall TZipCryptoDecryptStream::~TZipCryptoDecryptStream() { if (FOwnsSource) delete FSourceStream; } void TZipCryptoDecryptStream::UpdateKeys(uint8_t Byte) { FKeys[0] = static_cast<uint8_t>((FKeys[0] + FKeys[1]) * 134775813 + 1); FKeys[1] = static_cast<uint8_t>(FKeys[1] ^ FKeys[0]); FKeys[2] = static_cast<uint8_t>((FKeys[2] + FKeys[1]) * 134775813 + 1); } uint8_t TZipCryptoDecryptStream::DecryptByte(uint8_t Byte) { uint8_t Temp = static_cast<uint8_t>(FKeys[2] | 2); Temp = static_cast<uint8_t>((Temp * (Temp ^ 1)) >> 8); uint8_t Result = Byte ^ Temp; UpdateKeys(Result); return Result; } int __fastcall TZipCryptoDecryptStream::Read(void *Buffer, int Count) { int BytesRead = FSourceStream->Read(Buffer, Count); uint8_t* Ptr = static_cast<uint8_t*>(Buffer); for (int i = 0; i < BytesRead; i++) { Ptr[i] = DecryptByte(Ptr[i]); } return BytesRead; } return new TZipCryptoDecryptStream(InStream, true, Password); }
AES-256解密流实现
TStream* CreateAESDecryptStream(TStream* InStream, const UnicodeString& Password, const TZipHeader& Item) { uint8_t* ExtraField = reinterpret_cast<uint8_t*>(Item.ExtraField.c_str()); int ExtraFieldLen = Item.ExtraField.Length(); int KeySize = 0; uint8_t VerificationCode[2]; // 遍历ExtraField查找WinZip AES标记(0x9901) for (int i = 0; i < ExtraFieldLen - 4; ) { uint16_t Tag = *reinterpret_cast<uint16_t*>(ExtraField + i); uint16_t Len = *reinterpret_cast<uint16_t*>(ExtraField + i + 2); if (Tag == 0x9901) { KeySize = ExtraField[i + 5] * 8; // 256位对应32*8 memcpy(VerificationCode, ExtraField + i + 6, 2); break; } i += 4 + Len; } if (KeySize != 256) throw Exception(L"仅支持AES-256加密"); // 生成AES密钥(遵循Zip AES规范:密码+盐值迭代推导) TAES* AES = new TAES(TAES::ECB); TBytes PasswordBytes = TEncoding::UTF8::GetBytes(Password); TBytes SaltBytes(8); InStream->Read(SaltBytes.data(), 8); TBytes KeyBytes = AES->DeriveKeyFromPassword(PasswordBytes, SaltBytes, 1000, KeySize / 8); // 初始化CBC模式IV(前8字节为盐值,后8字节补0) TBytes IVBytes(16); memcpy(IVBytes.data(), SaltBytes.data(), 8); memset(IVBytes.data() + 8, 0, 8); TCryptoDecryptStream* AESStream = new TCryptoDecryptStream(InStream, true, AES, KeyBytes, IVBytes); // 验证解密后的校验码,确认密码正确性 uint8_t DecryptedCode[2]; AESStream->Read(DecryptedCode, 2); if (memcmp(DecryptedCode, VerificationCode, 2) != 0) { delete AESStream; throw Exception(L"密码错误或文件损坏"); } return AESStream; }
Delphi 完整实现
主调用代码
uses System.Zip, System.Cryptography, System.SysUtils; var g_ZipPassword: string; procedure ExtractEncryptedZip(const ZipPath, ExtractDir: string); var myZipFile: TZipFile; begin myZipFile := TZipFile.Create; try myZipFile.OnCreateDecompressStream := OnCreateDecompressStream; myZipFile.Open(ZipPath, zmRead); myZipFile.ExtractAll(ExtractDir); finally myZipFile.Free; end; end;
解密流创建回调完整实现
function OnCreateDecompressStream(InStream: TStream; ZipFile: TZipFile; const Item: TZipHeader; IsEncrypted: Boolean): TStream; begin if not IsEncrypted then begin Result := InStream; Exit; end; try case Item.EncryptionMethod of 0: Result := CreateZipCryptoDecryptStream(InStream, g_ZipPassword, Item); 99: Result := CreateAESDecryptStream(InStream, g_ZipPassword, Item); else raise Exception.Create('不支持的加密类型'); end; except on E: Exception do begin ShowMessage('解密失败:' + E.Message); Result := InStream; end; end; end;
ZipCrypto解密流实现
type TZipCryptoDecryptStream = class(TCustomMemoryStream) private FSourceStream: TStream; FOwnsSource: Boolean; FKeys: array[0..2] of Byte; procedure UpdateKeys(Byte: Byte); function DecryptByte(Byte: Byte): Byte; public constructor Create(Source: TStream; OwnsSource: Boolean; const Password: string); destructor Destroy; override; function Read(var Buffer; Count: Integer): Integer; override; end; constructor TZipCryptoDecryptStream.Create(Source: TStream; OwnsSource: Boolean; const Password: string); var I: Integer; Dummy: array[0..11] of Byte; begin inherited Create; FSourceStream := Source; FOwnsSource := OwnsSource; // 初始化密钥 FKeys[0] := $12; FKeys[1] := $34; FKeys[2] := $56; for I := 1 to Length(Password) do UpdateKeys(Byte(Password[I])); // 跳过验证头 FSourceStream.Read(Dummy, 12); for I := 0 to 11 do DecryptByte(Dummy[I]); end; destructor TZipCryptoDecryptStream.Destroy; begin if FOwnsSource then FSourceStream.Free; inherited; end; procedure TZipCryptoDecryptStream.UpdateKeys(Byte: Byte); begin FKeys[0] := (FKeys[0] + FKeys[1]) * $8088405 + 1; FKeys[1] := FKeys[1] xor FKeys[0]; FKeys[2] := (FKeys[2] + FKeys[1]) * $8088405 + 1; end; function TZipCryptoDecryptStream.DecryptByte(Byte: Byte): Byte; var Temp: Byte; begin Temp := FKeys[2] or 2; Temp := (Temp * (Temp xor 1)) shr 8; Result := Byte xor Temp; UpdateKeys(Result); end; function TZipCryptoDecryptStream.Read(var Buffer; Count: Integer): Integer; var Ptr: PByte; I: Integer; begin Result := FSourceStream.Read(Buffer, Count); Ptr := @Buffer; for I := 0 to Result - 1 do Ptr^ := DecryptByte(Ptr^); end; function CreateZipCryptoDecryptStream(InStream: TStream; const Password: string; const Item: TZipHeader): TStream; begin Result := TZipCryptoDecryptStream.Create(InStream, True, Password); end;
AES-256解密流实现
function CreateAESDecryptStream(InStream: TStream; const Password: string; const Item: TZipHeader): TStream; var ExtraField: PByte; ExtraFieldLen: Integer; I: Integer; Tag, Len: Word; KeySize: Integer; VerificationCode: array[0..1] of Byte; AES: TAES; PasswordBytes, SaltBytes, KeyBytes, IVBytes: TBytes; AESStream: TCryptoDecryptStream; DecryptedCode: array[0..1] of Byte; begin ExtraField := PByte(Item.ExtraField); ExtraFieldLen := Length(Item.ExtraField); KeySize := 0; FillChar(VerificationCode, SizeOf(VerificationCode), 0); // 查找WinZip AES扩展字段 I := 0; while I <= ExtraFieldLen - 4 do begin Tag := PWord(ExtraField + I)^; Len := PWord(ExtraField + I + 2)^; if Tag = $9901 then begin KeySize := ExtraField[I + 5] * 8; Move(ExtraField[I + 6], VerificationCode, 2); Break; end; Inc(I, 4 + Len); end; if KeySize <> 256 then raise Exception.Create('仅支持AES-256加密'); // 生成密钥 AES := TAES.Create(TAES.ECB); try PasswordBytes := TEncoding.UTF8.GetBytes(Password); SetLength(SaltBytes, 8); InStream.ReadBuffer(SaltBytes[0], 8); KeyBytes := AES.DeriveKeyFromPassword(PasswordBytes, SaltBytes, 1000, KeySize div 8); // 初始化CBC IV SetLength(IVBytes, 16); Move(SaltBytes[0], IVBytes[0], 8); FillChar(IVBytes[8], 8, 0); AESStream := TCryptoDecryptStream.Create(InStream, True, AES, KeyBytes, IVBytes); except AES.Free; raise; end; // 验证校验码 AESStream.ReadBuffer(DecryptedCode, 2); if not CompareMem(@DecryptedCode, @VerificationCode, 2) then begin AESStream.Free; raise Exception.Create('密码错误或文件损坏'); end; Result := AESStream; end;
内容的提问来源于stack exchange,提问作者Barry Andrews
相关产品推荐
相关产品推荐

