项目
版本

OpenIddict 加密与签名凭据

为了保护颁发的令牌,OpenIddict 使用了两种类型的凭据:

  • 签名凭据 用于防止篡改,可以是非对称的(如 RSA 或 ECDSA 密钥)或对称的。
  • 加密凭据 用于确保令牌内容不会被恶意方读取,同样可以是非对称的(如 RSA 密钥)或对称的。

使用可选的 ASP.NET Core 数据保护集成生成的令牌依赖于其自身的密钥环,这与本文档中讨论的凭据是分开的。 有关数据保护的更多信息,请访问 ASP.NET Core 数据保护

在授权服务器选项中注册凭据

OpenIddict 允许注册一个或多个密钥(原始密钥或嵌入在 X.509 证书中的密钥)。

当注册了多个密钥/证书时(这对于实现密钥轮换非常有用),OpenIddict 将根据以下算法选择最合适的密钥:

  • 除非是对身份令牌进行签名(这要求使用非对称密钥),否则总是优先选择 对称密钥
  • 嵌入在 X.509 证书中的 非对称密钥 根据 NotAfter 和 NotBefore 日期排序(尚未生效的证书不会被 OpenIddict 使用,并且优先选择有效期最远的证书)。
  • 总是优先选择 X.509 证书 而非原始的 RSA/ECDSA 密钥。

注册临时密钥

出于开发目的,可以使用 临时密钥 来签署或加密令牌,这种密钥不会被持久化或在实例间共享:

services.AddOpenIddict()
    .AddServer(options =>
    {
        options.AddEphemeralEncryptionKey()
               .AddEphemeralSigningKey();
    });

options.AddEphemeralEncryptionKey() 生成一个非对称 RSA 密钥,该密钥不直接用来加密令牌,而是用于加密一个中间的、每个令牌专用的对称密钥,之后使用这个对称密钥首先通过 AES 加密令牌内容。关于此机制的更多信息,请阅读 使用 RSAES OAEP 的密钥加密

注册开发证书

为了开发目的,OpenIddict 可以在运行 OpenIddict 服务器功能的用户账户的证书存储中生成并存储一个证书。与临时密钥不同,开发证书会被持久化——尽管不会在实例间共享——并且当应用程序主机重启时会重新使用。

services.AddOpenIddict()
    .AddServer(options =>
    {
        options.AddDevelopmentEncryptionCertificate()
               .AddDevelopmentSigningCertificate();
    });
此功能在.NET Framework 4.6.1 上不可用:如果找不到有效的开发证书且需要生成新的证书,调用 options.AddDevelopmentEncryptionCertificate()options.AddDevelopmentSigningCertificate() 将在运行时抛出 PlatformNotSupportedException
在部署于 IIS 或 Azure App Service 的应用程序中不能使用 options.AddDevelopmentEncryptionCertificate()options.AddDevelopmentSigningCertificate():尝试在 IIS 或 Azure App Service 上使用它们会导致运行时异常(除非应用程序池配置为加载用户配置文件)。为了避免这种情况,可以考虑创建自签名证书并将其存储在主机计算机的 X.509 证书存储中。

注册密钥

要注册签名或加密密钥,可以向 options.AddSigningKey() / options.AddEncryptionKey() 方法提供一个 SecurityKey 实例,通常是 SymmetricSecurityKeyRsaSecurityKey

services.AddOpenIddict()
    .AddServer(options =>
    {
        options.AddEncryptionKey(new SymmetricSecurityKey(
            Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));
    });

虽然签名密钥可以是对称或非对称的,但 OpenIddict 要求至少注册一个非对称密钥来签署身份令牌。如果同时注册了非对称和对称签名密钥,对称密钥在保护访问令牌、授权代码或刷新令牌时将始终优先,而非对称密钥则用于签署身份令牌,这些令牌旨在公开验证。

注册证书(推荐用于生产环境)

要注册签名或加密证书,可以使用 options.AddSigningCertificate() / options.AddEncryptionCertificate() 方法并传入 X509Certificate2 实例。或者,也可以提供一个唯一标识操作系统机器或用户证书存储中证书的 thumbprint(指纹)。

在生产环境中,建议使用两个独立于 HTTPS 所用证书的 RSA 证书:一个用于加密,一个用于签名。证书可以使用.NET Core 的 CertificateRequest API 本地生成和自签名:

using var algorithm = RSA.Create(keySizeInBits: 2048);

var subject = new X500DistinguishedName("CN=Fabrikam Encryption Certificate");
var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true));

var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));

File.WriteAllBytes("encryption-certificate.pfx", certificate.Export(X509ContentType.Pfx, string.Empty));
using var algorithm = RSA.Create(keySizeInBits: 2048);

var subject = new X500DistinguishedName("CN=Fabrikam Signing Certificate");
var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true));

var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));

File.WriteAllBytes("signing-certificate.pfx", certificate.Export(X509ContentType.Pfx, string.Empty));

存储证书的最佳位置取决于你的主机:

  • 对于 IIS 应用,推荐将证书存储在机器存储中。
  • 在 Azure 上,证书可以通过特殊的 WEBSITE_LOAD_CERTIFICATES 标志上传并暴露给 Azure App Service 应用。更多详情,请访问 [在 Azure App Service 中代码中使用 TLS/SSL 证书](链接未给出,请自行搜索相关 Azure 文档页面) 。

在 API/资源验证选项中导入凭证

使用 options.UseLocalServer() 集成

当 API 和授权服务器属于同一项目时,可以通过调用 options.UseLocalServer() 轻松地导入签名和加密凭证:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.UseLocalServer();
    });

使用 OpenID Connect 发现(仅限非对称签名密钥)

当 API 和授权服务器托管在不同的应用程序中时,可以使用 标准 OpenID Connect 发现 自动导入非对称签名密钥:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.SetIssuer("https://localhost:44319/");
        options.UseSystemNetHttp();
    });

在令牌验证参数中注册对称签名密钥

与非对称签名密钥不同,对称密钥(与基于 HMAC 的算法如 HS256 一起使用)不能安全地由 OpenID Connect 发现端点暴露。因此,它们不能被 OpenIddict 验证处理器自动导入。对于需要使用对称签名密钥的应用程序,可以使用高级配置 API 在其令牌验证选项中注册它:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.Configure(options => options.TokenValidationParameters.IssuerSigningKey =
            new SymmetricSecurityKey(
                Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));
    });

注册加密密钥或证书

为了导入加密密钥/证书,可以使用与 OpenIddict 服务器功能提供的相同重载方法:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.AddEncryptionCertificate("b82f36609cdaff9a95de60e8d5ac774b2e496c4b");
    });
在本文档中