2016年1月1日金曜日

VB zip ファイルの中身からハッシュ値算出 .Net4.5 以降

.Netframework 4.5 から zip ファイルをハンドリング出来る様になっていますね。
今回は、VB で、 zip ファイルに格納されたファイルの ハッシュ値 を算出し、 併せて、基本情報を取得するもの を書いて見ました。
まぁ、普通は zip の ハッシュ値 を公表するだけでよいのでしょうが ... 。
前にも書きましたが、zip に格納すると、オリジナルに対して、タイムスタンプに2秒の誤差が生じる可能性がありますので ... 。


検索でハッシュ値の算定方法に関しては色々と解説されたサイトが存在します。
zip にしたものも、展開して 各々のファイルに対して これらの操作を行えば、 無事、各種の情報を取得出来ますね。
でも、自分で作った zip ファイルを、わざわざ、再び展開してファイルを取り出し、それを確認するのは、
少し、 ばか げた 行為に思えます。
どうせなら、 zip を 直接 扱って 取得したいものです。
幸い、.Netframework 4.5 からは System.IO.Compression 以下のクラスを使って、ハンドリングが可能になっていますね。

そこで、 zip に格納されたファイルの情報と ハッシュ値算定とを 行うもの を書きました。

  実行ファイルを公開します。  DownLoad Page  >  Program(Tool) ダウンロード ファイル 整合性 確認ツール

                                   リンクの無断転載 禁止!。.

ご存知の様に、ハッシュ値 は FileStream に取得したファイルを ビット処理して 算定します。
zip の場合、FileStream では 圧縮されたファイル群の固まりを掴んでしまうので、 個々のファイルの部分を切り出す必要があります。

System.IO.Compression.ArchiveEntry から 目的の Stream を分離する事で、 後の処理は 通常のファイルと同じ様に行えます。
注目すべきは、FileStream  の上位クラスの Stream です。

ですから、ハッシュ値算定部分の Subroutine や Function を書くなら、 FileStream ではなく Stream で引数を書く事になります。


以下に、 ハッシュ値の例として、 近年、その安全性が低下したものの 広く目にする機会の多い MD5 で説明します。
この様に記述すれば、 ファイル自体も zip の中の個別ファイルも、 同じ様に扱えます。


先ずは、外堀の MD5 算出から。  少し、コードが冗長ですが ... 。.

    Private Function Get_MD5(ByVal fs As Stream) As String
        Dim value As String = ""
        Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider()

        Dim bs As Byte() = md5.ComputeHash(fs)
        md5.Clear()
        Dim result As New System.Text.StringBuilder()
        For Each b As Byte In bs
            result.Append(b.ToString("x2"))
        Next
        value = result.ToString
        Return value
    End Function

そして、zip を扱う部分。  途中に省略を挿みながら、コード全体を俯瞰。  あっ、上の Subroutine も入れて下さいね。.
Project の参照に  System.IO.Compression と System.IO.Compression.FileSystem を加えて下さい。

Imports System.IO
Imports System.IO.Compression       '.Net4.5

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
       Me.TextBox1.Text = "C:¥Users¥UserName¥Document¥xxx.zip"    'For Sample
       'Me.TextBox2.Text = "zip MD5"    'For Sample
       'Me.TextBox3.Text = "file MD5 in zip"    'For Sample
       'Me.ComboBox1.Text = "Files in zip"    'For Sample
       GetFileMD5()
       GetZipMD5()
    End Sub

    Private Sub GetFileMD5()
        Dim fileName As String = Me.TextBox1.Text
        Dim fi As New IO.FileInfo(fileName)
        Dim fLen As Long = fi.Length

        'ZipName.Add(IO.Path.GetFileName(fileName))
        'ZipSize.Add(fLen.ToString)
        'ZipDate.Add(IO.File.GetLastWriteTime(fileName).ToString())

        Using fs As New System.IO.FileStream( _
                                            fileName, _
                                            System.IO.FileMode.Open, _
                                            System.IO.FileAccess.Read, _
                                            System.IO.FileShare.Read)
            'ZipMD5.Add(Get_MD5(fs))
            Me.TextBox2.Text = Get_MD5(fs)    'For Sample
        End Using
    End Sub

    Private Sub GetZipMD5()
        Dim ZipMD5 As New List(Of String)    'For Sample

        Dim zipPath As String = Me.TextBox1.Text
        Me.ComboBox1.Items.Clear()
        Using archive As ZipArchive = ZipFile.OpenRead(zipPath)
             For Each entry As ZipArchiveEntry In archive.Entries

                Me.ComboBox1.Items.Add(entry.FullName)
                'ZipName.Add(entry.FullName)
                'ZipSize.Add(entry.Length.ToString)
                'ZipDate.Add(entry.LastWriteTime.DateTime.ToString)

                Using fs As IO.Stream = entry.Open()
                    ZipMD5.Add(Get_MD5(fs))
                End Using
   
            Next
        End Using
        Me.ComboBox1.SelectedIndex = 0
        Me.TextBox3.Text = ZipMD5(0)    'For Sample ( Top Item's MD5 )
    End Sub

End Class

こんな具合に、コードを書いて行けば、zip 自体も その中身も 一度に 情報取得が可能ですね。

参考までに、 説明用に コンパクトに 作ったコード ですので、 実際に このまま 運用するのは、ちょっと ... 。
一応、元のソースコードから、参考になりそうな部分の さわり だけは残して置きましたが ... 。  コメント部分を参照。.
各自、手を入れて下さい。



0 件のコメント:

コメントを投稿