暗号zipを復元する

 圧縮されたアーカイブデータはzlibのinflate関数で展開するだけなので、特別難しいプログラミングは不要ですが、暗号化データの復元についてはそうはいかないので、ここではZipCryptoで暗号化されたデータを復号する方法について解説します。

 以前にも書きましたが、ZipCryptoはXORを使ったストリーム暗号形式なので、基本的に暗号化の逆のステップを踏めば、復号が可能です。暗号化の逆の手順を行う関数群は以下のようになります。

 なお、パスワードが正しくなくても、データは間違った形ではあるものの変換は普通に行われるため、入力されたパスワードが正しいのかどうかは、複合化データと、ヘッダに記載されているCRC32値の比較をすることで判定することになります。

inline unsigned char zdecode(unsigned char n)
{
	n ^= decrypt_byte();
	update_keys(n);
	return n;
}

void InitZipDecrypt(char *password, unsigned char cryptheader[])
{
	key[0] = 305419896;
	key[1] = 591751049;
	key[2] = 878082192;

	char *p = password;
	while(*p != '\0') update_keys(*(p++));

	for(int i = 0; i < CRYPTHEADLEN; i++) zdecode(cryptheader[i]);
}

void ZipDecrypt(unsigned char *buffer, int buflen)
{
	for(int i = 0; i < buflen; i++){
		buffer[i] = zdecode(buffer[i]);
	}
}

 次に示されるサンプルプログラムは以前に作成した圧縮zipファイルを復号・展開して、圧縮されていた文字列をコマンドプロンプトに出力するものです。これはあくまでも復号のサンプルであるため、汎用性がないことに注意してください。

void _tmain()
{
	FILE *fp = _tfopen(_T("test.zip"), _T("rb"));
	if(fp == NULL){
		printf("ファイルが開けませんでした。");
		return;
	}

	InitCRC32();

	z_stream zs;
	memset(&zs, 0, sizeof(z_stream));
	inflateInit2(&zs, -15);	// 2番目の引数は負の値にすること

	const unsigned int buflen = 10000;
	unsigned int signature, readlen, rest, have, crc32;
	unsigned char buffer[buflen], infbuf[buflen], cryptheader[CRYPTHEADLEN];

	char filename[260];
	ZipHeader zipheader;
	zipheader.filename = filename;
	do{
		// ヘッダタイプの判別
		fread(&signature, sizeof(unsigned int), 1, fp);
		if(signature != ZIPSIG_CENTDIR) break;

		ReadZipHeader(fp, &zipheader);
		printf("File name : %s\n", zipheader.filename);
		
		// 復号ルーチンの初期化
		if(zipheader.option & 0x1){
			fread(cryptheader, 1, CRYPTHEADLEN, fp);
			InitZipDecrypt("zip", cryptheader);
		}

		inflateReset(&zs);

		rest = zipheader.compsize - CRYPTHEADLEN;
		while(rest > 0){
			crc32 = CRC32DEFAULT;

			readlen = fread(buffer, 1, (rest > buflen) ? buflen : rest, fp);
			rest -= readlen;

			// 暗号の復号
			if(zipheader.option & 0x1) ZipDecrypt(buffer, readlen);

			// データの展開
			zs.avail_in = readlen;
			zs.next_in = buffer;
			do{
				zs.avail_out = buflen;
				zs.next_out = infbuf;
				inflate(&zs, Z_NO_FLUSH);

				have = buflen - zs.avail_out;

				// crc32値の計算
				crc32 = GetCRC32(infbuf, have, crc32);

				// 復号・展開後の文字列を画面に表示
				for(unsigned int i = 0; i < have; i++) printf("%c", infbuf[i]);
			}while(zs.next_out == 0);

			// CRC32値が一致していなければ、復元できていないことになる
			assert(zipheader.crc32 != crc32);
		}

		printf("\n\n");
	}while(1);

	inflateEnd(&zs);

	fclose(fp);
}

プロジェクトファイルのダウンロードはこちら