I would like to encrypt a string in .NET Core using a key. I have a client / server scenario and would like to encrypt a string on the client, send it to the server and decrypt it.
As .NET Core is still in a early stage (e.g. Rijndael is not yet available), what are my options?
Encryption and decryption of query stringsThe protect method inputs a byte or string and encrypts it. The encrypted data can then be viewed in the web application. The 'Unprotect' method is used to decrypt the encrypted ID and display the content of the data. The below code snippet is used to decrypt data.
You really shouldn't ever use Rijndael/RijndaelManaged in .NET. If you're using it with a BlockSize value of 128 (which is the default) then you're using AES, as I explained in a similar question.
The symmetric encryption options available in .NET Core are:
And for asymmetric encryption
Especially on .NET Core the factories are the best way to go, because they will give back an object which works on the currently executing operating system. For example, RSACng is a public type but only works on Windows; and RSAOpenSsl is a public type but is only supported on Linux and macOS.
There is already an answer to this but I think that we can provide a simpler solution.
If you simply want to protect your data, there is an implementation for this in .NET Core which relieves you from the headaches of encryption; DataProtectionProvider
.
In Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection(); //Add this
[..]
services.AddMvc();
}
If you would like, it is possible to specify algorithms (using Microsoft.AspNetCore.DataProtection
) used for encryption and validation, like this:
services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
Then encrypt/decrypt using a service as such:
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 As mentioned in the comments below it might be a good idea to understand that using the DataProtectionProvider like this will only work on the same machine with keys stored on local disk.
The data protection system is enabled by default for ASP.NET Core applications. You don't even need to do anything in your StartUp method unless you want to reconfigure the default key storage location or the life time of keys. In which case you'd do the following in your ConfigureServices method:
services.ConfigureDataProtection(dp =>
{
dp.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"));
dp.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
});
Because the data protection system is in the application's services collection by default, it can be made available via dependency injection. Here's how you can inject the IDataProtectionProvider into a controller and then use it to create an instance of an IDataProtector in the controller's constructor:
public class HomeController : Controller
{
IDataProtector _protector;
public HomeController(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector(GetType().FullName);
}
}
You can then call the protector to encrypt content like this:
public IActionResult Index()
{
var model = _service.GetAll().Select(c => new ContractViewModel {
Id = _protector.Protect(c.Id.ToString()),
Name = c.Name }).ToList();
return View(model);
}
I hope this helps :)
I have a different approach where I want to encrypt a string with a key and get a scrambled string which I can decrypt by the same key again. See the following extension methods:
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());
}
}
}
Here is a trivial sample without authentication:
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();
}
}
For key generation:
private static byte[] GetRandomData(int bits)
{
var result = new byte[bits / 8];
RandomNumberGenerator.Create().GetBytes(result);
return result;
}
You can do by using System.Security.Cryptography
string keyString = "encrypt123456789";
var key = Encoding.UTF8.GetBytes(keyString);//16 bit or 32 bit key string
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
For Decryption
var fullCipher = Convert.FromBase64String(cipherText);
var iv = new byte[16];
var cipher = new byte[16];
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length);
var key = Encoding.UTF8.GetBytes(keyString);//same key string
using (var aesAlg = Aes.Create())
{
using (var decryptor = aesAlg.CreateDecryptor(key, iv))
{
string result;
using (var msDecrypt = new MemoryStream(cipher))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}
For those using the above solution posted by @sundarraj and getting the System.Security.Cryptography.CryptographicException: 'Padding is invalid and cannot be removed.' error when decrypting use these lines of code to fix the decryption:
var cipher = new byte[full.Length - iv.Length];
Buffer.BlockCopy(full, 0, iv, 0, iv.Length);
Buffer.BlockCopy(full, iv.Length, cipher, 0, cipher.Length);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With