Sometimes we just need to run kestrel without the need for a green browser padlock and therefor not needing to have a “real” certificate. but still want secure HTTPS communications on the APIs.

Here we will create a simple method you can drop in to generate a valid self-signed certificate on the fly to use in Kestrel on any port of your choice!

In our Program.cs we have the following method where we want to now manually configure Listen Options.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>()
                // Add UseKestrel to control it's options
                .UseKestrel(options => {
                    // http:localhost:5000
                    options.Listen(IPAddress.Loopback, 5000);
                    // https:localhost:5001
                    options.Listen(IPAddress.Loopback, 5001);
                    // http:*:80
                    options.Listen(IPAddress.Any, 80);
                    // https:*:443
                    options.Listen(IPAddress.Any, 443, listenOptions => {
                        listenOptions.UseHttps(GetSelfSignedCertificate());
                    });
                });
            });

As you can see on line 15 we have a method called GetSelfSignedCertificate() Now we need to add this method to Program.cs or anywhere else you see fit:

private static X509Certificate2 GetSelfSignedCertificate()
{
  var password = Guid.NewGuid().ToString();
  var commonName = "MyCommonName";
  var rsaKeySize = 2048;
  var years = 5;
  var hashAlgorithm = HashAlgorithmName.SHA256;

  using (var rsa = RSA.Create(rsaKeySize))
  {
    var request = new CertificateRequest($"cn={commonName}", rsa, hashAlgorithm, RSASignaturePadding.Pkcs1);

    request.CertificateExtensions.Add(
      new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)
    );
    request.CertificateExtensions.Add(
      new X509EnhancedKeyUsageExtension(
        new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false)
    );

    var certificate = request.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(years));
    certificate.FriendlyName = commonName;

	// Return the PFX exported version that contains the key
    return new X509Certificate2(certificate.Export(X509ContentType.Pfx, password), password, X509KeyStorageFlags.MachineKeySet);
  }

When your application starts up it will enable kestrel to serve on the defined ports and generate a new certificate on the fly and serve your API endpoints securly using HTTPs.

Enjoy 😊

5 Comments

  • Calin says:

    Thank you very much! This is exactly what I was looking for!
    P.S. There is a small mistake in “opt” and “options” variable but is an easy fix.

  • Magnus Sydoff says:

    Thanks Niels for your example.

    I must admit I’ve tried numerous ways to get a local RPI4 (as well as my Win machine) to just present a web page without that annoying initial message from Chrome “Your connection is not private”.

    However, trying your example I still get that. A green padlock would obviously be nice, but just getting rid of that cert is invalid message would be great.

    I’ve tried on Chromium v99 (Chrome and Edge) on my Windows 10 machine using VS2022.
    What can I be doing wrong?

    King regards
    Magnus
    Lund, Sweden

    NET::ERR_CERT_AUTHORITY_INVALID
    Subject: MyCommonName

    Issuer: MyCommonName

    Expires on: Mar 22, 2027

    Current date: Mar 22, 2022

    PEM encoded chain:
    —–BEGIN CERTIFICATE—–
    MIIC1DCCAbygAwIBAgIIfO/GRjHJd4kwDQYJKoZIhvcNAQELBQAwFzEVMBMGA1UE
    AxMMTXlDb21tb25OYW1lMB4XDTIyMDMyMTA5MjU0NloXDTI3MDMyMjA5MjU0Nlow
    FzEVMBMGA1UEAxMMTXlDb21tb25OYW1lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
    MIIBCgKCAQEAtnu5gl3uxNFxk43h51FzdVhQxO0Ds4EqZpb9R1R7HV4PHLIHMvcf
    gHjLr9iGmWPllQa5Ci0kqhY01W5lUPYjAtgdn43FsbVj46o98tUQWnH64VKrX5LR
    YlijL1E+dq4TDXvUYI3muMsPaO7kt0lqEt9ZcO7IjPCpjWdc/xAK9k5dP1Jo3SmI
    JlP99YzL6SRHFlakO7Im6pyodCj2hJe6Bp2fzH908Jhp7Rmueqib2WBWiP+ziDzx
    4gN4ChyICBZjQdouf8Vp+f4jEIuGYDFM5bEt5afFACQtxAC95v/CNCU3157B4bnR
    OyoQVLnZuEo63kNwNr1J1ldO6vMtzOQSbQIDAQABoyQwIjALBgNVHQ8EBAMCBLAw
    EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGP+az8mftRV
    kiJkmyTPJjjH6utqmobtojB+gvuijxLJyrvnLSCbQSDtQbVznXuqI1qTVJBvqPto
    UBCUlfe6Cv4nvaPOwNB6cWqidgw/6MbEP8liiTVjRVeUz3iO5Rasjf9WGdLc9sTN
    FYbGCQn4A+OsqQ/6Vz5QS3xrNynqU1rAxygeT9KlVJy7cfAHIBpz2waRhfDiML3E
    oFv4j13ro6JlcS+rCXmI5XXfgMuNxvcanTz9xMi7hpOF3p0rJNSj3F7J5ljsZGbV
    sdn+sxkjG3TmeTBDLJLyU37Nuqx9eJ1KfY3vrjqNIfbCACoYh7NK/o3PC8pH/L48
    Muw3Ybstxl0=
    —–END CERTIFICATE—–

    • Niels says:

      Hi Magnus,
      Thank you for your comment 🙂

      There is no way unfortunately for you to get a green browser with these certificates, as they are not signed by a CA and are generated on every startup so you cannot even save it to your PC.
      This is meant mostly for scenarios where you either do not have a purchased valid certificate or you are unable to use a service like Let’s Encrypt to generate and renew certificates.
      I personally only use this for API calls where there is no browser warnings but I would like to use SSL. This still means your code has to except “invalid” certificates for it to work.

      Hope this sheds some light on your question.

      Kind regards
      Niels

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.