Skip to content

Instantly share code, notes, and snippets.

@randyburden
Created September 20, 2021 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randyburden/7127d0f6e269d71a9dd0dd8704eb7840 to your computer and use it in GitHub Desktop.
Save randyburden/7127d0f6e269d71a9dd0dd8704eb7840 to your computer and use it in GitHub Desktop.
CustomDataProtectionProvider for ASP.NET Core that uses a hard-coded AES Key. This is more like the old machine key in classic ASP.NET. This can easily be refactored to have the key supplied from appsettings.json or the database.
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace DataProtectionHelpers
{
public static class ServiceCollectionExtensions
{
/// <summary>
/// Replaces the default <see cref="IDataProtectionProvider"/> with the <see cref="CustomDataProtectionProvider"/>.
/// <para>
/// Register this after adding Data Protection, Authentication, Session, Anti-Forgery, or other
/// features that depend on Data Protection.
/// </para>
/// </summary>
/// <param name="services">Service collection.</param>
/// <returns></returns>
public static IServiceCollection AddCustomDataProtectionProvider(this IServiceCollection services)
{
var dataProtectionProviderDescriptor = services.FirstOrDefault(s => s.ServiceType == typeof(IDataProtectionProvider));
if (dataProtectionProviderDescriptor == null)
{
throw new Exception("The IDataProtectionProvider could not be found in the IServiceCollection");
}
services.Replace(
ServiceDescriptor.Describe(
dataProtectionProviderDescriptor.ServiceType, serviceProvider =>
{
var innerDataProtectionProvider = (IDataProtectionProvider) dataProtectionProviderDescriptor.ImplementationFactory(serviceProvider);
return new CustomDataProtectionProvider(innerDataProtectionProvider);
},
dataProtectionProviderDescriptor.Lifetime));
return services;
}
}
public class CustomDataProtectionProvider : IDataProtectionProvider
{
private readonly IDataProtectionProvider _innerDataProtectionProvider;
public CustomDataProtectionProvider(IDataProtectionProvider innerDataProtectionProvider)
{
_innerDataProtectionProvider = innerDataProtectionProvider;
}
public IDataProtector CreateProtector(string purpose)
{
return new CustomDataProtector(this);
}
}
public class CustomDataProtector : IDataProtector
{
private readonly IDataProtectionProvider _dataProtectionProvider;
// Base-64 encoded 256-bit key
private readonly byte[] _key = Convert.FromBase64String("OVmfIaq5WZ3ErRMPZa5EqEKyBHA1kYXaXUzMVNlH/I8=");
// Base-64 encoded 128-bit key
private readonly byte[] _iv = Convert.FromBase64String("eC9BP0QoRytLYlBlU2hWaw==");
public CustomDataProtector(IDataProtectionProvider dataProtectionProvider)
{
_dataProtectionProvider = dataProtectionProvider;
}
public IDataProtector CreateProtector(string purpose)
{
return _dataProtectionProvider.CreateProtector(purpose);
}
public byte[] Protect(byte[] plaintext) => Encrypt(plaintext);
public byte[] Unprotect(byte[] protectedData) => Decrypt(protectedData);
private byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.IV = _iv;
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
return PerformCryptography(data, encryptor);
}
private byte[] Decrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.IV = _iv;
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
return PerformCryptography(data, decryptor);
}
private byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
{
using var ms = new MemoryStream();
using var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return ms.ToArray();
}
}
}
@randyburden
Copy link
Author

Use this if you want to use a similar pattern to the classic ASP.NET machineKey where you supply your own encryption keys. Worst case scenario you hard-code the keys into the code like above. A better scenario would be to refactor the code above to inject the keys at startup via an environment-specific appsettings.json file, from the database, or from a key vault.

This code is an amalgamation of the following articles:

You can generate your own keys from here:

You can then Base-64 encode your keys from here:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment