Как воспроизвести результат System.Security.Cryptography.SHA1 в Python - PullRequest
0 голосов
/ 22 марта 2010

Вот в чем дело: я перевожу веб-сайт .NET на Python. У меня есть база данных с паролями, хэшированными с помощью утилиты System.Security.Cryptography.SHA1Managed.

Я создаю хэш в .NET со следующим кодом:

string hashedPassword = Cryptographer.CreateHash("MYHasher", userInfo.Password);

Блок MYHasher выглядит так:

<add algorithmType="System.Security.Cryptography.SHA1Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=blahblahblah"
    saltEnabled="true" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=3.0.0.0, Culture=neutral, PublicKeyToken=daahblahdahdah"
    name="MYHasher" />

Так что для данного пароля я возвращаюсь и сохраняю в базе данных 48-байтовый соленый sha1. Я предполагаю, что последние 8 байтов являются солью. Я попытался воспроизвести процесс хеширования в python, выполнив sha1 (соль + пароль) и sha1 (пароль + соль), но мне не повезло.

Мой вопрос к вам:

  1. Как используются открытые ключи?
  2. Как пароль перефразируется с помощью соли.
  3. Как создается соль? (например, когда я говорю saltEnabled = "true", какая дополнительная магия происходит?)

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

Спасибо!

Ответы [ 4 ]

4 голосов
/ 02 декабря 2014

Извините за поздний ответ, но я только что столкнулся с подобной ситуацией, пытаясь скопировать логику хеширования SHA1, используемую в блоке криптографии Enterprise Library, но с использованием Java.

Чтобы ответить на каждый ваш вопрос:

  1. Как используются открытые ключи?

    PublicKeyToken в приведенном выше блоке конфигурации используется для идентификации подписанной сборки .net со строгим именем. Это 64-битный хэш открытого ключа, который соответствует закрытому ключу, используемому для подписи сборки. ПРИМЕЧАНИЕ. Этот ключ не имеет абсолютно никакого отношения к вашей реализации для хэширования данных.

  2. Как пароль перефразируется с помощью соли.

    Последовательность событий для создания хешированного пароля с солью следующая:

    • Звоните Cryptographer.CreateHash("MYHasher",value); где "MYHasher" - это имя настроенного поставщика экземпляров System.Security.Cryptography.SHA1Managed, указанного в вашем блоке конфигурации, а value - строка, которую нужно хэшировать.

    • Вышеуказанный метод вызывает CreateHash(IHashProvider provider, string plaintext), где предоставляется разрешенное IHashProvider. Внутри этого метода запускается следующий код:

    
    byte[] bytes = Encoding.Unicode.GetBytes(plaintext);
    byte[] hash = provider.CreateHash(bytes);
    CryptographyUtility.GetRandomBytes(bytes);
    return Convert.ToBase64String(hash);
    
    
    • Аргумент value, который был передан в самом начале (который теперь является аргументом plaintext), преобразуется в байтовый массив с использованием кодировки Unicode.

    • Затем вызывается метод CreateHash(bytes) провайдера хеша SHA1 с байтовым массивом, созданным выше. Внутри этого метода выполняются следующие шаги:

    • this.CreateHashWithSalt(plaintext, (byte[]) null); вызывается, где plaintext является байтовым массивом, содержащим исходную value, переданную в верхней части стека в виде строки. Второй аргумент - массив байтов соли (который является нулевым). Внутри этого метода вызывается следующий код:

    
    this.AddSaltToPlainText(ref salt, ref plaintext);
    byte[] hash = this.HashCryptographer.ComputeHash(plaintext);
    this.AddSaltToHash(salt, ref hash);
    return hash;
    
    
    • this.AddSaltToPlainText(ref salt, ref plaintext) - первая подсказка о том, как соленый предоставленный текст. Внутри этого метода выполняется следующий код:
    
    if (!this.saltEnabled)
        return;
      if (salt == null)
        salt = CryptographyUtility.GetRandomBytes(16);
      plaintext = CryptographyUtility.CombineBytes(salt, plaintext);
    
    
    • Переменная this.saltEnabled инициализируется saltEnabled="true" в вашем блоке конфигурации. Если это правда, и если вы не указали соль, для вас будет сгенерирован байтовый массив из 16 случайных байтов (посредством вызова внешнего C API).
    • Переменная plaintext имеет соль с добавлением . например: [соль] [открытый текст]

