跨 Asp.NET Core 和 Framework 的数据保护提供程序(生成密码重置链接)

Posted

技术标签:

【中文标题】跨 Asp.NET Core 和 Framework 的数据保护提供程序(生成密码重置链接)【英文标题】:Data Protection provider across Asp.NET Core and Framework (generate password reset link) 【发布时间】:2018-07-29 16:28:52 【问题描述】:

我遇到了与 DataProtectionProvider 相关的问题,最初我们只有 2 个 .NET Framework 项目,现在添加了一个 .NET Core 项目,这让我很困惑如何执行以下操作:从.NET Framework 项目,并在 .NET Core 项目中使用它。两者都使用相同的数据库和用户表,它们已经相互兼容。 .NET Framework 仍然是 Code-First 数据库生成的领先项目。

在这两个 .NET Frameworks 项目中,我使用了一个共享代码库,其代码如下:

//not sure where I got this from but it is part of the solution for solving
//password link generating and using in two different applications.
public class MachineKeyProtectionProvider : IDataProtectionProvider

    public IDataProtector Create(params string[] purposes)
    
        return new MachineKeyDataProtector(purposes);
    


public class MachineKeyDataProtector : IDataProtector

    private readonly string[] _purposes;

    public MachineKeyDataProtector(string[] purposes)
    
        _purposes = purposes;
    

    public byte[] Protect(byte[] userData)
    
        return MachineKey.Protect(userData, _purposes);
    

    public byte[] Unprotect(byte[] protectedData)
    
        return MachineKey.Unprotect(protectedData, _purposes);
    

然后在用户存储库中:

    private readonly UserManager<ApplicationUser> _userManager = null;
    private readonly RoleManager<IdentityRole> _roleManager = null;

    internal static IDataProtectionProvider DataProtectionProvider  get; private set; 
    public UserRepository(DatabaseContext dbContext)
    
        _userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(dbContext));
        _roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(dbContext));
        _userManager.UserValidator = new UserValidator<ApplicationUser>(_userManager)  AllowOnlyAlphanumericUserNames = false ;
        if (DataProtectionProvider == null)
        
            DataProtectionProvider = new MachineKeyProtectionProvider();
        
        _userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, string>(DataProtectionProvider.Create("Identity"));
    

在两个 .NET Framework 项目中,我都有一个 &lt;machineKey&gt; 集。之后我可以简单地使用:

    public string GeneratePasswordResetCode(string userId)
    
        return _userManager.GeneratePasswordResetToken(userId);
    

    public void ChangeUserPassword(string oldPassword, string newPassword)
    
        string id = InfrastructureUserHelper.User.GetUserId();
        IdentityResult result = _userManager.ChangePassword(id, oldPassword, newPassword);
        ...
    

所以,现在添加了一个 .NET Core 项目,它已经拥有自己的密码重置机制,但所有自动化作业都是从 .NET Framework 项目之一发送的。原因是用户应该为自动创建的帐户设置密码。

我该怎么做? 我一直在看这个: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?tabs=aspnetcore2x 还有这个:How to implement machineKey in ASP.NET Core 2.0

但我无法真正弄清楚简单易行的解决方案是什么。我宁愿避免创建额外的 redis 服务器。类似于机器密钥的东西可以完成这项工作。 我尝试从 this documentation 开始,但我无法真正弄清楚 .NET Core 项目中的哪些部分以及 .NET Framework 项目中的哪些部分。

到目前为止,我已经尝试过玩这部分但没有运气:

    public void ConfigureServices(IServiceCollection services)
    
        services.AddDataProtection().SetApplicationName("Identity")
            .SetDefaultKeyLifetime(TimeSpan.FromDays(60))
            .ProtectKeysWithDpapi();

        services.AddIdentity<ApplicationUser, ApplicationRole>().AddDefaultTokenProviders();
    

编辑: 看到这个页面,我最终可以使用我们的 Blob 存储: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?tabs=aspnetcore2x

添加这似乎有点帮助,两个应用程序都运行良好,但仍然在 .NET Framework 项目中没有使用正确的 DataProtectionProvider。 UserManager 找不到它(没有 ITokenProvider 错误)。

最终我有点放弃并将令牌存储在用户数据库中,这完全不理想,但我花了很多时间试图解决一些没有记录的问题。 -1 代表微软。

【问题讨论】:

