2009-05-18 56 views
3

我看不到任何方式來鹽MD5.ComputeHash(流)。 我錯過了一些注入字節到HashAlgorithm的方法?乞討流C#MD5 ComputeHash

我試圖在執行流計算之前執行ComputeHash(byte []),但是,毫不奇怪,它沒有效果。任何想法(除了修改文件)?

謝謝你的時間。

只是要更具體一點,我想用一個流得到關於我不想加載到內存的大型文件的哈希。

FileInfo myFI= new FileInfo("bigfile.dat"); 
FileStream myIFS = piFile.OpenRead(); 
MD5 md5 = MD5.Create(); 
byte[] hash = md5.ComputeHash (myIFS); 
myIFS.Close(); 
+0

我不知道是否有鹽特定方式的算法。但是你可以很容易地創建自己的Stream類來包裝給定的Stream對象。當詢問最初的幾個字節時,包裝器對象可以提供salt字節,然後開始提取底層流字節。 – 2009-05-18 18:48:59

+0

在這種情況下,根據給出的信息,鹽是不必要的,並且可能無法實現(回想一下鹽需要與* salthashed數據一起存儲)。 – yfeldblum 2009-11-14 17:51:15

回答

1

我想你可以使用一個語法像:

byte[] saltedBytes; 
//TODO: fill the saltedBytes; 
var hasher=new MD5CryptoServiceProvider(); 
var memoryStream=new MemoryStream(saltedBytes); 
hasher.ComputeHash(memoryStream); 
memoryStream.Close; 
5

答案缺少的例子在我看來:你並不真的需要它鹽。

像MD5這樣的散列算法需要一個任意長度的字節表並將其轉換爲已知長度的字節表 - 操作不容易可逆,對輸入表的小改動會導致輸出表中出現不可預知的變化:

輸入=> MD5 =>輸出

鹽析的目的,是針對其中用戶具有散列結果的預先計算的已經表(rainbow tables)攻擊的保護。通過在輸入中引入小的變化,結果發生了巨大的變化,因此即使攻擊者知道哈希結果和鹽,也很難猜測輸入:

input + salt => MD5 =>輸出

散列文件的原因是計算校驗和。例如。您在網頁上發佈一個文件以及散列結果。用戶然後下載一個文件,通過MD5運行它並將結果與​​您的發佈結果進行比較。篡改文件將非常困難,因爲每個操作都會改變結果散列。

這裏沒有必要進行醃製,因爲您必須將salt與生成的散列一起發佈,以便用戶可以重複散列操作。

如果您確實需要引入鹽分,只需以可重複的方式更改輸入流即可,例如,爲每個字節添加一個(帶溢出)。

0

爲了避免把所有的文件到像Dabblernl的解決方案的內存,你需要使用一個FileStream在這討論過質疑Computing MD5SUM of large files in C#,但MD5CryptoServiceProvider不允許你額外的數據添加到末尾。

因此,你需要一個合併的流這樣的:

public class MergedStream : Stream, IDisposable 
{ 
    Stream s1; 
    Stream s2; 

    public MergedStream(Stream first, Stream second) 
    { 
     s1 = first; 
     s2 = second; 
    } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     int s1count = (int)Math.Min((long)count, s1.Length - s1.Position); 
     int bytesRead = 0; 

     if (s1count > 0) 
     { 
      bytesRead += s1.Read(buffer, offset, s1count); 
     } 

     if (s1count < count) 
     { 
      bytesRead += s2.Read(buffer, offset + s1count, count - s1count); 
     } 

     return bytesRead; 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     throw new NotImplementedException(); 
    } 

    public override bool CanRead 
    { 
     get { return s1.CanRead && s2.CanRead; } 
    } 

    public override bool CanSeek 
    { 
     get { return s1.CanSeek && s2.CanSeek; } 
    } 

    public override bool CanWrite 
    { 
     get { return s1.CanWrite && s2.CanWrite; } 
    } 

    public override void Flush() 
    { 
     s1.Flush(); 
     s2.Flush(); 
    } 

    public override long Length 
    { 
     get { return s1.Length + s2.Length; } 
    } 

    public override long Position 
    { 
     get 
     { 
      return s1.Position + s2.Position; 
     } 
     set 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public override long Seek(long offset, SeekOrigin origin) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void SetLength(long value) 
    { 
     throw new NotImplementedException(); 
    } 

    void IDisposable.Dispose() 
    { 
     s1.Dispose(); 
     s2.Dispose(); 
    } 
} 

然後您可以使用像這樣的食鹽,文件哈希

 FileStream fs = new FileStream(@"c:\text.txt", FileMode.Open); 
     var m = new MemoryStream(ToAnsiiBytes("SALT"), false); 
     var ms = new MergedStream(fs, m); 

     var C = hasher.ComputeHash(ms); 
     PrintHash(Console.Out, C); 

與ToAnsiiBytes和PrintHash擺明實用的功能,例如:

public static void PrintHash(TextWriter op, byte[] hash) 
    { 
     foreach (byte b in hash) 
     { 
      op.Write("{0:X2}", b); 
     } 
    } 

當文件c:\的text.txt包含文本TOTO可以運行這個程序,看看文件+鹽等於相同的文字「totoSALT」

 FileStream fs = new FileStream(@"c:\text.txt", FileMode.Open); 

     var hasher = new MD5CryptoServiceProvider(); 
     var A = hasher.ComputeHash(fs); 
     PrintHash(Console.Out, A); 
     Console.Out.WriteLine(); 

     var salt = new byte[] { 0x53, 0x41, 0x4C, 0x54 }; 

     var B = hasher.ComputeHash(ToAnsiiBytes("SALT")); 
     PrintHash(Console.Out, B); 
     Console.Out.WriteLine(); 

     var m = new MemoryStream(ToAnsiiBytes("SALT"), false); 

     fs.Seek(0, SeekOrigin.Begin); 
     var ms = new MergedStream(fs, m); 

     var C = hasher.ComputeHash(ms); 
     PrintHash(Console.Out, C); 
     Console.Out.WriteLine(); 


     HashAndPrint(Console.Out, "toto"); 
     HashAndPrint(Console.Out, "totoSALT"); 
     HashAndPrint(Console.Out, "SALT"); 

與此輸出

F71DBE52628A3F83A77AB494817525C6 
8C4F4370C53E0C1E1AE9ACD577DDDBED 
308DB2451D6580FEEB09FCF2DC1CEE19 
F71DBE52628A3F83A77AB494817525C6 = toto 
308DB2451D6580FEEB09FCF2DC1CEE19 = totoSALT 
8C4F4370C53E0C1E1AE9ACD577DDDBED = SALT 
3

你可能會考慮使用HMACMD5類和設置Key屬性。一般來說,我會使用HMAC而不是標準的哈希函數,因爲它們提供了更好的安全性。

4

這是做正確的方式:

private static byte[] _emptyBuffer = new byte[0]; 

    public static byte[] CalculateMD5(Stream stream) 
    { 
     return CalculateMD5(stream, 64 * 1024); 
    } 

    public static byte[] CalculateMD5(Stream stream, int bufferSize) 
    { 
     MD5 md5Hasher = MD5.Create(); 

     byte[] buffer = new byte[bufferSize]; 
     int readBytes; 

     while ((readBytes = stream.Read(buffer, 0, bufferSize)) > 0) 
     { 
      md5Hasher.TransformBlock(buffer, 0, readBytes, buffer, 0); 
     } 

     md5Hasher.TransformFinalBlock(_emptyBuffer, 0, 0); 

     return md5Hasher.Hash; 
    }