Yanıtlar:
GÜNCELLEME : BU CEVAP CİDDİ ŞEKİLDE GEÇMİŞTİR . Lütfen bunun yerine https://stackoverflow.com/a/10402129/251311'deki önerileri kullanın.
Ya kullanabilirsiniz
var md5 = new MD5CryptoServiceProvider();
var md5data = md5.ComputeHash(data);
veya
var sha1 = new SHA1CryptoServiceProvider();
var sha1data = sha1.ComputeHash(data);
data
Bayt dizisi olarak almak için kullanabilirsiniz
var data = Encoding.ASCII.GetBytes(password);
ve geri dize almak md5data
veyasha1data
var hashedPassword = ASCIIEncoding.GetString(md5data);
md5
neredeyse her tür görev için yeterince iyi. Güvenlik açıkları aynı zamanda çok özel durumları da ifade eder ve neredeyse saldırganın kriptografi hakkında çok şey bilmesini gerektirir.
Buradaki diğer cevapların çoğu, günümüzün en iyi uygulamaları ile biraz güncel değil. Bu nedenle, Rfc2898DeriveBytes
şifreleri saklamak ve doğrulamak için PBKDF2 / kullanma uygulamasıdır . Aşağıdaki kod, bu gönderide bağımsız bir sınıftadır: Tuzlu bir şifre karmasının nasıl saklanacağına dair başka bir örnek . Temel bilgiler gerçekten çok kolay, bu yüzden burada ayrıntılı olarak açıklanmıştır:
ADIM 1 Kriptografik bir PRNG ile tuz değerini oluşturun:
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
ADIM 2 Rfc2898DeriveBytes'i oluşturun ve hash değerini alın:
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(20);
ADIM 3 Daha sonra kullanmak için tuz ve şifre baytlarını birleştirin:
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
ADIM 4 Birleşik tuz + hash'i depolama için bir dizeye çevirin
string savedPasswordHash = Convert.ToBase64String(hashBytes);
DBContext.AddUser(new User { ..., Password = savedPasswordHash });
ADIM 5 Kullanıcı tarafından girilen parolayı kayıtlı bir parolayla karşılaştırarak doğrulayın
/* Fetch the stored value */
string savedPasswordHash = DBContext.GetUser(u => u.UserName == user).Password;
/* Extract the bytes */
byte[] hashBytes = Convert.FromBase64String(savedPasswordHash);
/* Get the salt */
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(20);
/* Compare the results */
for (int i=0; i < 20; i++)
if (hashBytes[i+16] != hash[i])
throw new UnauthorizedAccessException();
Not: Özel uygulamanızın performans gereksinimlerine bağlı olarak değer 100000
düşürülebilir. Minimum değer civarında olmalıdır 10000
.
Csharptest.net'in harika cevabına dayanarak, bunun için bir Sınıf yazdım:
public static class SecurePasswordHasher
{
/// <summary>
/// Size of salt.
/// </summary>
private const int SaltSize = 16;
/// <summary>
/// Size of hash.
/// </summary>
private const int HashSize = 20;
/// <summary>
/// Creates a hash from a password.
/// </summary>
/// <param name="password">The password.</param>
/// <param name="iterations">Number of iterations.</param>
/// <returns>The hash.</returns>
public static string Hash(string password, int iterations)
{
// Create salt
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[SaltSize]);
// Create hash
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
var hash = pbkdf2.GetBytes(HashSize);
// Combine salt and hash
var hashBytes = new byte[SaltSize + HashSize];
Array.Copy(salt, 0, hashBytes, 0, SaltSize);
Array.Copy(hash, 0, hashBytes, SaltSize, HashSize);
// Convert to base64
var base64Hash = Convert.ToBase64String(hashBytes);
// Format hash with extra information
return string.Format("$MYHASH$V1${0}${1}", iterations, base64Hash);
}
/// <summary>
/// Creates a hash from a password with 10000 iterations
/// </summary>
/// <param name="password">The password.</param>
/// <returns>The hash.</returns>
public static string Hash(string password)
{
return Hash(password, 10000);
}
/// <summary>
/// Checks if hash is supported.
/// </summary>
/// <param name="hashString">The hash.</param>
/// <returns>Is supported?</returns>
public static bool IsHashSupported(string hashString)
{
return hashString.Contains("$MYHASH$V1$");
}
/// <summary>
/// Verifies a password against a hash.
/// </summary>
/// <param name="password">The password.</param>
/// <param name="hashedPassword">The hash.</param>
/// <returns>Could be verified?</returns>
public static bool Verify(string password, string hashedPassword)
{
// Check hash
if (!IsHashSupported(hashedPassword))
{
throw new NotSupportedException("The hashtype is not supported");
}
// Extract iteration and Base64 string
var splittedHashString = hashedPassword.Replace("$MYHASH$V1$", "").Split('$');
var iterations = int.Parse(splittedHashString[0]);
var base64Hash = splittedHashString[1];
// Get hash bytes
var hashBytes = Convert.FromBase64String(base64Hash);
// Get salt
var salt = new byte[SaltSize];
Array.Copy(hashBytes, 0, salt, 0, SaltSize);
// Create hash with given salt
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
byte[] hash = pbkdf2.GetBytes(HashSize);
// Get result
for (var i = 0; i < HashSize; i++)
{
if (hashBytes[i + SaltSize] != hash[i])
{
return false;
}
}
return true;
}
}
Kullanımı:
// Hash
var hash = SecurePasswordHasher.Hash("mypassword");
// Verify
var result = SecurePasswordHasher.Verify("mypassword", hash);
Örnek bir karma şu olabilir:
$MYHASH$V1$10000$Qhxzi6GNu/Lpy3iUqkeqR/J1hh8y/h5KPDjrv89KzfCVrubn
Gördüğünüz gibi, kolay kullanım için iterasyonları ve yükseltmemiz gerekirse bunu yükseltme olasılığını da ekledim.
.Net çekirdeği ile ilgileniyorsanız, Kod İncelemesinde bir .net çekirdek sürümüm de var .
V1
ve V2
hangi doğrulama ihtiyacınız yöntemi.
Parola şifrelemem için bir karma ve bir tuz kullanıyorum (Asp.Net Üyeliğinin kullandığı karmayla aynıdır):
private string PasswordSalt
{
get
{
var rng = new RNGCryptoServiceProvider();
var buff = new byte[32];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
}
private string EncodePassword(string password, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(password);
byte[] src = Encoding.Unicode.GetBytes(salt);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inarray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inarray);
}
public class CryptographyProcessor
{
public string CreateSalt(int size)
{
//Generate a cryptographic random number.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
public string GenerateHash(string input, string salt)
{
byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
SHA256Managed sHA256ManagedString = new SHA256Managed();
byte[] hash = sHA256ManagedString.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
public bool AreEqual(string plainTextInput, string hashedInput, string salt)
{
string newHashedPin = GenerateHash(plainTextInput, salt);
return newHashedPin.Equals(hashedInput);
}
}
KeyDerivation.Pbkdf2 kullanmanın Rfc2898DeriveBytes'ten daha iyi olduğunu düşünüyorum.
Örnek ve açıklama: ASP.NET Core'da karma parolalar
using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
public class Program
{
public static void Main(string[] args)
{
Console.Write("Enter a password: ");
string password = Console.ReadLine();
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
Console.WriteLine($"Hashed: {hashed}");
}
}
/*
* SAMPLE OUTPUT
*
* Enter a password: Xtw9NMgx
* Salt: NZsP6NnmfBuYeJrrAKNuVQ==
* Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
*/
Bu, makaleden örnek bir koddur. Ve minimum güvenlik seviyesidir. Arttırmak için KeyDerivationPrf yerine kullanırdım.HMACSHA1 parametresi
KeyDerivationPrf.HMACSHA256 veya KeyDerivationPrf.HMACSHA512.
Parola karmaşasından ödün vermeyin. Parola hash hacklemeyi optimize etmek için matematiksel olarak sağlam birçok yöntem vardır. Sonuçlar felaket olabilir. Bir kötü niyetli kişi, kullanıcılarınızın şifre karma tablosunu ele geçirdiğinde, algoritmanın zayıf olması veya uygulamanın yanlış olması nedeniyle şifreleri kırması nispeten daha kolay olacaktır. Şifreleri kırmak için çok zamanı (zaman x bilgisayar gücü) var. Parola hashingi, "çok fazla zamanı" " mantıksız bir süreye" dönüştürmek için kriptografik olarak güçlü olmalıdır .
Eklenecek bir nokta daha
Karma doğrulama zaman alır (ve iyidir). Kullanıcı yanlış kullanıcı adı girdiğinde, kullanıcı adının yanlış olup olmadığını kontrol etmek hiç zaman almaz. Kullanıcı adı doğru olduğunda parola doğrulamasını başlatırız - bu nispeten uzun bir süreçtir.
Bir bilgisayar korsanı için, kullanıcının var olup olmadığını anlamak çok kolay olacaktır.
Kullanıcı adı yanlış olduğunda hemen cevap vermemeye dikkat edin.
Söylemeye gerek yok: neyin yanlış olduğuna asla cevap vermeyin. Sadece genel "Kimlik bilgileri yanlış".
@ csharptest.net ve Christian Gollhardt'ın cevapları harika, çok teşekkür ederim. Ancak bu kodu milyonlarca kayıtla üretimde çalıştırdıktan sonra bir bellek sızıntısı olduğunu keşfettim. RNGCryptoServiceProvider ve Rfc2898DeriveBytes sınıfları IDisposable'dan türetilir, ancak bunları elden çıkarmayız. Birinin elden çıkarılmış versiyona ihtiyacı olursa çözümümü bir cevap olarak yazacağım.
public static class SecurePasswordHasher
{
/// <summary>
/// Size of salt.
/// </summary>
private const int SaltSize = 16;
/// <summary>
/// Size of hash.
/// </summary>
private const int HashSize = 20;
/// <summary>
/// Creates a hash from a password.
/// </summary>
/// <param name="password">The password.</param>
/// <param name="iterations">Number of iterations.</param>
/// <returns>The hash.</returns>
public static string Hash(string password, int iterations)
{
// Create salt
using (var rng = new RNGCryptoServiceProvider())
{
byte[] salt;
rng.GetBytes(salt = new byte[SaltSize]);
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations))
{
var hash = pbkdf2.GetBytes(HashSize);
// Combine salt and hash
var hashBytes = new byte[SaltSize + HashSize];
Array.Copy(salt, 0, hashBytes, 0, SaltSize);
Array.Copy(hash, 0, hashBytes, SaltSize, HashSize);
// Convert to base64
var base64Hash = Convert.ToBase64String(hashBytes);
// Format hash with extra information
return $"$HASH|V1${iterations}${base64Hash}";
}
}
}
/// <summary>
/// Creates a hash from a password with 10000 iterations
/// </summary>
/// <param name="password">The password.</param>
/// <returns>The hash.</returns>
public static string Hash(string password)
{
return Hash(password, 10000);
}
/// <summary>
/// Checks if hash is supported.
/// </summary>
/// <param name="hashString">The hash.</param>
/// <returns>Is supported?</returns>
public static bool IsHashSupported(string hashString)
{
return hashString.Contains("HASH|V1$");
}
/// <summary>
/// Verifies a password against a hash.
/// </summary>
/// <param name="password">The password.</param>
/// <param name="hashedPassword">The hash.</param>
/// <returns>Could be verified?</returns>
public static bool Verify(string password, string hashedPassword)
{
// Check hash
if (!IsHashSupported(hashedPassword))
{
throw new NotSupportedException("The hashtype is not supported");
}
// Extract iteration and Base64 string
var splittedHashString = hashedPassword.Replace("$HASH|V1$", "").Split('$');
var iterations = int.Parse(splittedHashString[0]);
var base64Hash = splittedHashString[1];
// Get hash bytes
var hashBytes = Convert.FromBase64String(base64Hash);
// Get salt
var salt = new byte[SaltSize];
Array.Copy(hashBytes, 0, salt, 0, SaltSize);
// Create hash with given salt
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations))
{
byte[] hash = pbkdf2.GetBytes(HashSize);
// Get result
for (var i = 0; i < HashSize; i++)
{
if (hashBytes[i + SaltSize] != hash[i])
{
return false;
}
}
return true;
}
}
}
Kullanımı:
// Hash
var hash = SecurePasswordHasher.Hash("mypassword");
// Verify
var result = SecurePasswordHasher.Verify("mypassword", hash);
Önce Tuz Üretmek için aşağıdaki sınıfı kullanın. Her kullanıcının farklı bir tuza sahip olması gerekir, onu diğer kullanıcı özellikleriyle birlikte veritabanına kaydedebiliriz. Tur değeri, şifrenin kaç kez karma haline getirileceğine karar verir.
Daha fazla ayrıntı için: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes.-ctor?view=netcore-3.1#System_Security_Cryptography_Rfc2898DeriveBytes__ctor_System_Byte___System_Byte___System_Int32 _
public class HashSaltWithRounds
{
int saltLength = 32;
public byte[] GenerateSalt()
{
using (var randomNumberGenerator = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[saltLength];
randomNumberGenerator.GetBytes(randomNumber);
return randomNumber;
}
}
public string HashDataWithRounds(byte[] password, byte[] salt, int rounds)
{
using(var rfc2898= new Rfc2898DeriveBytes(password, salt, rounds))
{
return Convert.ToBase64String(rfc2898.GetBytes(32));
}
}
}
Aşağıdaki gibi bir konsol uygulamasından çağırabiliriz. Aynı tuzu kullanarak şifreyi iki kez hashledim .
public class Program
{
public static void Main(string[] args)
{
int numberOfIterations = 99;
var hashFunction = new HashSaltWithRounds();
string password = "Your Password Here";
byte[] salt = hashFunction.GenerateSalt();
var hashedPassword1 = hashFunction.HashDataWithRounds(Encoding.UTF8.GetBytes(password), salt, numberOfIterations);
var hashedPassword2 = hashFunction.HashDataWithRounds(Encoding.UTF8.GetBytes(password), salt, numberOfIterations);
Console.WriteLine($"hashedPassword1 :{hashedPassword1}");
Console.WriteLine($"hashedPassword2 :{hashedPassword2}");
Console.WriteLine(hashedPassword1.Equals(hashedPassword2));
Console.ReadLine();
}
}
using
ifadeye koyduğunuzdan veya çağırdığınızdan emin olunClear()
.