In this article we quick look how to use Encryption and Decryption In ASP.NET Core
Difference between Encoding, Encryption and Hashing
First, I want to quickly go over some basics so you can make the right choice, particularly when it comes to managing passwords securely. I have seen articles purportedly about encrypting passwords in ASP.NET that show how to Base-64 encode and decode a string. Encoding data is not a security measure – even if the resulting output is unreadable to humans. Encoding data simply provides protection against data corruption when transferring that data from one place to another, such as across networks.
Encryption is similar to encoding in that it is designed to be reversible. However, encryption uses a cryptographic algorithm to convert the data from one form to another, and only those with access to a particular cryptographic key are able to unscramble it back to its original form.
Data Protection APIs in ASP.NET Core
The data protection APIs provide mechanisms for encryption and hashing but this article is only concerned with encryption. Encryption requires a key, which is created and managed by the data protection system. Keys are created with a default lifetime of 90 days, and stored in a suitable location according to the environment. Keys are temporary, so the data protection API is designed mainly for short term data protection scenarios such as encryption of authentication cookies or query string data. It is not designed for the protection of data that, for example, might be stored long term in a database.
The data protection system is enabled as part of the default services for an ASP.NET Core application, so you do not need to do anything in your StartUp
method unless you want to reconfigure the default key storage location or the life time of keys. If that is the case, you can use the ConfigureDataProtection
extension method in the ConfigureServices
method:
public void ConfigureServices(IServiceCollection services) { services.ConfigureDataProtection(dp => { dp.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys")); dp.SetDefaultKeyLifetime(TimeSpan.FromDays(14)); }); ....
The data protection system is built upon two core concepts – a data protection provider (represented by the IDataProtectionProvider
interface), which is used to create a data protector (represented by the IDataProtector
interface). The data protector is used to encrypt and decrypt data. Because the data protection system has been added to 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); } }
The CreateProtector
method of the IDataProtectionProvider
requires a string, known as a “purpose” string. This is used to differentiate one data protector from another in the same application. Data that has been protected by one data protector cannot be unprotected by a different protector. The recommendation is that you pass in the fully qualified name of the current component as this won’t conflict with protectors instantiated in other parts of the system.
Encrypting Query Strings/Route Data
One of the most frequent encryption scenarios that I see questions about is in the realm of encrypting identity values in query strings or route data to prevent tampering, and potential access to restricted data. The following section of code illustrates getting some data from a service, and then transforming that data to a view model while encrypting the ID of each item for use in a URL:
public IActionResult Index() { var model = _service.GetAll().Select(c => new ContractViewModel { Id = _protector.Protect(c.Id.ToString()), Name = c.Name }).ToList(); return View(model); }
The Protect method takes a byte array or a string, and encrypts it. The encrypted value is then used in the view to form the Id parameter of a route:
@foreach(var entry in Model) { <div><a asp-action="Details" asp-route-id="@entry.Id">@entry.Name</a></div> }
The links point to an action named Details, where the IDataProtector’s Unprotect
method is used to decrypt the Id parameter and retrieve details of the selected item:
public IActionResult Details(string id) { var contract = _service.Find(Convert.ToInt32(_protector.Unprotect(id))); return View(contract); }
You can see the encrypted value in the URL when any of the links are clicked:
Summary
This article provides an introduction to the Data Protection system that has been built from the ground up for use with ASP.NET Core applications. It began with a refresher on the differences between encoding, encryption and hashing, and when you might want to use each process. Then it described the core concepts on which the encryption/decryption system is built and how to use the components to securely encrypt and decrypt data.