Методы хранения паролей - Алгоритмы шифрования и примеры
ОГЛАВЛЕНИЕ
Алгоритмы шифрования и примеры
Каркас .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;
}
}
Заключение
У вас уже есть достаточно информации, чтобы принимать обоснованные решения насчет хранения паролей.
Простые рекомендации:
• Если надо восстанавливать пароли, используйте шифрование.
• Если не надо восстанавливать пароли, используйте хеши (более безопасно).
• Что бы вы ни делали, солите пароли.