像“no ITokenProvider”这样的错误听起来更像是一个没有为依赖注入正确注册的服务。 【参考方案1】:

数据保护 API 在 .NET Framework 和 .NET Core 中的工作方式相同。

使用 X509 证书而不是旧的机器密钥方法对密钥进行加密,而不是机器密钥。自行生成的证书很好,因为加密仅供内部使用。

重要提示:如果您控制服务器,则证书必须安装在证书存储中。使用接受 X509 证书实例的 ProtectWithCertificate 重载有一个荒谬且未记录的问题:它只能使用该实例来加密。如果证书不在商店中,则解密失败。微软声称这是“底层框架”的一些限制,不管这意味着什么,但可以使用变通方法(而且并不复杂)。我在 this 上使用了一个变体,并且证书被序列化到 Azure Key Vault,这样我们就不必接触每个单独的服务器。

您还需要指定 DPAPI 持久性选项之一,以确保所有服务器都可以访问数据。由于我们使用的是 Azure,我下面的代码使用 blob 存储,但如果您运行自己的服务器,这可能是通过 PersistKeysToFileSystem 进行的网络共享。

我在ConfigureServices 中的设置如下所示:

var x509 = GetDpApiCert(); // library utility
var container = GetBlobStorageRef(); // library

services.AddDataProtection()
    .SetApplicationName(appconfig["DpapiSiteName"])
    .ProtectKeysWithProvidedCertificate(x509)
    .PersistKeysToAzureBlobStorage(container, appconfig["DpapiFileName"]);

这是我生成证书的 Powershell 脚本:

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)][string]$password = "",
    [Parameter(Mandatory=$true)][string]$rootDomain = ""
)

$cwd = Convert-Path .
$CerFile = "$cwd\aspnet_dpapi.cer"
$PfxFile = "$cwd\aspnet_dpapi.pfx"

# abort if files exist
if((Test-Path($PfxFile)) -or (Test-Path($CerFile)))

    Write-Warning "Failed, aspnet_dpapi already exists in $cwd"
    Exit


$cert = New-SelfSignedCertificate `
        -Subject $rootDomain `
        -DnsName $rootDomain `
        -FriendlyName "ASP.NET Data Protection $rootDomain" `
        -NotBefore (Get-Date) `
        -NotAfter (Get-Date).AddYears(10) `
        -CertStoreLocation "cert:CurrentUser\My" `
        -KeyAlgorithm RSA `
        -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" `
        -KeyLength 2048 `
        -KeyUsage KeyEncipherment, DataEncipherment
        # -HashAlgorithm SHA256 `
        # -Type Custom,DocumentEncryptionCert `
        # -TextExtension @("2.5.29.37=text1.3.6.1.5.5.7.3.1")

$store = 'Cert:\CurrentUser\My\' + ($cert.ThumbPrint)  
$securePass = ConvertTo-SecureString -String $password -Force -AsPlainText

Export-Certificate -Cert $store -FilePath $CerFile
Export-PfxCertificate -Cert $store -FilePath $PfxFile -Password $securePass

【讨论】:

我不控制服务器,它们是 Azure 中的应用服务。我已经尝试将它与 Blob 存储一起使用,该存储更容易且可运行,但 .NET Framework 仍然无法使用该数据保护提供程序(请参阅更新)。另外我想知道“目的”在哪里设置。 “目的”是您传递给SetApplicationName 的内容。将您的证书序列化到密钥库很容易,我在this 答案中展示了如何检索它们,并且在同一问题上接受的答案展示了如何存储它们。 Key Vault 非常便宜,但您需要缓存证书。 更新:ASP.NET Core 2.1.0 将更正不使用提供的证书进行解密的问题。同时,我一直在使用带有 2.0 的 this 的变体来实现相同的目标。

以上是关于跨 Asp.NET Core 和 Framework 的数据保护提供程序(生成密码重置链接)的主要内容,如果未能解决你的问题,请参考以下文章

2021-06-22 .NET高级班 60-ASP.NET Core 解决跨域问题(工具版)

无法在 ASP.NET Core 1.0 中启用跨域请求 (CORS)

如何在ASP NET Core中实现CORS跨域

asp.net core 系列之允许跨域访问2之测试跨域(Enable Cross-Origin Requests:CORS)

asp.net core 系列之允许跨域访问-1(Enable Cross-Origin Requests:CORS)

uniapp跨域调用ASP.NET Core Web API