Skip to content

Adjusting the default Kestrel development certificate to support WebTransport #42787

Closed
@Daniel-Genkin-MS-2

Description

@Daniel-Genkin-MS-2

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Currently we need to use a custom certificate for the WebTransport connection. It would be great to have the default Kestrel certificate work with WebTransport to avoid this. It would make development easier and smoother.

Current setup

var builder = WebApplication.CreateBuilder(args);

// generate a certificate and hash to be shared with the client
var certificate = GenerateManualCertificate();
var hash = SHA256.HashData(certificate.RawData);
var certStr = Convert.ToBase64String(hash);

// configure the ports
builder.WebHost.ConfigureKestrel((context, options) =>
{
    // webtransport configured port
    options.Listen(IPAddress.Any, 5002, listenOptions =>
    {
        listenOptions.UseHttps(certificate);
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
    });
});

// Adapted from: https://github.com/wegylexy/webtransport
// We will need to eventually merge this with existing Kestrel certificate generation
// tracked in issue #41762
static X509Certificate2 GenerateManualCertificate()
{
    X509Certificate2 cert;
    var store = new X509Store("KestrelWebTransportCertificates", StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadWrite);
    if (store.Certificates.Count > 0)
    {
        cert = store.Certificates[^1];

        // rotate key after it expires
        if (DateTime.Parse(cert.GetExpirationDateString(), null) >= DateTimeOffset.UtcNow)
        {
            store.Close();
            return cert;
        }
    }
    // generate a new cert
    var now = DateTimeOffset.UtcNow;
    SubjectAlternativeNameBuilder sanBuilder = new();
    sanBuilder.AddDnsName("localhost");
    using var ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);
    CertificateRequest req = new("CN=localhost", ec, HashAlgorithmName.SHA256);
    // Adds purpose
    req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection
    {
        new("1.3.6.1.5.5.7.3.1") // serverAuth
    }, false));
    // Adds usage
    req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false));
    // Adds subject alternate names
    req.CertificateExtensions.Add(sanBuilder.Build());
    // Sign
    using var crt = req.CreateSelfSigned(now, now.AddDays(14)); // 14 days is the max duration of a certificate for this
    cert = new(crt.Export(X509ContentType.Pfx));

    // Save
    store.Add(cert);
    store.Close();
    return cert;
}

Describe the solution you'd like

var builder = WebApplication.CreateBuilder(args);

// configure the ports
builder.WebHost.ConfigureKestrel((context, options) =>
{
    // webtransport configured port
    options.Listen(IPAddress.Any, 5002, listenOptions =>
    {
        listenOptions.UseHttps();
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.IsWebTransportPort = true; // switches to the webtransport-compatible cert (Could be cool if this wasn't even necessary).
        Console.WriteLine(listenOptions.certHash); // prints the hash so that the user can paste it into the JS API or inject into their client side code.
    });
});

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-commandlinetoolsIncludes: Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPIfeature-devcerts

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions