【问题标题】:Salting a C# MD5 ComputeHash on a stream在流上加盐 C# MD5 ComputeHash
【发布时间】:2015-01-16 21:03:43
【问题描述】:

我看不到任何方法来加盐 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 ();

【问题讨论】:

  • 我不知道是否有特定的方法可以对算法进行加盐。但是您可以轻松地创建自己的 Stream 类来包装给定的 Stream 对象。当被要求提供前几个字节时,包装器对象可以提供盐字节,然后开始提供底层流字节。
  • 在这种情况下,根据给定的信息,salt 是不必要的,并且可能无法实现(请记住,salt 需要 salthashed 数据一起存储)。跨度>

标签: c# hash stream md5 salt


【解决方案1】:

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

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

输入 => MD5 => 输出

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

输入 + 盐 => MD5 => 输出

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

这里不需要加盐,因为您必须使用生成的散列发布盐,以便用户可以重复散列操作。

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

【讨论】:

  • 还有一种情况,您希望通过比较文件存储的 MD5 哈希值与其计算的 MD5 值来防止文件被更改。通过使用盐,您可以公开存储已知的 MD5。如果没有加盐,用户可以重新计算和替换 MD5,因此不会检测到任何变化。谢谢大家花时间回答。
  • 我仍然不清楚这条评论。您希望“公开”存储散列,并防止攻击者通过保密盐来对其进行变异。但是人们需要知道盐来验证哈希。听起来真正想要的是数字签名。 (尽管让攻击者更改任何类型的完整性凭证都会导致相当明显的拒绝服务漏洞。)
【解决方案2】:

这是正确的做法:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        为避免像 Dabblernl 的解决方案那样将所有文件拉入内存,您需要使用这个 SO 问题 Computing MD5SUM of large files in C# 中讨论的 FileStream,但 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
        

        【讨论】:

          猜你喜欢
          • 2013-08-29
          • 2014-05-03
          • 2010-11-30
          • 2014-05-02
          • 2012-08-26
          • 2015-07-04
          • 2019-10-02
          • 2011-10-27
          • 2012-03-01
          相关资源
          最近更新 更多