Шифрование строки в .NET Core
Я хотел бы зашифровать строку в .NET Core с помощью ключа. У меня сценарий клиент/сервер и вы хотите зашифровать строку на клиенте, отправить ее на сервер и расшифровать.
Поскольку .NET Core все еще находится на ранней стадии (например, Rijndael пока недоступен), каковы мои варианты?
Ответы
Ответ 1
Вы действительно никогда не должны использовать Rijndael/RijndaelManaged в .NET. Если вы используете его с значением BlockSize, равным 128 (это значение по умолчанию), вы используете AES, так как я объясняется в аналогичном вопросе.
Симметричные параметры шифрования, доступные в .NET Core, следующие:
- AES (System.Security.Cryptography.Aes.Create())
- 3DES (System.Security.Cryptography.TripleDES.Create())
А для асимметричного шифрования
- RSA (System.Security.Cryptography.RSA.Create())
В частности, на .NET Core фабрики - лучший способ, потому что они вернут объект, который работает в текущей исполняемой операционной системе. Например, RSACng является общедоступным, но работает только в Windows; и RSAOpenSsl является общедоступным, но поддерживается только в Linux и macOS.
Ответ 2
На это уже есть ответ, но я думаю, что мы можем предложить более простое решение.
Если вы просто хотите защитить свои данные, в .NET Core есть реализация для этого, которая избавляет вас от головной боли при шифровании; DataProtectionProvider
.
В Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection(); //Add this
[..]
services.AddMvc();
}
При желании можно указать алгоритмы (используя Microsoft.AspNetCore.DataProtection
), используемые для шифрования и проверки, например:
services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
Затем зашифруйте/расшифруйте, используя службу как таковую:
public class CipherService : ICipherService
{
private readonly IDataProtectionProvider _dataProtectionProvider;
private const string Key = "my-very-long-key-of-no-exact-size";
public CipherService(IDataProtectionProvider dataProtectionProvider)
{
_dataProtectionProvider = dataProtectionProvider;
}
public string Encrypt(string input)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Protect(input);
}
public string Decrypt(string cipherText)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Unprotect(cipherText);
}
}
Edit
Как упомянуто в комментариях ниже, было бы неплохо понять, что использование DataProtectionProvider, подобного этому, будет работать только на той же машине с ключами, хранящимися на локальном диске.
Ответ 3
Вот тривиальный пример без аутентификации:
var text = "Hello World";
var buffer = Encoding.UTF8.GetBytes(text);
var iv = GetRandomData(128);
var keyAes = GetRandomData(256);
byte[] result;
using (var aes = Aes.Create())
{
aes.Key = keyAes;
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
result = resultStream.ToArray();
}
}
Для генерации ключей:
private static byte[] GetRandomData(int bits)
{
var result = new byte[bits / 8];
RandomNumberGenerator.Create().GetBytes(result);
return result;
}
Ответ 4
У меня другой подход, когда я хочу зашифровать строку ключом и получить зашифрованную строку, которую я могу снова расшифровать по тому же ключу. Смотрите следующие методы расширения:
public static string Encrypt(this string text, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(text))
throw new ArgumentException("The text must have valid value.", nameof(text));
var buffer = Encoding.UTF8.GetBytes(text);
var hash = new SHA512CryptoServiceProvider();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, encryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(buffer))
{
plainStream.CopyTo(aesStream);
}
var result = resultStream.ToArray();
var combined = new byte[aes.IV.Length + result.Length];
Array.ConstrainedCopy(aes.IV, 0, combined, 0, aes.IV.Length);
Array.ConstrainedCopy(result, 0, combined, aes.IV.Length, result.Length);
return Convert.ToBase64String(combined);
}
}
}
public static string Decrypt(this string encryptedText, string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must have valid value.", nameof(key));
if (string.IsNullOrEmpty(encryptedText))
throw new ArgumentException("The encrypted text must have valid value.", nameof(encryptedText));
var combined = Convert.FromBase64String(encryptedText);
var buffer = new byte[combined.Length];
var hash = new SHA512CryptoServiceProvider();
var aesKey = new byte[24];
Buffer.BlockCopy(hash.ComputeHash(Encoding.UTF8.GetBytes(key)), 0, aesKey, 0, 24);
using (var aes = Aes.Create())
{
if (aes == null)
throw new ArgumentException("Parameter must not be null.", nameof(aes));
aes.Key = aesKey;
var iv = new byte[aes.IV.Length];
var ciphertext = new byte[buffer.Length - iv.Length];
Array.ConstrainedCopy(combined, 0, iv, 0, iv.Length);
Array.ConstrainedCopy(combined, iv.Length, ciphertext, 0, ciphertext.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var resultStream = new MemoryStream())
{
using (var aesStream = new CryptoStream(resultStream, decryptor, CryptoStreamMode.Write))
using (var plainStream = new MemoryStream(ciphertext))
{
plainStream.CopyTo(aesStream);
}
return Encoding.UTF8.GetString(resultStream.ToArray());
}
}
}
Ответ 5
Система защиты данных включена по умолчанию для приложений ASP.NET Core. Вам даже не нужно ничего делать в вашем методе StartUp, если вы не хотите перенастроить хранилище ключей по умолчанию или время жизни ключей. В этом случае вы должны сделать следующее в методе ConfigureServices:
services.ConfigureDataProtection(dp =>
{
dp.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"));
dp.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
});
Поскольку система защиты данных по умолчанию используется в коллекции сервисов приложений, она может быть доступна через инъекцию зависимостей. Здесь, как вы можете ввести IDataProtectionProvider в контроллер, а затем использовать его для создания экземпляра IDataProtector в конструкторе контроллера:
public class HomeController : Controller
{
IDataProtector _protector;
public HomeController(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector(GetType().FullName);
}
}
Затем вы можете вызвать защитника для шифрования содержимого следующим образом:
public IActionResult Index()
{
var model = _service.GetAll().Select(c => new ContractViewModel {
Id = _protector.Protect(c.Id.ToString()),
Name = c.Name }).ToList();
return View(model);
}
Надеюсь, это поможет:)