Claude
Skills
Sign in
Back

identityserver-key-management

Included with Lifetime
$97 forever

Managing cryptographic signing keys in Duende IdentityServer, including automatic key management, KeyManagementOptions, data protection at rest, static key configuration, migration from static to automatic, and multi-instance deployment considerations.

General

What this skill does


# Key Management and Signing

## When to Use This Skill

- Configuring automatic key management for signing token keys
- Setting up static/manual signing keys from certificates or key vaults
- Configuring key rotation intervals and key lifecycle
- Migrating from static keys to automatic key management
- Deploying IdentityServer in load-balanced or multi-instance environments
- Protecting keys at rest using data protection
- Configuring per-algorithm or per-resource signing
- Troubleshooting key-related errors (CryptographicException, unprotecting key failures)

Docs: https://docs.duendesoftware.com/identityserver/fundamentals/keys

## Core Concepts

IdentityServer issues cryptographically signed tokens: identity tokens, JWT access tokens, and logout tokens. These signatures require key material that can be managed automatically or manually (statically).

### Supported Signing Algorithms

IdentityServer supports the `RS`, `PS`, and `ES` families:

| Family | Algorithms                | Key Type |
| ------ | ------------------------- | -------- |
| RS     | `RS256`, `RS384`, `RS512` | RSA      |
| PS     | `PS256`, `PS384`, `PS512` | RSA      |
| ES     | `ES256`, `ES384`, `ES512` | ECDSA    |

## Automatic Key Management (Recommended)

Automatic Key Management handles key creation, rotation, announcement, and retirement. It is enabled by default and is part of the Business and Enterprise editions.

### Key Lifecycle

Keys move through four phases:

```
Announced --> Signing --> Retired --> Deleted
   |             |            |          |
   |<--Propagation-->|        |          |
   |             |<--Rotation-->|        |
   |             |            |<--Retention-->|
```

| Phase         | Duration (default)                           | Purpose                                       |
| ------------- | -------------------------------------------- | --------------------------------------------- |
| **Announced** | 14 days (`PropagationTime`)                  | Published in discovery, not yet signing       |
| **Signing**   | 76 days (RotationInterval - PropagationTime) | Active signing credential                     |
| **Retired**   | 14 days (`RetentionDuration`)                | In discovery for token validation only        |
| **Deleted**   | After retention                              | Removed from discovery and optionally deleted |

**Default schedule:** Keys rotate every 90 days, announced 14 days early, retained 14 days after rotation.

### Configuration

```csharp
// Program.cs
var idsvrBuilder = builder.Services.AddIdentityServer(options =>
{
    // Key rotates every 30 days
    options.KeyManagement.RotationInterval = TimeSpan.FromDays(30);

    // Announce new key 2 days in advance in discovery
    options.KeyManagement.PropagationTime = TimeSpan.FromDays(2);

    // Keep old key for 7 days in discovery for validation
    options.KeyManagement.RetentionDuration = TimeSpan.FromDays(7);

    // Don't delete keys after their retention period is over
    options.KeyManagement.DeleteRetiredKeys = false;
});
```

### KeyManagement Options Reference

| Property                             | Default   | Description                                               |
| ------------------------------------ | --------- | --------------------------------------------------------- |
| `Enabled`                            | `true`    | Enable automatic key management                           |
| `SigningAlgorithms`                  | `[RS256]` | Algorithms for which keys are managed                     |
| `RsaKeySize`                         | `2048`    | RSA key size in bits                                      |
| `RotationInterval`                   | 90 days   | Age at which keys stop signing                            |
| `PropagationTime`                    | 14 days   | Time for new keys to propagate to all servers and clients |
| `RetentionDuration`                  | 14 days   | Duration retired keys remain in discovery                 |
| `DeleteRetiredKeys`                  | `true`    | Delete keys after retention period                        |
| `KeyPath`                            | `{ContentRootPath}/keys` | File system path for default key store               |
| `DataProtectKeys`                    | `true`    | Encrypt keys at rest using data protection                |
| `KeyCacheDuration`                   | 24 hours  | Cache duration for keys from store                        |
| `InitializationDuration`             | 5 minutes | Synchronization window on first key creation              |
| `InitializationSynchronizationDelay` | 5 seconds | Delay between retries during initialization               |

### Multiple Signing Algorithms

Configure multiple algorithms to serve clients with different requirements. The first algorithm in the list is the default for signing tokens.

```csharp
options.KeyManagement.SigningAlgorithms = new[]
{
    // RS256 for older clients (with X.509 wrapping)
    new SigningAlgorithmOptions(SecurityAlgorithms.RsaSha256) { UseX509Certificate = true },

    // PS256
    new SigningAlgorithmOptions(SecurityAlgorithms.RsaSsaPssSha256),

    // ES256
    new SigningAlgorithmOptions(SecurityAlgorithms.EcdsaSha256)
};
```

Override the default on a per-client or per-resource basis:

```csharp
// Client level
var client = new Client
{
    AllowedIdentityTokenSigningAlgorithms = { SecurityAlgorithms.RsaSsaPssSha256 }
};

// API Resource level
var api = new ApiResource("invoice")
{
    AllowedAccessTokenSigningAlgorithms = { SecurityAlgorithms.RsaSsaPssSha256 }
};
```

## Key Storage

### Default: File System

The default `FileSystemKeyStore` writes keys to the `KeyPath` directory (defaults to `{ContentRootPath}/keys`). This directory must be:

- Excluded from source control
- Accessible (read/write) to all load-balanced instances if using file-based storage

```csharp
// Program.cs
var idsvrBuilder = builder.Services.AddIdentityServer(options =>
{
    options.KeyManagement.KeyPath = "/home/shared/keys";
});
```

### EntityFramework Store

Use the EF operational store for database-backed key storage:

```csharp
// Program.cs
builder.Services.AddIdentityServer()
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = b =>
            b.UseSqlServer(connectionString);
    });
```

### Custom Store

Implement `ISigningKeyStore` for custom storage (e.g., Azure Key Vault, AWS KMS):

```csharp
// Program.cs
builder.Services.AddIdentityServer()
    .AddSigningKeyStore<YourCustomStore>();
```

The store interface methods:

- `LoadKeysAsync` - load all keys (cached for `KeyCacheDuration`)
- `StoreKeyAsync` - persist a new key
- `DeleteKeyAsync` - remove a retired key

## Encryption of Keys at Rest

By default, keys are protected at rest using ASP.NET Core Data Protection (`DataProtectKeys = true`). Keep this enabled unless your custom `ISigningKeyStore` already ensures encryption (e.g., Azure Key Vault).

```csharp
// ❌ WRONG: Disabling without alternative encryption
options.KeyManagement.DataProtectKeys = false;

// ✅ CORRECT: Only disable when using a vault that encrypts at rest
options.KeyManagement.DataProtectKeys = false; // OK if using Azure Key Vault via custom ISigningKeyStore
```

### Data Protection Configuration for Production

Data protection must be properly configured for key encryption to work across instances. See [ASP.NET Core Data Protection](https://docs.duendesoftware.com/general/data-protection/) for foundational concepts and troubleshooting.

```csharp
// Program.cs
builder.Services.AddDataProtection()
    .PersistKeysToDbContext<MyDbContext>()        // or PersistKeysToAzureBlobStorage, etc.
    .ProtectKeysWithCertificate(certificate)      // or ProtectKeysWithAzureKeyVault
    .SetApplicationName("My.IdentityServer");
```

### Common Data Protection Problems

| Symptom                                                              | Cause                                       

Related in General