A .NET library that provides extensions and utilities for ASP.NET Core Data Protection, including a type-safe factory pattern for creating IDataProtector instances and high-performance span-based protection operations.
-
Type-Safe Data Protector Factory (
IDataProtectorFactory<T>)- Creates
IDataProtectorinstances with purpose strings derived from generic type parameters - Ensures cryptographic isolation between different components automatically
- Supports custom factory implementations for advanced scenarios
- Creates
-
High-Performance Span Extensions (
DataProtectorExtensions)ProtectSpan<TWriter>- Protects plaintext data usingReadOnlySpan<byte>input andIBufferWriter<byte>outputUnprotectSpan<TWriter>- Unprotects data usingReadOnlySpan<byte>input andIBufferWriter<byte>output- Reduces memory allocations compared to standard array-based methods
- Leverages native
ISpanDataProtectoron .NET 11.0+ when available - Includes security measures like memory pinning and secure memory clearing on older frameworks
-
Dependency Injection Integration
AddDataProtectorFactory()- Registers the open generic factory for anyIDataProtectorFactory<T>AddDataProtectorFactory<T, TImplementation>()- Registers a custom factory implementation for a specific type- Uses
TryAddsemantics to avoid overwriting existing registrations
dotnet add package NCode.Extensions.DataProtection// Configure Data Protection (your responsibility to set up the keyring)
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"))
.ProtectKeysWithDpapi();
// Register the data protector factory
services.AddDataProtectorFactory();public class MyService
{
private readonly IDataProtector _protector;
public MyService(IDataProtectorFactory<MyService> factory)
{
_protector = factory.CreateDataProtector();
}
public string Protect(string data) => _protector.Protect(data);
public string Unprotect(string data) => _protector.Unprotect(data);
}var protector = factory.CreateDataProtector();
var writer = new ArrayBufferWriter<byte>();
// Protect data
ReadOnlySpan<byte> plaintext = "sensitive data"u8;
protector.ProtectSpan(plaintext, ref writer);
// Unprotect data
var unprotectWriter = new ArrayBufferWriter<byte>();
protector.UnprotectSpan(writer.WrittenSpan, ref unprotectWriter);- .NET 8.0, .NET 9.0, or .NET 10.0+
- Microsoft.AspNetCore.DataProtection
⚠️ Security Notice: On frameworks prior to .NET 11.0,UnprotectSpancannot guarantee that sensitive plaintext data won't be copied by the garbage collector before memory pinning is applied.
When using UnprotectSpan on .NET 8.0, 9.0, or 10.0, the implementation:
- Calls
IDataProtector.Unprotect()which returns abyte[]containing the plaintext - Immediately attempts to pin the array using
GCHandle.Alloc(..., GCHandleType.Pinned) - Copies the data to the destination buffer
- Clears the memory using
CryptographicOperations.ZeroMemory()
The limitation: Between steps 1 and 2, there is a brief window where the GC could relocate the plaintext array, potentially leaving copies of sensitive data in memory that cannot be cleared.
While the likelihood of this occurring is extremely low in practice, applications with strict security requirements should be aware of this limitation. The ZeroMemory call remains the primary security measure for clearing sensitive data.
On .NET 11.0+, the native ISpanDataProtector interface is used when available, which eliminates this limitation entirely.
Licensed under the Apache License, Version 2.0. See LICENSE.txt for details.
- v1.0.0 - Initial release
- v1.0.1 - Fix xmldoc