Миграция внутренней аутентификации в ASP.NET Core Identity
Я работаю над обновлением очень старого сайта с классического ASP до ASP.NET Core. Часть этого состоит в том, чтобы перенести пользователей (чьи пароли хранятся в незашифрованном виде, естественно) в Identity. Мы используем проект модульного тестирования для управления миграцией, но этот проект, понятно, не может получить доступ к Identity.
Какой лучший вариант здесь? Основная проблема, которую я вижу, - это получение паролей в правильном формате, но я думаю, что я мог бы взглянуть на источник удостоверения ASP.NET и имитировать функциональность, чтобы правильно хэшировать все в процессе миграции. Есть ли способ лучше?
2 ответа
Алгоритм хеширования, используемый ASP.NET Identity 3, здесь. Однако запустить ASP.NET Idetity нелегко, если вы не скопируете его зависимости в свой проект.
private static byte[] HashPasswordV3(string password,
RandomNumberGenerator rng, KeyDerivationPrf prf,
int iterCount, int saltSize, int numBytesRequested)
{
// Produce a version 3 (see comment above) text hash.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
byte[] subkey = KeyDerivation.Pbkdf2(
password, salt, prf, iterCount, numBytesRequested);
var outputBytes = new byte[13 + salt.Length + subkey.Length];
outputBytes[0] = 0x01; // format marker
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
return outputBytes;
}
private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, out int iterCount)
{
iterCount = default(int);
try
{
// Read header information
KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1);
iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5);
int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9);
// Read the salt: must be >= 128 bits
if (saltLength < 128 / 8)
{
return false;
}
byte[] salt = new byte[saltLength];
Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length);
// Read the subkey (the rest of the payload): must be >= 128 bits
int subkeyLength = hashedPassword.Length - 13 - salt.Length;
if (subkeyLength < 128 / 8)
{
return false;
}
byte[] expectedSubkey = new byte[subkeyLength];
Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);
// Hash the incoming password and verify it
byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength);
return ByteArraysEqual(actualSubkey, expectedSubkey);
}
catch
{
// This should never occur except in the case of a malformed payload, where
// we might go off the end of the array. Regardless, a malformed payload
// implies verification failed.
return false;
}
}
Пароли в виде простого текста, так что вы можете просто загрузить каждого пользователя из текущей базы данных и добавить его в новую базу данных, используя UserManager
класс в идентичности.
UserManager
класс имеет CreateAsync
метод, который принимает пароль, и он будет обрабатывать хеширование для вас.
Пример:
var user = new IdentityUser // or whatever your user class is
{
UserName = userName,
Email = email,
// set other required properties
};
var result = await userManager.CreateAsync(user, password);
Я бы порекомендовал запустить его вручную в отдельной программе как разовую задачу. Вы не хотите, чтобы эта процедура жила в новом приложении.