OpenIddict加密和签名凭据
为了保护它发布的令牌,OpenIddict 使用两种类型的凭据:
- 签名凭据用于防止篡改。它们可以是非对称的(例如 RSA 或 ECDSA 密钥)或对称的。
- 加密凭据用于确保令牌的内容不能被恶意方读取。它们可以是非对称的(例如 RSA 密钥)或对称的。
在授权服务器选项中注册凭据
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 进行密钥加密。
注册开发证书
For development purposes, a certificate can be generated and stored by OpenIddict in the certificates store of the user account running the OpenIddict server feature. Unlike ephemeral keys, development certificates are persisted - but not shared across instances - and will be reused when the application host is restarted.
services.AddOpenIddict()
.AddServer(options =>
{
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
});
警告
This feature is not available on .NET Framework 4.6.1: calling options.AddDevelopmentEncryptionCertificate()
or options.AddDevelopmentSigningCertificate()
will result in a PlatformNotSupportedException
being thrown at runtime if no valid development certificate can be found and a new one must be generated.
Caution
options.AddDevelopmentEncryptionCertificate()
or options.AddDevelopmentSigningCertificate()
cannot be used in applications deployed on IIS or Azure App Service:
trying to use them on IIS or Azure App Service will result in an exception being thrown at runtime (unless the application pool is configured to load a user profile).
To avoid that, consider creating self-signed certificates and storing them in the X.509 certificates store of the host machine(s).
Registering a key
To register a signing or encryption key, an instance of a SecurityKey
- typically a SymmetricSecurityKey
or a RsaSecurityKey
-
can be provided to the options.AddSigningKey()
/options.AddEncryptionKey()
methods:
services.AddOpenIddict()
.AddServer(options =>
{
options.AddEncryptionKey(new SymmetricSecurityKey(
Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));
});
Note
While signing keys can be either symmetric or asymmetric, OpenIddict requires registering at least one asymmetric key to sign identity tokens. If both an asymmetric and a symmetric signing key are registered, the symmetric key will always be preferred when protecting access tokens, authorization codes or refresh tokens, while the asymmetric key will be used to sign identity tokens, that are meant to be publicly validated.
Registering a certificate (recommended for production-ready scenarios)
To register a signing or encryption certificate, the options.AddSigningCertificate()
/options.AddEncryptionCertificate()
methods can be called
with an instance of X509Certificate2
. Alternatively, a unique thumbprint
identifying the certificate in the machine or user certificate store
of the operating system can also be provided.
In production, it is recommended to use two RSA certificates, distinct from the certificate(s) used for HTTPS: one for encryption, one for signing.
Certificates can be generated and self-signed locally using the .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));
The best place to store your certificates will depend on your host:
- For IIS applications, storing the certificates in the machine store is the recommended option.
- On Azure, certificates can be uploaded and exposed to Azure App Service applications using the special
WEBSITE_LOAD_CERTIFICATES
flag. For more information, visit Use a TLS/SSL certificate in your code in Azure App Service.
Importing credentials in the API/resource validation options
Using the options.UseLocalServer()
integration
When the API and the authorization server are part of the same project, both the signing and
encryption credentials can be easily imported by calling options.UseLocalServer()
:
services.AddOpenIddict()
.AddValidation(options =>
{
options.UseLocalServer();
});
Using OpenID Connect discovery (asymmetric signing keys only)
When the API and the authorization server are hosted in different applications, standard OpenID Connect discovery can be used to automatically import asymmetric signing keys:
services.AddOpenIddict()
.AddValidation(options =>
{
options.SetIssuer("https://localhost:44319/");
options.UseSystemNetHttp();
});
Registering a symmetric signing key in the token validation parameters
与非对称签名密钥不同,对称密钥 - 与基于 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");
});