Skip to content

Simplify DI-backed rotating server certificate selection in Kestrel #69666

Description

@rosebyte

Kestrel supports dynamic server certificate selection through HttpsConnectionAdapterOptions.ServerCertificateSelector. However, the most discoverable configuration paths accept an X509Certificate2 or assign HttpsConnectionAdapterOptions.ServerCertificate, which captures one certificate while the application is starting. This is easy to misuse with certificate platforms that rotate material behind a DI-managed provider. Many service teams assume that the certificate is configured once for the entire application lifetime, or that updating the provider automatically updates the certificate already assigned to Kestrel. In practice, Kestrel continues presenting the captured certificate until the endpoint or process is recreated.

A first-class DI-backed configuration path would help teams manage certificates effectively by making the intended lifetime explicit:

  • Resolve a long-lived certificate provider from application services.
  • Ask that provider for its current certificate when each new TLS connection is authenticated.
  • Keep refresh, validation, overlap and disposal inside the provider rather than application startup code.

The existing workaround is:

var certificateProvider = listenOptions.ApplicationServices.GetRequiredService<IServerCertificateProvider>();

listenOptions.UseHttps(httpsOptions =>
{
    httpsOptions.ServerCertificateSelector = (_, serverName) => certificateProvider.GetCurrentCertificate(serverName);
});

This works, but it requires teams to understand that ServerCertificate is a snapshot, discover ServerCertificateSelector, find the application service provider exposed by Kestrel, and compose the lifetimes correctly. Incorrect wiring still looks valid and generally fails only after the first live rotation.

Proposed direction

Add a focused ListenOptions extension that resolves a selector through Kestrel's application services and installs the existing ServerCertificateSelector. An illustrative API shape is:

namespace Microsoft.AspNetCore.Hosting;

public static partial class ListenOptionsHttpsExtensions
{
    public static ListenOptions UseHttps(
        this ListenOptions listenOptions,
        Func<IServiceProvider, Func<ConnectionContext?, string?, X509Certificate2?>> serverCertificateSelectorFactory);
}

The factory would run once while configuring the endpoint. The returned selector would run for each new TLS connection:

listenOptions.UseHttps(services =>
{
    var certificateProvider = services.GetRequiredService<IServerCertificateProvider>();
    return (_, serverName) => certificateProvider.GetCurrentCertificate(serverName);
});

Behaviour and ownership

  • Selection remains synchronous and may run concurrently. Providers should return validated in-memory material and perform external refresh asynchronously.
  • The provider owns returned certificates. Kestrel and application wiring must not dispose borrowed instances.
  • The provider is responsible for retaining a last-known-good certificate and defining overlap and retirement of old material.
  • Selection affects new TLS handshakes only. Existing connections retain their established TLS state.
  • The selector receives the existing ConnectionContext and SNI server name without losing information.
  • Existing HTTPS settings and defaults remain unchanged except that the installed selector takes precedence over a fixed ServerCertificate.
  • Interaction with an already configured selector should be explicit, preferably by rejecting conflicting configuration rather than silently replacing it.

Alternative designs

Add an Action<HttpsConnectionAdapterOptions, IServiceProvider> overload

A general DI-aware HTTPS configuration overload would enable the scenario, but it would not draw attention to certificate lifetime or prevent callers from resolving and assigning one certificate during configuration.

Use Kestrel configuration reload

dotnet/aspnetcore#32351, implemented by dotnet/aspnetcore#50251, supports automatic rotation for certificates loaded from Kestrel's file-based configuration. This proposal concerns certificates supplied by live DI-managed providers and is therefore not a duplicate.

Related work

  • dotnet/aspnetcore#60380 concerns retiring existing connections that authenticated with an old certificate. This proposal affects certificate selection for new connections only.
  • dotnet/aspnetcore#6968 concerned asynchronous certificate selection. This proposal deliberately retains the existing synchronous selector and expects providers to refresh material outside the handshake callback.
  • dotnet/runtime#135206 proposes the corresponding HttpClientFactory helper for DI-backed rotating outbound mTLS client certificates. This proposal covers inbound server certificates selected by Kestrel.

Activity

  1. added
    area-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
    on Oct 5, 2026
  2. github-actions commented on Oct 5, 2026

    @github-actions
    Contributor

    Triage Summary

    Area: area-networking (Kestrel ListenOptions / UseHttps / ServerCertificateSelector)
    Type: Feature (requests a new UseHttps overload that is not currently implemented)

    Potential Duplicates

    • None found

    Generated by Issue Triage Agent for dotnet/aspnetcore for #69666 · copilot · auto · 27 AIC · ⌖ 16.2 AIC · ⊞ 31.1K · ◷

  3. rzikm commented on Oct 5, 2026

    @rzikm
    Member

    Same as in dotnet/runtime#135206, let's consider an API that will allow SslStreamCertificateContext reuse. I think Kestrel can cache the context internally, but only in cases when the cert is not selected by the certificate callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions