golang解壓帶密碼的zip包的方法詳解
Zip文件詳解
ZIP 文件格式是一種常用的壓縮和歸檔格式,用于將多個(gè)文件和目錄打包到一個(gè)單獨(dú)的文件中,同時(shí)對其內(nèi)容進(jìn)行壓縮以減少文件大小。ZIP 文件格式的設(shè)計(jì)旨在支持多種壓縮算法、加密和數(shù)據(jù)完整性校驗(yàn)。以下是 ZIP 文件格式的主要特性和常用算法:
ZIP 文件格式主要特性
文件頭:
每個(gè)文件都有一個(gè)本地文件頭和一個(gè)中央目錄文件頭。文件頭包含文件名、壓縮方法、時(shí)間戳、CRC-32 校驗(yàn)和、壓縮前后的大小等信息。
數(shù)據(jù)描述符:
可選的后綴結(jié)構(gòu),包含文件的 CRC-32 校驗(yàn)和、壓縮大小和未壓縮大小。
中央目錄:
ZIP 文件的末尾包含一個(gè)中央目錄記錄,列出了 ZIP 文件中的所有文件和目錄的文件頭信息,用于快速定位和訪問。
結(jié)束記錄:
ZIP 文件末尾的“中央目錄結(jié)束記錄”標(biāo)識了中央目錄的結(jié)束,包含中央目錄的偏移量和大小等信息。
一個(gè)簡單的 ZIP 文件可以包含多個(gè)文件的本地文件頭、壓縮數(shù)據(jù)、中央目錄和結(jié)束記錄。解壓工具通過讀取中央目錄找到各個(gè)文件的偏移量和大小,然后根據(jù)這些信息讀取和解壓文件數(shù)據(jù)。
以下是一個(gè) ZIP 文件結(jié)構(gòu)的簡化示例:
[本地文件頭1] [文件數(shù)據(jù)1] [本地文件頭2] [文件數(shù)據(jù)2] ... [中央目錄] [中央目錄結(jié)束記錄]
ZIP 文件格式因其廣泛的支持和高效的壓縮性能,廣泛應(yīng)用于文件歸檔和傳輸。DEFLATE 算法是其最常用的壓縮算法,提供了良好的平衡點(diǎn)。
常用算法
壓縮算法:
- DEFLATE:這是 ZIP 文件中最常用的壓縮算法,由 Phil Katz 發(fā)明。它結(jié)合了 LZ77 算法和 Huffman 編碼,提供了良好的壓縮比和解壓速度。
- STORE:不進(jìn)行任何壓縮,僅用于存儲數(shù)據(jù)。適用于已經(jīng)被壓縮過的數(shù)據(jù),如 JPEG 圖像或 MP3 音頻文件。
- 其他壓縮方法:ZIP 規(guī)范還支持其他壓縮方法,如 BZIP2、LZMA 和 PPMd,但這些方法在實(shí)際使用中較為少見。
加密算法:
- 傳統(tǒng) ZIP 加密:早期的 ZIP 文件使用一種相對簡單的對稱加密方法,但這種方法的安全性較弱。
- AES 加密:一些現(xiàn)代的 ZIP 工具支持使用高級加密標(biāo)準(zhǔn) (AES) 進(jìn)行加密,提供了更強(qiáng)的安全性。
校驗(yàn)算法:
- CRC-32:用于每個(gè)文件的數(shù)據(jù)完整性校驗(yàn)。每個(gè)文件都有一個(gè) CRC-32 校驗(yàn)和,用于檢測數(shù)據(jù)傳輸或存儲過程中的錯(cuò)誤。
Zip格式結(jié)構(gòu)圖總覽
Zip文件結(jié)構(gòu)詳解
zip格式壓縮包主要由三大部分組成:數(shù)據(jù)區(qū)、中央目錄記錄區(qū)(也有叫核心目錄記錄)、中央目錄記錄尾部區(qū)。
數(shù)據(jù)區(qū)
數(shù)據(jù)區(qū)是由一系列本地文件記錄組成,本地文件記錄主要是記錄了壓縮前后文件的元數(shù)據(jù)以及存放壓縮后的文件,組成部分也分為三大部分:本地文件頭
、文件數(shù)據(jù)
、文件描述
本地文件頭
local file header signature 4 bytes (0x04034b50) version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes file name length 2 bytes extra field length 2 bytes file name (variable size) extra field (variable size)
本地文件頭主要是記錄了壓縮文件的元數(shù)據(jù):
- loca file header signature:0~3,4個(gè)字節(jié),用來存放本地文件頭標(biāo)識,一般為固定值
0x04034b50
,用于解壓時(shí)候,讀取判斷文件頭的開始; - version needed to extract:4~5,2個(gè)字節(jié),記錄解壓縮文件所需的最低支持的ZIP規(guī)范版本,apk壓縮版本默認(rèn)是20, 即Deflate壓縮方式。該字段值=解壓所需的最低ZIP規(guī)范版本*10,比如,最低支持的ZIP規(guī)范版本是2.0,那么該字段的值就是20。每個(gè)版本定義如下。
當(dāng)前最低功能版本定義如下:(壓縮包記錄的解壓版本都是需要版本*10,比如:2.0 * 10 = 20) 1.0 – 默認(rèn)值 1.1 – 文件是卷標(biāo) 2.0 – 文件是一個(gè)文件夾(目錄) 2.0 – 使用 Deflate 壓縮來壓縮文件 2.0 – 使用傳統(tǒng)的 PKWARE 加密對文件進(jìn)行加密 2.1 – 使用 Deflate64? 壓縮文件 2.5 – 使用 PKWARE DCL Implode 壓縮文件 2.7 – 文件是補(bǔ)丁數(shù)據(jù)集 4.5 – 文件使用 ZIP64 格式擴(kuò)展 4.6 – 使用 BZIP2 壓縮文件壓縮 5.0 – 文件使用 DES 加密 5.0 – 文件使用 3DES 加密 5.0 – 使用原始 RC2 加密對文件進(jìn)行加密 5.0 – 使用 RC4 加密對文件進(jìn)行加密 5.1 – 文件使用 AES 加密進(jìn)行加密 5.1 – 使用更正的 RC2 加密對文件進(jìn)行加密 5.2 – 使用更正的 RC2-64 加密對文件進(jìn)行加密 6.1 – 使用非 OAEP 密鑰包裝對文件進(jìn)行加密 6.2 – 中央目錄加密
- genaral purpose bit flag:6~7,2個(gè)字節(jié),記錄通用標(biāo)志位,第0位為
1
時(shí)(即二進(jìn)制:00000000 00000001),表示文件被加密,解壓時(shí)候需要解密;第3位為1
時(shí)候(即二進(jìn)制:00000000 00000100),表示有數(shù)據(jù)描述部分,本地文件頭中的 CRC-32、壓縮大小和未壓縮大小字段都被設(shè)置為0(雖然zip規(guī)范是這么定義,但是發(fā)現(xiàn)有些壓縮包即使聲明有數(shù)據(jù)描述部分,但是本地文件頭的CRC-32、壓縮大小和未壓縮大小依然還是設(shè)置為真實(shí)值) , 正確的值被放在緊跟在壓縮數(shù)據(jù)之后的數(shù)據(jù)描述部分,apk的通用標(biāo)志位默認(rèn)傳0即可,也有傳2048、2056,目前第15位是PKWARE保留位。 - compression method:8~9,2個(gè)字節(jié),記錄壓縮包所用到的壓縮方式,apk默認(rèn)Deflate壓縮,傳8即可, 要是傳0 ,則是不壓縮,各種壓縮方式對應(yīng)數(shù)值如下:
0 – The file is stored (no compression) 1 – The file is Shrunk 2 – The file is Reduced with compression factor 1 3 – The file is Reduced with compression factor 2 4 – The file is Reduced with compression factor 3 5 – The file is Reduced with compression factor 4 6 – The file is Imploded 7 – Reserved for Tokenizing compression algorithm 8 – The file is Deflated 9 – Enhanced Deflating using Deflate64? 10 – PKWARE Data Compression Library Imploding 11 – Reserved by PKWARE 12 – File is compressed using BZIP2 algorithm
- last mod file time:10~11,2個(gè)字節(jié),記錄文件最后修改時(shí)間,是MS-DOS格式編碼的時(shí)間
- last mod date time:12~13,2個(gè)字節(jié),記錄文件最后修改日期,是MS-DOS格式編碼的日期
- crc-32:14~17,4個(gè)字節(jié),記錄文件未壓縮時(shí)的CRC-32校驗(yàn)碼
- compressed size:18~21,4個(gè)字節(jié),記錄文件壓縮后的大小
- uncompressed size:22~25,4個(gè)字節(jié),記錄文件未壓縮的大小
- file name length:26~27,2個(gè)字節(jié),記錄文件名的長度(假設(shè)文件名長度為n)
- extra field length:28~29,2個(gè)字節(jié),記錄擴(kuò)展區(qū)的長度(假設(shè)擴(kuò)展區(qū)長度為m)
- file name:30~30+n,n個(gè)字節(jié),記錄文件名
- extral field:30+n~30+n+m,m個(gè)字節(jié),記錄擴(kuò)展數(shù)據(jù)
文件數(shù)據(jù)
文件數(shù)據(jù)緊跟在本地文件頭之后,一般是壓縮后的文件數(shù)據(jù)或壓縮方式選擇不壓縮時(shí)候,用來存儲未壓縮文件數(shù)據(jù)。
文件描述
文件描述符僅在通用位標(biāo)志的第 3 位被設(shè)置為1時(shí)才存在。 它是字節(jié)對齊的,緊跟在文件數(shù)據(jù)的最后一個(gè)字節(jié)之后。當(dāng)且僅當(dāng)無法在 .ZIP 文件中查找時(shí)才使用此描述符,例如:當(dāng)輸出 的.ZIP 文件是標(biāo)準(zhǔn)輸出或不可查找設(shè)備時(shí)使用文件描述,換句話說,正常情況下都不需要使用。
( 數(shù)據(jù)描述符標(biāo)識不一定有,因?yàn)橐婚_始規(guī)范是沒有的,后面才加上去的)
中央目錄記錄區(qū)(核心目錄記錄區(qū) )
中心目錄區(qū)的結(jié)構(gòu)如下。
[file header 1] . . . [file header n] [digital signature]
central file header signature 4 bytes (0x02014b50) version made by 2 bytes version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes file name length 2 bytes extra field length 2 bytes file comment length 2 bytes disk number start 2 bytes internal file attributes 2 bytes external file attributes 4 bytes relative offset of local header 4 bytes file name (variable size) extra field (variable size) file comment (variable size)
中央目錄記錄區(qū)是有一系列中央目錄記錄所組成,一條中央目錄記錄對應(yīng)數(shù)據(jù)區(qū)中的一個(gè)壓縮文件記錄,中央目錄記錄由以下部分構(gòu)成:(中央目錄區(qū)通常由多個(gè)文件頭(file header)組成,每一個(gè)被壓縮的文件都有一個(gè)對應(yīng)的file header(注意,這里不是local file header),用于標(biāo)識和定位該文件在ZIP文件中的位置。這個(gè)文件頭和本地文件頭類似,記錄了被壓縮文件的元數(shù)據(jù)信息,包括文件原始大小,壓縮之后的大小,文件注釋等。)
- central file header signature:0~3,4個(gè)字節(jié),記錄核心目錄文件頭標(biāo)識,固定值:
0x02014b50
,用于解壓時(shí)候,查找判斷是否是中央目錄的開始位置 - version made by:4~5,2個(gè)字節(jié),記錄壓縮所用的版本,同數(shù)據(jù)區(qū)本地文件頭的解壓所需版本,apk設(shè)置20
- 6~7:2個(gè)字節(jié),記錄解壓所需的最小版本,同數(shù)據(jù)區(qū)本地文件頭的解壓所需版本,apk設(shè)置20
- 8~9:2個(gè)字節(jié),通用位標(biāo)記,同數(shù)據(jù)區(qū)本地文件頭的通用位標(biāo)記
- 壓縮方法、文件最后修改時(shí)間、文件最后修改日期、CRC-32校驗(yàn)碼、壓縮后大小、未壓縮大小、文件名長度、擴(kuò)展區(qū)長度,這幾個(gè)字段的含義都等同于數(shù)據(jù)區(qū)本地文件頭對應(yīng)字段的含義
- file comment length:32~33,2個(gè)字節(jié),記錄文件注釋的長度
- disk number start:34~35,2個(gè)字節(jié),記錄文件開始位置的磁盤編號,一般傳0即可
- 36~41:內(nèi)部文件屬性、外部文件屬性,一般也是傳0即可
- 42~45:4個(gè)字節(jié),記錄數(shù)據(jù)區(qū)本地文件頭相對于壓縮包開始位置的偏移量
數(shù)據(jù)簽名(digital signature):
header signature 4 bytes (0x05054b50) size of data 2 bytes signature data (variable size)
- header signature:數(shù)字簽名起始標(biāo)識,固定值為0x05054b50。
- size of data:數(shù)字簽名數(shù)據(jù)大小。
- signature data :簽名數(shù)據(jù)
中央目錄記錄尾部區(qū)
中央目錄記錄尾部主要作用是用來定位中央目錄記錄區(qū)的開始位置,同時(shí)記錄壓縮包的注釋內(nèi)容:
end of central dir signature 4 bytes (0x06054b50) number of this disk 2 bytes number of the disk with the start of the central directory 2 bytes total number of entries in the central directory on this disk 2 bytes total number of entries in the central directory 2 bytes size of the central directory 4 bytes offset of start of central directory with respect to the starting disk number 4 bytes .ZIP file comment length 2 bytes .ZIP file comment (variable size)
- end of central dir signature:0~3,4個(gè)字節(jié),中央目錄記錄尾部開頭標(biāo)記,固定值:
0x06054b50
,用于解壓時(shí),查找判斷中央目錄尾部的起始位置 - number of this disk:4~5,2個(gè)字節(jié),記錄中央目錄記錄尾部區(qū)所在磁盤編號
- number of the disk with the start of the central directory:6~7,2個(gè)字節(jié),記錄中央目錄開始位置所在的磁盤編號
- total number of entries in the central directory on this disk:8~9,2個(gè)字節(jié),該磁盤上所記錄的核心目錄數(shù)量
- total number of entries in the central directory:10~11,2個(gè)字節(jié),zip壓縮包中的文件總數(shù)
- size of the central directory:12~15,4個(gè)字節(jié),整個(gè)中央目錄的大小(以字節(jié)為單位)
- offset of start of central directory with respect to the starting disk number:16~19,4個(gè)字節(jié),中央目錄開始位置相對位移
- ZIP file comment length:20~21,2個(gè)字節(jié),注釋內(nèi)容的長度(假設(shè)長度為n)
- ZIP file comment:22~22+n,n個(gè)字節(jié),注釋內(nèi)容
中央目錄結(jié)束標(biāo)識是ZIP文件解壓的入口。通過讀取中央目錄結(jié)束標(biāo)識,解壓縮軟件可以快速地找到中央目錄,并據(jù)此解析整個(gè)ZIP文件的結(jié)構(gòu)和內(nèi)容。通過里面的中央核心目錄區(qū)的大小可以找到對應(yīng)的中央目錄模塊,然后根據(jù)中央目錄文件頭中的本地文件頭偏移(relative offset of local header)可以尋址到對應(yīng)的文件,并進(jìn)行解壓。
每個(gè)壓縮文件都必須且僅有一個(gè)中央目錄結(jié)束標(biāo)識。如果ZIP文件損壞或結(jié)構(gòu)不正確,可能會導(dǎo)致中央目錄結(jié)束標(biāo)識丟失或損壞,從而使得解壓縮軟件無法正確讀取和解析ZIP文件。
壓縮包解壓過程
方式1 通過解析中央目錄區(qū)來解壓
通過ZIP文件的結(jié)構(gòu)我們發(fā)現(xiàn),ZIP文件的中央目錄區(qū)保存了所有的文件信息。所以,可以通過中央目錄區(qū)拿到所有的文件信息并進(jìn)行解壓,步驟如下所示。
- 首先在 ZIP 文件末尾通過中央目錄結(jié)束標(biāo)識 (0x06054b50)找到中央目錄結(jié)束標(biāo)識數(shù)據(jù)塊。
- 通過中央目錄結(jié)束標(biāo)識中的中央目錄區(qū)開始位置偏移找到中央目錄區(qū)數(shù)據(jù)塊。
- 根據(jù)中央目錄區(qū)的File Header中的 local file header的偏移量找到對應(yīng)的local file header。
- 根據(jù) local file header找到對應(yīng)的file data
- 解密 file data(如果需要);
- 解壓 file data;
方式2 通過讀取本地文件頭來解壓
根據(jù) ZIP 文件格式標(biāo)準(zhǔn)可知,除了 中央目錄區(qū), 本地文件頭中也包含了每個(gè)文件的相關(guān)信息。因此,可以基于本地文件頭去解壓文件數(shù)據(jù),其解壓流程就可以變?yōu)椋?/p>
- 從頭開始,通過本地文件頭標(biāo)識搜索對應(yīng)的 local file header
- 讀取 local file header并找到file data;
- 解密 file data(如果需要);
- 解壓 file data;
兩種解壓方式對比
通過兩種解壓方式可以明顯看出,兩種解壓方式適用的場景不同。
方式1適用場景:
- 適用于在解壓文件已經(jīng)存在于磁盤上,并且需要解壓壓縮包中所有的文件。
方式2適用場景:
- 當(dāng)文件不在磁盤上,比如從網(wǎng)絡(luò)接收的數(shù)據(jù),想邊接收邊解壓;
- 需要順序解壓ZIP文件前面的一小部分文件,可以使用這種方式,因?yàn)榉绞?讀中央目錄區(qū)會帶來額外的耗時(shí);
- ZIP文件中的中央目錄區(qū)遭到損壞;
golang解壓zip包
官方archive/zip
golang zip包的解壓有官方的zip包(archive/zip),但是官方給的zip解壓包代碼只有解壓不帶密碼的zip包。
下面給出解壓操作的封裝:
func Unzip(src, dst string) error { // zip.NewReader() 適合從stream中讀取字節(jié)序列 zf, err := zip.OpenReader(src) if err != nil { return err } defer zf.Close() for _, file := range zf.File { // fmt.Println(file.Name) path := filepath.Join(dst, file.Name) // 如果是目錄則創(chuàng)建目錄 if file.FileInfo().IsDir() { if err = os.MkdirAll(path, 0o644); err != nil { return err } continue } f, err := os.Create(path) if err != nil { return err } reader, err := file.Open() if err != nil { return err } _, err = io.Copy(f, reader) if err != nil { return err } _ = f.Close() _ = reader.Close() } return nil } func main() { log.Println(Unzip("Go.zip", "./static")) }
通常情況下,目錄權(quán)限設(shè)為 0755,文件權(quán)限設(shè)為 0644 更為合適。
在 Unix 和 Unix-like 操作系統(tǒng)(如 Linux)中,文件權(quán)限使用三位八進(jìn)制數(shù)來表示,每一位分別表示所有者(Owner)、組(Group)和其他人(Others)的權(quán)限。每一位的權(quán)限可以是 1(執(zhí)行權(quán)限),2(寫入權(quán)限),4(讀取權(quán)限)的組合。它們的組合值表示特定的權(quán)限設(shè)置。
權(quán)限位的含義:
- 1:執(zhí)行權(quán)限 (Execute)
- 2:寫入權(quán)限 (Write)
- 4:讀取權(quán)限 (Read)
這些權(quán)限可以相加來組合權(quán)限。例如:
- 7 (4+2+1):讀取、寫入和執(zhí)行權(quán)限 (Read + Write + Execute)
- 6 (4+2):讀取和寫入權(quán)限 (Read + Write)
- 5 (4+1):讀取和執(zhí)行權(quán)限 (Read + Execute)
- 4:讀取權(quán)限 (Read)
- 3 (2+1):寫入和執(zhí)行權(quán)限 (Write + Execute)
- 2:寫入權(quán)限 (Write)
- 1:執(zhí)行權(quán)限 (Execute)
- 0:無權(quán)限 (No permissions)
權(quán)限設(shè)置示例:
權(quán)限設(shè)置通常以三位八進(jìn)制數(shù)表示,例如 0755
,每一位代表不同用戶類別的權(quán)限:
- 第一位(Owner 權(quán)限):
7
,表示所有者有讀取、寫入和執(zhí)行權(quán)限。 - 第二位(Group 權(quán)限):
5
,表示組用戶有讀取和執(zhí)行權(quán)限。 - 第三位(Others 權(quán)限):
5
,表示其他用戶有讀取和執(zhí)行權(quán)限。
rwxr-xr-x
r
代表讀取權(quán)限w
代表寫入權(quán)限x
代表執(zhí)行權(quán)限-
代表沒有該權(quán)限
示例解釋:
0755
:
- 所有者(Owner)權(quán)限:
7
(rwx) 讀取、寫入和執(zhí)行 - 組(Group)權(quán)限:
5
(r-x) 讀取和執(zhí)行 - 其他人(Others)權(quán)限:
5
(r-x) 讀取和執(zhí)行
0644
:
- 所有者(Owner)權(quán)限:
6
(rw-) 讀取和寫入 - 組(Group)權(quán)限:
4
(r–) 讀取 - 其他人(Others)權(quán)限:
4
(r–) 讀取
第三方包github.com/yeka/zip
使用go get github.com/yeka/zip
安裝后,代碼都不需要改變,只是導(dǎo)入的zip包替換為第三方即可:
https://github.com/yeka/zip,關(guān)于這個(gè)庫里面的整個(gè)代碼是在官方的zip庫的基礎(chǔ)上做了一些修改,并且這個(gè)庫里面的代碼拋棄了注冊的功能,直接把解密代碼寫在了open文件里,廢棄了一個(gè)很好用的功能。
自己實(shí)現(xiàn)解壓加密的zip包
golang官方的zip代碼庫,https://golang.google.cn/pkg/archive/zip/#pkg-examples有兩個(gè)注冊接口,一個(gè)是壓縮和一個(gè)是解壓的:
官方注冊壓縮器方法示例:
package main import ( "archive/zip" "bytes" "compress/flate" "io" ) func main() { // Override the default Deflate compressor with a higher compression level. // Create a buffer to write our archive to. buf := new(bytes.Buffer) // Create a new zip archive. w := zip.NewWriter(buf) // Register a custom Deflate compressor. w.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { return flate.NewWriter(out, flate.BestCompression) }) // Proceed to add files to w. }
類比這個(gè)案例可以寫出:
func main() { zf, _ := zip.OpenReader("") zf.RegisterDecompressor(zip.Deflate, func(r io.Reader) io.ReadCloser { // TODO:解密算法實(shí)現(xiàn) return flate.NewReader(r) }) }
接下來實(shí)現(xiàn)解密算法(官方鏈接:https://support.pkware.com/pkzip/appnote):
zip標(biāo)準(zhǔn)文件:https://pkwaredownloads.blob.core.windows.net/pem/APPNOTE.txt,溫馨提示:不要機(jī)器翻譯成中文。
ZIP 文件加密算法通常使用一種簡單的流加密方法,稱為 ZipCrypto。解密過程包括初始化三個(gè) 32 位整數(shù) key0, key1, 和 key2,并根據(jù)密碼和加密的字節(jié)數(shù)據(jù)更新這些值。加密和解密使用同樣的邏輯,只是加密是將明文轉(zhuǎn)換為密文,而解密是將密文轉(zhuǎn)換為明文。
type ZipCrypto struct { password []byte Keys [3]uint32 } func NewZipCrypto(passphrase []byte) *ZipCrypto { z := &ZipCrypto{} z.password = passphrase z.init() return z } func (z *ZipCrypto) init() { z.Keys[0] = 0x12345678 z.Keys[1] = 0x23456789 z.Keys[2] = 0x34567890 for i := 0; i < len(z.password); i++ { z.updateKeys(z.password[i]) } } func (z *ZipCrypto) updateKeys(byteValue byte) { z.Keys[0] = crc32update(z.Keys[0], byteValue) z.Keys[1] += z.Keys[0] & 0xff z.Keys[1] = z.Keys[1]*134775813 + 1 z.Keys[2] = crc32update(z.Keys[2], (byte)(z.Keys[1]>>24)) } func (z *ZipCrypto) magicByte() byte { var t uint32 = z.Keys[2] | 2 return byte((t * (t ^ 1)) >> 8) } func (z *ZipCrypto) Encrypt(data []byte) []byte { length := len(data) chiper := make([]byte, length) for i := 0; i < length; i++ { v := data[i] chiper[i] = v ^ z.magicByte() z.updateKeys(v) } return chiper } func (z *ZipCrypto) Decrypt(chiper []byte) []byte { length := len(chiper) plain := make([]byte, length) for i, c := range chiper { v := c ^ z.magicByte() z.updateKeys(v) plain[i] = v } return plain } func crc32update(pCrc32 uint32, bval byte) uint32 { return crc32.IEEETable[(pCrc32^uint32(bval))&0xff] ^ (pCrc32 >> 8) }
實(shí)現(xiàn)加密解密算法后寫一個(gè)小例子來測試下:
func main() { password := "generalzy" zc := NewZipCrypto(password) // 示例數(shù)據(jù) data := []byte("Hello, ZipCrypto!") fmt.Printf("Original: %s\n", data) // 加密數(shù)據(jù) encrypted := zc.Encrypt(data) fmt.Printf("Encrypted: %x\n", encrypted) // 初始化解密器 zcDecrypt := NewZipCrypto(password) // 解密數(shù)據(jù) decrypted := zcDecrypt.Decrypt(encrypted) fmt.Printf("Decrypted: %s\n", decrypted) }
要利用 zip 包提供的注冊方法來注冊解密函數(shù),可以使用 RegisterDecompressor 方法。首先,需要擴(kuò)展之前的 ZipCrypto 實(shí)現(xiàn),使其能夠解密壓縮數(shù)據(jù)流。然后,可以將這個(gè)解密流注冊到 zip 包中。
最后給出整體代碼:
package main import ( "archive/zip" "bytes" "compress/flate" "fmt" "hash/crc32" "io" "io/ioutil" "os" "path/filepath" ) type ZipCrypto struct { password []byte Keys [3]uint32 } func NewZipCrypto(passphrase []byte) *ZipCrypto { z := &ZipCrypto{} z.password = passphrase z.init() return z } func (z *ZipCrypto) init() { z.Keys[0] = 0x12345678 z.Keys[1] = 0x23456789 z.Keys[2] = 0x34567890 for i := 0; i < len(z.password); i++ { z.updateKeys(z.password[i]) } } func (z *ZipCrypto) updateKeys(byteValue byte) { z.Keys[0] = crc32update(z.Keys[0], byteValue) z.Keys[1] += z.Keys[0] & 0xff z.Keys[1] = z.Keys[1]*134775813 + 1 z.Keys[2] = crc32update(z.Keys[2], (byte)(z.Keys[1]>>24)) } func (z *ZipCrypto) magicByte() byte { var t uint32 = z.Keys[2] | 2 return byte((t * (t ^ 1)) >> 8) } func (z *ZipCrypto) Encrypt(data []byte) []byte { length := len(data) chiper := make([]byte, length) for i := 0; i < length; i++ { v := data[i] chiper[i] = v ^ z.magicByte() z.updateKeys(v) } return chiper } func (z *ZipCrypto) Decrypt(chiper []byte) []byte { length := len(chiper) plain := make([]byte, length) for i, c := range chiper { v := c ^ z.magicByte() z.updateKeys(v) plain[i] = v } return plain } func crc32update(pCrc32 uint32, bval byte) uint32 { return crc32.IEEETable[(pCrc32^uint32(bval))&0xff] ^ (pCrc32 >> 8) } func ZipCryptoDecryptor(r *io.SectionReader, password []byte) (*io.SectionReader, error) { z := NewZipCrypto(password) b := make([]byte, r.Size()) r.Read(b) m := z.Decrypt(b) return io.NewSectionReader(bytes.NewReader(m), 12, int64(len(m))), nil } type unzip struct { offset int64 fp *os.File name string } func (uz *unzip) init() (err error) { uz.fp, err = os.Open(uz.name) return err } func (uz *unzip) close() { if uz.fp != nil { uz.fp.Close() } } func (uz *unzip) Size() int64 { if uz.fp == nil { if err := uz.init(); err != nil { return -1 } } fi, err := uz.fp.Stat() if err != nil { return -1 } return fi.Size() - uz.offset } func (uz *unzip) ReadAt(p []byte, off int64) (int, error) { if uz.fp == nil { if err := uz.init(); err != nil { return 0, err } } return uz.fp.ReadAt(p, off+uz.offset) } // DeCompressZip 解壓zip包 func DeCompressZip(zipFile, dest, passwd string, offset int64) error { uz := &unzip{offset: offset, name: zipFile} defer uz.close() zr, err := zip.NewReader(uz, uz.Size()) if err != nil { return err } if passwd != "" { // Register a custom Deflate compressor. zr.RegisterDecompressor(zip.Deflate, func(r io.Reader) io.ReadCloser { rs := r.(*io.SectionReader) r, _ = ZipCryptoDecryptor(rs, []byte(passwd)) return flate.NewReader(r) }) zr.RegisterDecompressor(zip.Store, func(r io.Reader) io.ReadCloser { rs := r.(*io.SectionReader) r, _ = ZipCryptoDecryptor(rs, []byte(passwd)) return ioutil.NopCloser(r) }) } for _, f := range zr.File { fpath := filepath.Join(dest, f.Name) if f.FileInfo().IsDir() { os.MkdirAll(fpath, os.ModePerm) continue } if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { return err } inFile, err := f.Open() if err != nil { return err } outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { inFile.Close() return err } _, err = io.Copy(outFile, inFile) inFile.Close() outFile.Close() if err != nil { return err } } return nil } func main() { err := DeCompressZip("Go.zip", "./tmp", "123456", 0) if err != nil { fmt.Println(err) } return }
以上就是golang解壓帶密碼的zip包的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于golang解壓zip包的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang?gorm的Callbacks事務(wù)回滾對象操作示例
這篇文章主要為大家介紹了golang?gorm的Callbacks事務(wù)回滾對象操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04Go基礎(chǔ)教程系列之?dāng)?shù)據(jù)類型詳細(xì)說明
這篇文章主要介紹了Go基礎(chǔ)教程系列之?dāng)?shù)據(jù)類型詳細(xì)說明,需要的朋友可以參考下2022-04-04使用go進(jìn)行云存儲上傳實(shí)現(xiàn)實(shí)例
這篇文章主要為大家介紹了使用go進(jìn)行云存儲上傳實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2024-01-01基于Go語言實(shí)現(xiàn)應(yīng)用IP防火墻
在公司里面經(jīng)常會聽到某應(yīng)用有安全漏洞問題,沒有做安全加固,IP防火墻就是一個(gè)典型的安全加固解決方案,下面我們就來學(xué)習(xí)一下如何使用go語言實(shí)現(xiàn)IP防火墻吧2023-11-11Golang自定義開發(fā)Prometheus?exporter詳解
Exporter是基于Prometheus實(shí)施的監(jiān)控系統(tǒng)中重要的組成部分,承擔(dān)數(shù)據(jù)指標(biāo)的采集工作,這篇文章主要為大家介紹了如何自定義編寫開發(fā)?Prometheus?exporter,感興趣的可以了解一下2023-06-06