在流上加盐 C# MD5 ComputeHash

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/878837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 05:10:11  来源:igfitidea点击:

Salting a C# MD5 ComputeHash on a stream

c#hashstreammd5salt

提问by Keith Marsh

I can't see any way to salt a MD5.ComputeHash(Stream). Am I missing some way of injecting bytes into the HashAlgorithm?

我看不出有什么方法可以对 MD5.ComputeHash(Stream) 加盐。我是否缺少将字节注入 HashAlgorithm 的某种方法?

I tried performing a ComputeHash(byte[]) before performing the stream compute, but, unsurprisingly, it had no effect. Any ideas (apart from modifying the file)?

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

Thanks for your time.

谢谢你的时间。

addendumJust to be a little more specific, I want to use a stream to get a hash on a large file that I don't want to load into memory.

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

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

回答by Dabblernl

I think you can use a syntax like:

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

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

回答by bbmud

The answer to the lack of examples is in my opinion: you don't really need to salt it.

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

The hash algorithm like MD5 takes a table of bytes of arbitrary length and converts it to a table of bytes of known length - the operation is not easily reversible and small changes to the input table cause unpredictable changes in the output table:

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

input => MD5 => output

输入 => MD5 => 输出

The purpose of salting is protection against attacks where user has already precomputed table of hash results (rainbow tables). By introducing small changes in the input, the results are change drastically, so even if attacker knows the hash result and the salt, it is very difficult to guess the input:

加盐的目的是防止用户已经预先计算哈希结果彩虹表)的攻击。通过在输入中引入微小的变化,结果会发生剧烈的变化,因此即使攻击者知道哈希结果和盐,也很难猜测输入:

input + salt => MD5 => output

输入 + 盐 => MD5 => 输出

The reason for hashing files is to compute a checksum. E.g. you publish a file on your web page along with the hash result. User then downloads a file, runs it through MD5 and compares the result with your published result. It would be very difficult to tamper with the file, because each manipulation would change the resulting hash.

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

Salting is not necessary here, because you would have to publish the salt with the resulting hash, so that the user can repeat the hashing operation.

此处不需要加盐,因为您必须发布带有结果散列的盐,以便用户可以重复散列操作。

If you really need to introduce salting, just change the input stream in the repeatable way, e.g. add one (with overflow) to each byte.

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

回答by Simeon Pilgrim

To avoid pull all the file into memory like Dabblernl's solution you'll want to use a FileStream as is discussed in this SO question Computing MD5SUM of large files in C#, but the MD5CryptoServiceProvider does not allow you to add extra data to the end.

为避免像 Dabblernl 的解决方案一样将所有文件拉入内存,您需要使用 FileStream,如此 SO 问题Computing MD5SUM of large files in C# 中所述,但 MD5CryptoServiceProvider 不允许您将额外数据添加到最后。

Thus you need a merged stream like this:

因此你需要一个像这样的合并流:

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();
    }
}

Which you can then use like this to salt you file hash

然后你可以像这样使用它来为你的文件哈希加盐

        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);

with ToAnsiiBytes and PrintHash just being utility functions as such:

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);
    }

and

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

when the file c:\text.txt contains the text totoyou can run this code to see that the file + salt equals the same as the text "totoSALT"

当文件 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");

with this output

有了这个输出

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

回答by Jeff Moser

You might consider using the HMACMD5class and setting the Keyproperty instead. In general, I'd go with an HMAC rather than the standard hash function since they offer a bit better security.

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

回答by DxCK

this is the right way to do it:

这是正确的方法:

    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;
    }