Это очень важно отметить!

  • Комбинация соли и plaintext затем SHA1-хешируется путем вызова this.HashCryptographer.ComputeHash(plaintext);. Это создаст массив длиной 20 байт.

  • Затем соль снова добавляется к 20-байтовому массиву, созданному ранее с помощью вызова this.AddSaltToHash(salt, ref hash);, чтобы получить массив длиной 36 байт.

  • Возвращение в стек в конечном итоге приведет вас к вызову return Convert.ToBase64String(hash); внутри метода CreateHash(). Это вернет строковое представление Base64 соленого хэшированного значения SHA1 + соль, которое было предоставлено.

Формула: Base64 (соль + SHA1 (соль + значение))

  1. Как создается соль? (например, когда я говорю saltEnabled = "true", какая дополнительная магия происходит?)

    Ответ на этот вопрос был дан в вопросе 2, а именно на вызов CryptographyUtility.GetRandomBytes(16);, который в конечном итоге вызывает библиотеку C:

[DllImport("QCall", CharSet = CharSet.Unicode)] private static extern void GetBytes(SafeProvHandle hProv, byte[] randomBytes, int count);

Надеюсь, это каким-то образом поможет!

1 голос
/ 23 марта 2010

При использовании перегрузки string CreateHash(string, string) происходит следующее:

  1. Строка преобразуется в байты с использованием UTF16 (с использованием Encoding.Unicode.GetBytes ()).
  2. Генерируется случайная 16-байтовая соль.
  3. Соль добавляется к преобразованной строке и хэшируется.
  4. Соль добавляется к хешу.
  5. Хеш + сольпреобразуется обратно в строку, используя base64 (используя Convert.ToBase64String ()).
1 голос
/ 22 марта 2010

Согласно этой предыдущей теме , это должно быть что-то вроде sha1 (пароль + соль) + соль. Вывод SHA-1 составляет двадцать байтов, поэтому для 48 байтов это должна быть 28-байтовая соль, а не 8-байтовая соль, если только не используется какая-либо кодировка.

0 голосов
/ 26 июня 2018

Спасибо Гарет Стивенсон! в вашем ответе были все ответы, которые мне были нужны. Я был полностью потерян с этим. Мне нужно было обновить устаревший модуль, который использовал эту корпоративную библиотеку, но с компиляцией было так много проблем, что я не смог отладить код. Сохранение кода открыло множество других проблем с зависимостями и несоответствиями / версиями токенов открытого ключа. Поэтому я переписал необходимые функции, основываясь на ответе Гарета. В конце концов я нашел шифрование, используемое в файле конфигурации. Это может быть в app.config (в моем случае), web.config или в другом конфиге где-то:

<securityCryptographyConfiguration>
<hashProviders>
  <add algorithmType="System.Security.Cryptography.SHA1Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
    saltEnabled="true" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=06300324c959bce8"
    name="ABC" />
</hashProviders>

Код, который я написал:

//Because of the random salt added, each time you hash a password it will create a new result.
    public static string GetHashedValue(string password)
    {
        //this will create a new hash?
        //Hashed Password Formula: Base64(salt + Sha1(salt + value))
        var crypto = new SHA1CryptoServiceProvider();
        byte[] saltBytes = new byte[16];
        RandomNumberGenerator.Create().GetBytes(saltBytes); 

        byte[] checkPasswordBytes = Encoding.Unicode.GetBytes(password);
        byte[] tempResult = crypto.ComputeHash(saltBytes.Concat(checkPasswordBytes).ToArray()); //ComputeHash(salt + value)
        byte[] resultBytes = saltBytes.Concat(tempResult).ToArray();  //salt + ComputeHash(salt + value)

        return Convert.ToBase64String(resultBytes);
    }

и для проверки правильности пароля:

public static bool IsPasswordValid(string passwordToCheck, string savedPassword)
    {
        bool retVal = false;

        var crypto = new SHA1CryptoServiceProvider();

        //get the salt, which is part of the saved password. These are the first 16 bytes.
        byte[] storedPasswordBytes = Convert.FromBase64String(savedPassword);
        byte[] saltBytes = new byte[16];
        Array.Copy(storedPasswordBytes, saltBytes, 16);

        //hash the password that you want to check with the same salt and the same algoritm:
        byte[] checkPasswordBytes = Encoding.Unicode.GetBytes(passwordToCheck);
        byte[] tempResult = crypto.ComputeHash(saltBytes.Concat(checkPasswordBytes).ToArray()); //ComputeHash(salt + value)
        byte[] resultBytes = saltBytes.Concat(tempResult).ToArray();  //salt + ComputeHash(salt + value)
        string resultString = Convert.ToBase64String(resultBytes);

        if (savedPassword == resultString)
        {
            retVal = true;
        }

        return retVal;
    }

И это как раз перед тем, как я подумал, что мне придется сбросить пароли всех моих клиентов ... Я надеюсь, это однажды спасет и кого-то еще!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...