在流上腌制C#MD5 ComputeHash

时间:2009-05-18 17:23:11

标签: c# hash stream md5 salt

我看不出任何方法来加密MD5.ComputeHash(Stream)。 我错过了一些将字节注入HashAlgorithm的方法吗?

我在执行流计算之前尝试执行ComputeHash(byte []),但不出所料,它没有任何效果。任何想法(除了修改文件)?

感谢您的时间。

附录 为了更具体一点,我想使用一个流来获取一个我不想加载到内存中的大文件的哈希。

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

5 个答案:

答案 0 :(得分:5)

缺乏示例的答案在我看来:你真的不需要加盐。

像MD5这样的哈希算法采用任意长度的字节表并将其转换为已知长度的字节表 - 操作不易反转,输入表的小变化会导致输出表中出现不可预测的变化: / p>

input => MD5 =>输出

salting的目的是防止攻击,其中用户已经预先计算了哈希结果表(rainbow tables)。通过在输入中引入微小的变化,结果会发生巨大变化,因此即使攻击者知道散列结果和盐,也很难猜测输入:

input + salt => MD5 =>输出

散列文件的原因是计算校验和。例如。您在网页上发布文件以及哈希结果。然后,用户下载文件,通过MD5运行,并将结果与​​发布的结果进行比较。篡改文件非常困难,因为每次操作都会改变生成的哈希值。

这里不需要Salting,因为您必须使用生成的哈希发布salt,以便用户可以重复哈希操作。

如果您确实需要引入salting,只需以可重复的方式更改输入流,例如为每个字节添加一个(带溢出)。

答案 1 :(得分: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;
    }

答案 2 :(得分:3)

您可以考虑使用HMACMD5类并设置Key属性。一般来说,我会使用HMAC而不是标准哈希函数,因为它们提供了更好的安全性。

答案 3 :(得分:1)

我认为您可以使用如下语法:

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

答案 4 :(得分:0)

为了避免像Dabblernl的解决方案一样将所有文件拉入内存,您将需要使用FileSream,如本问题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只是实用功能:

    static void HashAndPrint(TextWriter op, string text)
    {
        MD5 md5 = new MD5CryptoServiceProvider();

        byte[] bytes = ToAnsiiBytes(text);

        byte[] hash = md5.ComputeHash(bytes);

        PrintHash(Console.Out, hash);
        Console.Out.WriteLine( " = {0}", text);
    }

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

当文件c:\ text.txt包含文本 toto 时,您可以运行此代码以查看文件+ salt与文本“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