Je voudrais chiffrer une chaîne dans .NET Core en utilisant une clé. J'ai un scénario client/serveur et je voudrais chiffrer une chaîne sur le client, l'envoyer au serveur et la déchiffrer.
Étant donné que .NET Core en est encore à ses débuts (Rijndael n’est pas encore disponible, par exemple), quelles sont mes options?
Vous ne devriez vraiment jamais utiliser Rijndael/RijndaelManaged dans .NET. Si vous l'utilisez avec une valeur BlockSize de 128 (valeur par défaut), vous utilisez alors AES, comme je expliqué dans une question similaire .
Les options de chiffrement symétrique disponibles dans .NET Core sont les suivantes:
Et pour le cryptage asymétrique
Les usines sont la meilleure solution, particulièrement sur .NET Core, car elles renverront un objet qui fonctionne sur le système d'exploitation en cours d'exécution. Par exemple, RSACng est un type public mais ne fonctionne que sous Windows. et RSAOpenSsl est un type public, mais n’est pris en charge que sous Linux et macOS.
Il y a déjà une réponse à cela, mais je pense que nous pouvons fournir une solution plus simple.
Si vous voulez simplement protéger vos données, il existe une implémentation pour cela dans .NET Core qui vous évite les problèmes de chiffrement; DataProtectionProvider
.
Dans Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection(); //Add this
[..]
services.AddMvc();
}
Si vous le souhaitez, il est possible de spécifier des algorithmes (en utilisant Microsoft.AspNetCore.DataProtection
) utilisé pour le chiffrement et la validation, comme ceci:
services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
Puis chiffrez/déchiffrez en utilisant un service en tant que tel:
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 Comme mentionné dans les commentaires ci-dessous, il peut être judicieux de comprendre que l'utilisation de DataProtectionProvider comme ceci ne fonctionnera que sur le même ordinateur avec des clés stockées sur un disque local.
Voici un exemple trivial sans authentification:
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();
}
}
Pour la génération de clé:
private static byte[] GetRandomData(int bits)
{
var result = new byte[bits / 8];
RandomNumberGenerator.Create().GetBytes(result);
return result;
}
J'ai une approche différente où je veux chiffrer une chaîne avec une clé et obtenir une chaîne brouillée que je peux déchiffrer à nouveau avec la même clé. Voir les méthodes d'extension suivantes:
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());
}
}
}
Le système de protection des données est activé par défaut pour les applications ASP.NET Core. Vous n'avez même pas besoin de faire quoi que ce soit dans votre méthode StartUp à moins de vouloir reconfigurer l'emplacement de stockage de clés par défaut ou la durée de vie des clés. Dans ce cas, procédez comme suit dans votre méthode ConfigureServices:
services.ConfigureDataProtection(dp =>
{
dp.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"));
dp.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
});
Étant donné que le système de protection des données se trouve par défaut dans la collection de services de l'application, il peut être rendu disponible via une injection de dépendance. Voici comment vous pouvez injecter IDataProtectionProvider dans un contrôleur, puis l'utiliser pour créer une instance d'un IDataProtector dans le constructeur de l'automate:
public class HomeController : Controller
{
IDataProtector _protector;
public HomeController(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector(GetType().FullName);
}
}
Vous pouvez ensuite appeler le protecteur pour chiffrer le contenu de la manière suivante:
public IActionResult Index()
{
var model = _service.GetAll().Select(c => new ContractViewModel {
Id = _protector.Protect(c.Id.ToString()),
Name = c.Name }).ToList();
return View(model);
}
J'espère que ça aide :)