ASP.NET Core 数据保护提供加密 API,用于使用包括密钥管理、密钥轮换等在内的目的密钥对字符串进行加密和解密。可以通过使用 Windows DPAPI 加密或以纯文本形式安全地存储密钥,但它不适用于 Web 应用程序,因为它依赖于使用 DPAPI 来加密或解密它。我们将在这篇博文中更详细地讨论,敬请期待!
数据保护提供了我们简介中提到的加密 API,用于使用安全目的密钥加密和解密用户数据。那里有两种类型的密钥:
A. 主密钥
B. 目的
主密钥存在于密钥环中,用于保护和取消保护有效负载。密钥由数据保护提供商生成,并存储在配置的位置,即项目目录、共享位置、Azure 等内部。您可以选择使用 Windows DPAPI 加密主密钥,但它只能在 Windows 计算机上运行,而在 Linux 等非 Windows 环境中不起作用。
目的是对数据进行加密,并使用相同的数据来解密数据。如果没有目的,解密的数据就无法反转为原始字符串。
在启动类中注册 DataProtection 服务,该启动类在控制器类或任何类的构造函数中注入 DataProtectionProvider 接口,并使用它来创建保护程序以保护或取消保护字符串。
使用 DataProtectionProvider 具体类创建数据保护,并在控制器中使用它来创建保护程序以保护或取消保护字符串。
使用目录信息注册数据保护服务,以将密钥存储在给定位置的密钥环中。此外,还有一个构建器服务选项,用于设置应用程序名称以区分应用程序之间的密钥,以便每个应用程序都有单独的密钥;否则,其他应用将共享相同的密钥。
public void ConfigureServices(IServiceCollection services)
{
// Data protection service for crptography
services.AddDataProtection()
.SetApplicationName("MyAppName")
.PersistKeysToFileSystem(new DirectoryInfo(@"wwwroot/DataProtectionKeys"));
}
public class DataProtection {
private IDataProtectionProvider _dataProtectionProvider;
private IDataProtector _protector;
// Data Protection class constructor
public DataProtection(IConfiguration configuration, IDataProtectionProvider dataProtectionProvider)
{
// Create the data protector from data protection provider with the purpose key
_protector = dataProtectionProvider.CreateProtector("MyPurpose");
}
// Encrypt the user data
public string Encrypt(string data)
{
return _protector.Protect(data);
}
// Decrypt the protected data
public string? Decrypt(string data)
{
try
{
return _protector.Unprotect(data);
}
catch (CryptographicException ex)
{
return null;
}
}
}
无需在启动类中注册数据保护服务,因为某些项目具有不同的要求,并且不想使用依赖项注入。还有另一种方法可以在启动类中注册数据保护,并在自定义类中创建数据保护器,这使您在项目初始加载时创建一次数据保护提供程序。如果您想了解方法 3,请在下方发表评论以分享方法 3。
public class DataProtection {
private IDataProtectionProvider _dataProtectionProvider;
private IDataProtector _protector;
// Data Protection class constructor
public DataProtection(IConfiguration configuration)
{
// Creates the data protection provider and stores the generated key & other optional configurations
_dataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@configuration.GetSection("DataProtection:KeyPath").Value), (builder) => { builder.SetApplicationName("MyApplicationName"); });
_protector = _dataProtectionProvider.CreateProtector("MyPurpose");
}
public string Encrypt(string data)
{
return _protector.Protect(data);
}
public string? Decrypt(string data)
{
try
{
return _protector.Unprotect(data);
}
catch (CryptographicException ex)
{
return null;
}
}
}
使用 DPAPI 保护密钥环中的密钥。
public void ConfigureServices(IServiceCollection services)
{
// Data protection service for crptography
services.AddDataProtection()
.SetApplicationName("MyAppName")
.PersistKeysToFileSystem(new DirectoryInfo(@"wwwroot/DataProtectionKeys"))
.ProtectKeysWithDpapi();
}
public class UserController : ControllerBase
{
IConfiguration _configuration;
private IDataProtectionProvider _dataProtectionProvider;
public PermissionController(IConfiguration configuration, IDataProtectionProvider dataProtectionProvider) {
_configuration = configuration;
_dataProtectionProvider = dataProtectionProvider;
}
/// <summary>
/// Encrypt the string using the data protection class
/// </summary>
/// <param name="data"></param>
/// <param name="purpose"></param>
/// <returns></returns>
[HttpGet]
public ActionResult<string> Encrypt(string data, string purpose)
{
if(purpose != Constants.DataProtection.Purpose)
{
return BadRequest();
}
DataProtection dataProtection = new DataProtection(_configuration, _dataProtectionProvider);
return dataProtection.Encrypt(data);
}
/// <summary>
/// Decrypt the encrypted string using the data protection class
/// </summary>
/// <param name="data"></param>
/// <param name="purpose"></param>
/// <returns></returns>
[HttpGet]
public ActionResult<string> Decrypt(string data, string purpose)
{
if (purpose != Constants.DataProtection.Purpose)
{
return BadRequest();
}
DataProtection dataProtection = new DataProtection(_configuration, _dataProtectionProvider);
var decryptedString = dataProtection.Decrypt(data);
if(decryptedString == null)
{
return BadRequest();
}
return decryptedString;
}
}
_注意:_DataProtectionProvider 具体类型的实例的创建成本很高。如果应用维护多个此类实例,并且它们都使用相同的密钥存储目录,则应用性能可能会降低。