Соление C # MD5 ComputeHash на потоке - PullRequest
3 голосов
/ 18 мая 2009

Я не вижу способа засолить 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 ]

5 голосов
/ 18 мая 2009

Ответ на отсутствие примеров на мой взгляд: вам не нужно солить.

Алгоритм хеширования, такой как MD5, берет таблицу байтов произвольной длины и преобразует ее в таблицу байтов известной длины - операция не является легко обратимой, а небольшие изменения во входной таблице вызывают непредсказуемые изменения в выходной таблице:

вход => MD5 => выход

Целью засолки является защита от атак, когда пользователь уже предварительно вычислил таблицу результатов хеширования ( радужные таблицы ). Внося небольшие изменения во входные данные, результаты резко меняются, поэтому даже если злоумышленник знает результат хеширования и соль, очень сложно угадать ввод:

вход + соль => MD5 => выход

Причиной хэширования файлов является вычисление контрольной суммы. Например. Вы публикуете файл на своей веб-странице вместе с результатом хеширования. Затем пользователь загружает файл, запускает его через MD5 и сравнивает результат с вашим опубликованным результатом. Было бы очень трудно вмешаться в файл, потому что каждая манипуляция изменила бы полученный хеш.

Соль здесь не нужна, потому что вам нужно будет опубликовать соль с полученным хешем, чтобы пользователь мог повторить операцию хеширования.

Если вам действительно нужно ввести засолку, просто измените входной поток повторяющимся образом, например, добавьте по одному (с переполнением) к каждому байту.

4 голосов
/ 14 ноября 2009

это правильный способ сделать это:

    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 голосов
/ 23 июня 2009

Возможно, вы захотите использовать класс HMACMD5 и установить вместо него свойство Key . В целом, я бы предпочел использовать HMAC, а не стандартную хеш-функцию, поскольку они предлагают немного лучшую защиту.

1 голос
/ 18 мая 2009

Я думаю, вы можете использовать такой синтаксис, как:

byte[] saltedBytes;
//TODO: fill the saltedBytes;
var hasher=new MD5CryptoServiceProvider();
var memoryStream=new MemoryStream(saltedBytes);
hasher.ComputeHash(memoryStream);
memoryStream.Close;
0 голосов
/ 23 июня 2009

Чтобы избежать вытягивания всего файла в память, как решение Dabblernl, вам нужно использовать FileStream, как описано в этом вопросе SO Вычисление MD5SUM больших файлов в 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 , вы можете запустить этот код, чтобы увидеть, что file + 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
...