mirror of
https://gitee.com/dcren/openiddict-documentation.git
synced 2025-11-10 11:24:45 +08:00
Add encryption/signing credentials documentation
This commit is contained in:
179
configuration/encryption-and-signing-credentials.md
Normal file
179
configuration/encryption-and-signing-credentials.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Encryption and signing credentials
|
||||
|
||||
To protect the tokens it issues, OpenIddict uses 2 types of credentials:
|
||||
- **Signing credentials are used to protect against tampering**. They can be either asymmetric (e.g a RSA or ECDSA key) or symmetric.
|
||||
- **Encryption credentials are used to ensure the content of tokens cannot be read by malicious parties**. They can be either asymmetric (e.g a RSA key) or symmetric.
|
||||
|
||||
> [!NOTE]
|
||||
> Tokens generated using the opt-in ASP.NET Core Data Protection integration rely on their own key ring, distinct from the credentials discussed in this documentation.
|
||||
> For more information about Data Protection, visit [ASP.NET Core Data Protection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction).
|
||||
|
||||
## Registering credentials in the server options
|
||||
|
||||
OpenIddict allows registering one or multiple keys (raw keys or embedded in X.509 certificates).
|
||||
|
||||
> [!NOTE]
|
||||
> When multiple keys/certificates are registered (which can be useful to implement keys rotation), OpenIddict chooses the most appropriate key based on the following algorithm:
|
||||
> - Symmetric keys are always chosen first, except for identity tokens, that can only be signed using asymmetric keys.
|
||||
> - Asymmetric keys embedded in X.509 certificates are ordered based on the `NotAfter` and `NotBefore` dates (certificates that are not yet valid
|
||||
> are not used by OpenIddict and certificates with the furthest expiration date are always preferred).
|
||||
> - X.509 certificates are always preferred to raw RSA/ECDSA keys.
|
||||
|
||||
### Registering an ephemeral key
|
||||
|
||||
For development purposes, an ephemeral key - that is not persisted or shared across instances - can be used to sign or encrypt tokens:
|
||||
|
||||
```csharp
|
||||
services.AddOpenIddict()
|
||||
.AddServer(options =>
|
||||
{
|
||||
options.AddEphemeralEncryptionKey()
|
||||
.AddEphemeralSigningKey();
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `options.AddEphemeralEncryptionKey()` generates an asymmetric RSA key which is not directly used as-is to encrypt the tokens but is used to encrypt an
|
||||
> intermediate *per-token* symmetric key with which the token content is first encrypted using [AES](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.6).
|
||||
> For more information about this mechanism, read [Key Encryption with RSAES OAEP](https://datatracker.ietf.org/doc/html/rfc7518#section-4.3).
|
||||
|
||||
### Registering a development certificate
|
||||
|
||||
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.
|
||||
|
||||
```csharp
|
||||
services.AddOpenIddict()
|
||||
.AddServer(options =>
|
||||
{
|
||||
options.AddDevelopmentEncryptionCertificate()
|
||||
.AddDevelopmentSigningCertificate();
|
||||
});
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> 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.
|
||||
|
||||
### 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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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));
|
||||
|
||||
var data = certificate.Export(X509ContentType.Pfx, string.Empty);
|
||||
```
|
||||
|
||||
```csharp
|
||||
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));
|
||||
|
||||
var data = certificate.Export(X509ContentType.Pfx, string.Empty);
|
||||
```
|
||||
|
||||
The best place to store your certificates will mostly 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 Services applications using the special `WEBSITE_LOAD_CERTIFICATES` flag.
|
||||
For more information, visit https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate-in-code
|
||||
|
||||
## Importing credentials in the 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()`:
|
||||
|
||||
```csharp
|
||||
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](https://openid.net/specs/openid-connect-discovery-1_0.html) can be used to automatically import asymmetric signing keys:
|
||||
|
||||
```csharp
|
||||
services.AddOpenIddict()
|
||||
.AddValidation(options =>
|
||||
{
|
||||
options.SetIssuer("https://localhost:44319/");
|
||||
options.UseSystemNetHttp();
|
||||
});
|
||||
```
|
||||
|
||||
### Registering a symmetric signing key in the token validation parameters
|
||||
|
||||
Unlike asymmetric signing keys, symmetric keys - used with HMAC-based algorithms like [HS256](https://datatracker.ietf.org/doc/html/rfc7518#section-3.2) - cannot
|
||||
be safely exposed by an OpenID Connect discovery endpoint. As such, they can't be automatically imported by the OpenIddict validation handler.
|
||||
For applications that require using a symmetric signing key, the advanced configuration APIs can be used to register it in the token validation options:
|
||||
|
||||
|
||||
```csharp
|
||||
services.AddOpenIddict()
|
||||
.AddValidation(options =>
|
||||
{
|
||||
options.Configure(options => options.TokenValidationParameters.IssuerSigningKey =
|
||||
new SymmetricSecurityKey(
|
||||
Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));
|
||||
});
|
||||
```
|
||||
|
||||
### Registering an encryption key or certificate
|
||||
|
||||
To import an encryption key/certificate, the same overloads as the ones exposed by the OpenIddict server feature can be used:
|
||||
|
||||
```csharp
|
||||
services.AddOpenIddict()
|
||||
.AddValidation(options =>
|
||||
{
|
||||
options.AddEncryptionCertificate("b82f36609cdaff9a95de60e8d5ac774b2e496c4b");
|
||||
});
|
||||
```
|
||||
@@ -10,6 +10,9 @@
|
||||
- name: Claim destinations
|
||||
href: claim-destinations.md
|
||||
|
||||
- name: Encryption and signing credentials
|
||||
href: encryption-and-signing-credentials.md
|
||||
|
||||
- name: Proof Key for Code Exchange
|
||||
href: proof-key-for-code-exchange.md
|
||||
|
||||
|
||||
Reference in New Issue
Block a user