Методы хранения паролей - Алгоритмы шифрования и примеры

ОГЛАВЛЕНИЕ

Алгоритмы шифрования и примеры

Каркас .NET (System.Security.Cryptography) включает в себя встроенную поддержку нескольких алгоритмов шифрования:

•    DES – старый, вышедший из употребления.
•    TripleDES – старый, но все еще стойкий.
•    RC2 – старый, но все еще полезный.
•    Rijndael (AES) - современный.

Пример – кодирование строки:

// Кодируется строка ‘message(сообщение)’
// ScrambleKey и ScambleIV случайно генерируются
// RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
// rc2.GenerateIV();
// ScrambleIV = rc2.IV;
// rc2.GenerateKey();
// ScrambleKey = rc2.Key
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP =
                 new RC2CryptoServiceProvider();

//Данные преобразуются в массив байтов.
byte[] toEncrypt = textConverter.GetBytes(message);

//Получается шифратор.
ICryptoTransform encryptor =
   rc2CSP.CreateEncryptor(ScrambleKey, ScrambleIV);
           
//Шифруются данные.
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt,
                         encryptor, CryptoStreamMode.Write);

//Все данные записываются в криптопоток, и он сбрасывается.
//Длина кодируется в виде первых 4 байтов
byte[] length = new byte[4];
length[0] = (byte)(message.Length & 0xFF);
length[1] = (byte)((message.Length >> 8) & 0xFF);
length[2] = (byte)((message.Length >> 16) & 0xFF);
length[3] = (byte)((message.Length >> 24) & 0xFF);
csEncrypt.Write(length, 0, 4);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();

//Получается зашифрованный массив байтов.
byte[] encrypted = msEncrypt.ToArray();

Пример – раскодирование строки:

// Раскодируется зашифрованный byte[]
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
//Получается дешифратор, использующий тот же ключ и проверку идентичности, что и шифратор.
ICryptoTransform decryptor =
       rc2CSP.CreateDecryptor(ScrambleKey, ScrambleIV);

//Дешифруется ранее зашифрованное сообщение с помощью дешифратора,
// полученного в шаге выше.
MemoryStream msDecrypt = new MemoryStream(encrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
                         decryptor, CryptoStreamMode.Read);

byte[] fromEncrypt = new byte[encrypted.Length-4];

//Читаются данные из криптопотока.
byte[] length = new byte[4];
csDecrypt.Read(length, 0, 4);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
int len = (int)length[0] | (length[1] << 8) |
          (length[2] << 16) | (length[3] << 24);

//Массив байтов преобразуется обратно в строку.
return textConverter.GetString(fromEncrypt).Substring(0, len);

Алгоритмы хеширования и примеры

Каркас .NET (System.Security.Cryptography) включает в себя встроенную поддержку нескольких криптографических хеш-функций:

•    MD5 – взломан, старайтесь избегать.
•    RIPEMD160 – приемлем, вообще не популярен в США.
•    SHA1 – приемлем, но 160 битные выходные данные считаются слишком короткими.
•    SHA256 – рекомендуемый минимум.
•    SHA384 (SHA2) - рекомендуется.
•    SHA512 – очень надежен, но не обеспечивает дополнительной защиты в сравнении с SHA384.

Пример – хеширование строки:

public byte[] EncryptPassword(string userName, string password, 
       int encryptionVersion, byte[] salt1, byte[] salt2)
{
    string tmpPassword = null;

    switch(encryptionVersion)
    {
        case 2: // пароль + много соли
            tmpPassword = Convert.ToBase64String(salt1)
             + Convert.ToBase64String(salt2)
             + userName.ToLower() + password;
            break;
        case 1: // логин в качестве соли
            tmpPassword  = userName.ToLower() + password;
            break;
        case 0: // без соли
        default:
            tmpPassword = password;
            break;
    }

    //Строка пароля преобразуется в массив байтов.
    UTF8Encoding textConverter = new UTF8Encoding();
    byte[] passBytes = textConverter.GetBytes(tmpPassword);

    //Возвращаются зашифрованные байты
    if (encryptionVersion == 2)
        return new SHA384Managed().ComputeHash(passBytes);
    else
        return new MD5CryptoServiceProvider().ComputeHash(passBytes);
}

Пример – сравнение двух хешей на равенство:

// Сравниваются два массива на равенство
// Можно добавить сравнение длины, но обычно
// размер всех хешей одинаков.
private bool PasswordsMatch(byte[] psswd1, byte[] psswd2)
{
    try
    {
        for(int i = 0; i < psswd1.Length; i++)
        {
            if(psswd1[i] != psswd2[i])
                return false;
        }
        return true;
    }
    catch(IndexOutOfRangeException)
    {
        return false;
    }
}

Заключение

У вас уже есть достаточно информации, чтобы принимать обоснованные решения насчет хранения паролей.

Простые рекомендации:

•    Если надо восстанавливать пароли, используйте шифрование.
•    Если не надо восстанавливать пароли, используйте хеши (более безопасно).
•    Что бы вы ни делали, солите пароли.