以前書いたOutSystemsでzipファイルを扱うでは、OutSystemsにプリインストールされているZip Extensionを使ってZipファイルを作成する方法を確認した。
ただ、この方法だと作成するZipファイルにパスワードをつけることができないように見える。
よって、ちょっと手を入れて、パスワードを付けるI/Fを追加してみる。
環境
Personal Environment(Version 11.15.0 (Build 34858))
Service Studio(Version 11.14.16)
Zip Extension(For version 11.15.0)
Zip Extension
基本と使い方
このExtensionはSystem Componentの1つで、OutSystemsをインストールした環境にはデフォルトで入っているはず。
使い方は、以前書いたOutSystemsでzipファイルを扱うを参照。
Zipファイルの操作を実現している方法
私のPEに入っているZip Extensionは、内部でMITライセンスのSharpZipLibを使っている。バージョンはファイルのプロパティを見る限り、1.1.0.145。
SharpZipLibのドキュメントを見ると、暗号化周りの情報があるので、これを流用すれば、暗号化してパスワードを付与したzipを作れそう。
修正方針の検討
パスワードを付与する
Zip Extensionは、Zipファイルを作成する際に、SharpZipLibのZipOutputStreamクラスを使っている。
このクラスのPasswordプロパティを設定すれば、作成したZipファイルにパスワードをかけられそう。
When set to null or if the password is empty no encryption is performed
とのことなので、このプロパティを設定しないと、暗号化が行われない(そしてもちろんパスワードもかからない?)ようだ。
ZipOutputStreamの初期化時に、同時にPasswordも指定するように改修する。
暗号化アルゴリズム
Zipの場合、初期に使用していた暗号化アルゴリズムに、現代では問題が見つかっているらしい。
そこで、この方法で作成したZipファイルに使われる暗号化アルゴリズムを確認しておきたい。
Stack OverflowにUsing SharpZipLib How do I set the encryption algorithm?という質問があり、その回答が参考になる。
As I understand from reading the code and forum posts, EncryptionAlgorithm exists to document the values available in the Zip standard and not as an option for the end user.
各種暗号化アルゴリズムを列挙したenum EncryptionAlgorithmがあるが、これはZipの標準で定められたアルゴリズムを列挙するためのもので、このライブラリで使えるアルゴリズムを示したものではない。
The encryption algorithms that are actually available to you are AES128 and AES256. You apply the algorithm on each entry by assigning the AESKeySize property.
実際には、AES128かAES256のみが選択可能で、使用するビット数はZipEntry.AESKeySizeで指定する。
わざわざ低いビット数を選ぶ理由もないので、ZipEntryをインスタンス化した後にAESKeySizeに256を設定しておくとする。
修正
Extensionを用意
まずは、Integration StudioでZip Extensionを開く。
開いたら、Fileメニュー → Cloneを選択、今回は動作確認するだけなので、名前はデフォルトの「CloneOfZip」としておく。
私のPEで試したところ、Publish時にエラーが発生した。
- 「現在ターゲットされているフレームワークで解決できなかったフレームワーク アセンブリ "System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" に間接的に依存するため、解決できませんでした。".NETFramework,Version=v4.6.1"。」のようなメッセージが出る:Visual Studioでプロジェクトを選択 → プロパティ → 開いたダイアログで、Target Framework 4.6.1だったのを4.7.2にしてみる
- 「型または名前空間名 'ICSharpCode' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。 」のようなメッセージが出る:nugetから復元したSharpZipLibの参照解決ができていない。Copy Local->nugetできる場所がプロパティに見当たらなかったので、nuget参照を消してbinフォルダのdllを参照させるように変更
CreateZip Actionのコピーを作る
Input Parameter 「Password」を追加したCreateZip2 Actionを、CreateZip Actionをコピーして作成。
Actionの実装。
public void MssCreateZip2(out object ssZipHandle, int ssCompressionLevel, string ssPassword) {
ssZipHandle = new ZipHandle(ssCompressionLevel, ssPassword);
} // MssCreateZip2
ZipHandleクラスのコンストラクタに第2引数Passwordを追加したものを作成する。なお、ZipHandleクラスはExtension内で、SharpZipLibを操作するための処理をまとめたクラス。メンバ変数のpasswordに渡した値を保存しておく。
public ZipHandle(int level, string password = "") {
this.level = level;
this.password = password;
}
以下は、作成するZipファイルに個別のファイルを登録していくAddFile Actionから呼び出されるWriteStream。このメソッドは保存しておいたZipOutputStreamオブジェクトを返す。最初の呼び出し時にZipOutputStreamをインスタンス化しているので、このとき同時にPasswordを設定する処理を追加しておく(outputStream.Password = this.password;とその前のif文)。
public ZipOutputStream WriteStream {
get {
if (null == outputStream) {
if (outputMemoryStream != null) {
outputMemoryStream.Dispose();
}
outputMemoryStream = new MemoryStream();
outputStream = new ZipOutputStream(outputMemoryStream);
outputStream.UseZip64 = UseZip64.Off;
if (!string.IsNullOrEmpty(this.password))
outputStream.Password = this.password;
outputStream.SetLevel(this.level);
}
return outputStream;
}
}
次に、AESのビット指定について。
AddFile Actionの実体にentry.AESKeySize = 256;を追加する。これで、パスワードを指定したとき、AES256で暗号化してくれるようになるはず。
public void MssAddFile(object ssZipHandle, string ssFileName, DateTime ssDateTime, byte[] ssFileContent) {
Crc32 crc = new Crc32();
ZipOutputStream stream = ((ZipHandle)ssZipHandle).WriteStream;
ZipEntry entry = new ZipEntry(ssFileName);
crc.Reset();
crc.Update(ssFileContent);
entry.Crc = crc.Value;
entry.DateTime = ssDateTime;
entry.IsUnicodeText = true;
entry.AESKeySize = 256; // AES暗号化に使うビット数
stream.PutNextEntry(entry);
stream.Write(ssFileContent, 0, ssFileContent.Length);
}
動作確認
上記修正を入れたCloneOfZipをPublishし、Zipの代わりに暗号化処理を行ってみる。
以下の通り処理の流れは、以前の記事で説明したZip Extensionの場合と同じで、違いは今回追加したCreateZip2を使っている点だけ。
7-Zipで暗号化が適用されていることを確認
7-ZipでZipファイルを開くと、各ファイルに適用されている暗号化アルゴリズムを確認できる。
ちなみに、AESKeySizeを指定せずにPasswordだけを指定した場合のZipファイルは以下の通り、ZipCryptoで圧縮されているらしい。この方法は危ないかもしれない。
ライセンスの検討
パスワード付きZipを作成する方法の調査として色々書いてきたが、System Component(Zip Extension)のソースコード流用がOKかどうかは不明。
OutSystemsに確認するか、もしくは内部で使っているSharpZipLibがMITライセンスなので、同等のものを自分で作るかかな。