{"id":36906,"date":"2017-11-29T09:00:12","date_gmt":"2017-11-29T17:00:12","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/?p=10805"},"modified":"2017-11-29T09:00:12","modified_gmt":"2017-11-29T17:00:12","slug":"configuring-https-in-asp-net-core-across-different-platforms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/configuring-https-in-asp-net-core-across-different-platforms\/","title":{"rendered":"Configuring HTTPS in ASP.NET Core across different platforms"},"content":{"rendered":"<p>As the web moves to be more <a href=\"https:\/\/blog.chromium.org\/2017\/04\/next-steps-toward-more-connection.html\">secure by default<\/a> it&#8217;s more important than ever to make sure your websites have HTTPS enabled. And if you&#8217;re going to use HTTPS in production its a good idea to develop with HTTPS enabled so that your development environment is as close to your production environment as possible. In this blog post we&#8217;re going to go through how to setup an ASP.NET Core app with HTTPS for local development on Windows, Mac, and Linux.<\/p>\n<p>This post is primarily focused on enabling HTTPS in ASP.NET Core during development using Kestrel. When using Visual Studio you can alternatively enable HTTPS in the Debug tab of your app to easily have IIS Express enable HTTPS without it going all the way to Kestrel. This closely mimics what you would have if you&#8217;re handling HTTPS connections in production using IIS. However, when running from the command-line or in a non-Windows environment you must instead enable HTTPS directly using Kestrel.<\/p>\n<p>The basic steps we will use for each OS are:<\/p>\n<ol>\n<li>Create a self-signed certificate that Kestrel can use<\/li>\n<li>Optionally trust the certificate so that your browser will not warn you about using a self-signed certificate<\/li>\n<li>Configure Kestrel to use that certificate<\/li>\n<\/ol>\n<p><a href=\"https:\/\/github.com\/aspnet\/samples\/tree\/master\/samples\/aspnetcore\/security\/KestrelHttps\">Kestrel HTTPS sample app<\/a><\/p>\n<h2>Create a certificate<\/h2>\n<h3>Windows<\/h3>\n<p>Use the <a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/module\/pkiclient\/new-selfsignedcertificate\">New-SelfSignedCertificate<\/a> Powershell cmdlet to generate a suitable certificate for development:<\/p>\n<pre><code class=\"powershell\">New-SelfSignedCertificate -NotBefore (Get-Date) -NotAfter (Get-Date).AddYears(1) -Subject \"localhost\" -KeyAlgorithm \"RSA\" -KeyLength 2048 -HashAlgorithm \"SHA256\" -CertStoreLocation \"Cert:\\CurrentUser\\My\" -KeyUsage KeyEncipherment -FriendlyName \"HTTPS development certificate\" -TextExtension @(\"2.5.29.19={critical}{text}\",\"2.5.29.37={critical}{text}1.3.6.1.5.5.7.3.1\",\"2.5.29.17={critical}{text}DNS=localhost\")\n<\/code><\/pre>\n<h3>Linux &amp; Mac<\/h3>\n<p>For Linux and Mac we will use <a href=\"https:\/\/www.openssl.org\/\">OpenSSL<\/a>.<\/p>\n<p>Create a file https.config with the following data:<\/p>\n<pre><code>[ req ]\ndefault_bits       = 2048\ndefault_md         = sha256\ndefault_keyfile    = key.pem\nprompt             = no\nencrypt_key        = no\n\ndistinguished_name = req_distinguished_name\nreq_extensions     = v3_req\nx509_extensions    = v3_req\n\n[ req_distinguished_name ]\ncommonName             = \"localhost\"\n\n[ v3_req ]\nsubjectAltName      = DNS:localhost\nbasicConstraints    = critical, CA:false\nkeyUsage            = critical, keyEncipherment\nextendedKeyUsage    = critical, 1.3.6.1.5.5.7.3.1\n<\/code><\/pre>\n<p>Run the following command to generate a private key and a certificate signing request:<\/p>\n<pre><code>openssl req -config https.config -new -out csr.pem\n<\/code><\/pre>\n<p>Run the following command to create a self-signed certificate:<\/p>\n<pre><code>openssl x509 -req -days 365 -extfile https.config -extensions v3_req -in csr.pem -signkey key.pem -out https.crt\n<\/code><\/pre>\n<p>Run the following command to generate a pfx file containing the certificate and the private key that you can use with Kestrel.<\/p>\n<pre><code>openssl pkcs12 -export -out https.pfx -inkey key.pem -in https.crt -password pass:&lt;&lt;Password&gt;&gt;\n<\/code><\/pre>\n<h2>Trust the certificate<\/h2>\n<p>This step is optional, but without it the browser will warn you about your site being potentially unsafe. You will see something like the following if you browser doesn&#8217;t trust your certificate:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2017\/11\/chrome-untrusted-cert-1-1.png\" alt=\"cert_error_chrome\" \/><\/p>\n<h3>Windows<\/h3>\n<p>To trust the generated certificate on Windows you need to add it to the current user&#8217;s trusted root store:<\/p>\n<ol>\n<li>Run <em>certmgr.msc<\/em><\/li>\n<li>\n<p>Find the certificate under <code>Personal\/Certificates<\/code>. The &#8220;Issued To&#8221; field should be <code>localhost<\/code> and the &#8220;Friendly Name&#8221; should be <code>HTTPS development certificate<\/code><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2017\/11\/certmgr-1.png\" alt=\"image\" \/><\/p>\n<\/li>\n<li>\n<p>Copy the certificate and paste it under <code>Trusted Root Certification Authorities\/Certificates<\/code><\/p>\n<\/li>\n<li>\n<p>When Windows presents a security warning dialog to confirm you want to trust the certificate, click on &#8220;Yes&#8221;.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2017\/11\/windows-trust-cert-1.png\" alt=\"image\" \/><\/p>\n<\/li>\n<\/ol>\n<h3>Linux<\/h3>\n<p>There is no centralized way of trusting the a certificate on Linux so you can do one of the following:<\/p>\n<ol>\n<li>Cxclude the URL you are using in your browsers exclude list<\/li>\n<li>Trust all self-signed certificates on localhost<\/li>\n<li>Add the https.crt to the list of trusted certificates in your browser.<\/li>\n<\/ol>\n<p>How exactly to achieve this depends on your browser\/distro, and we aren&#8217;t going to through all the options here.<\/p>\n<h3>Mac<\/h3>\n<h4>Option 1: command line<\/h4>\n<p>Run the following command<\/p>\n<pre><code>sudo security add-trusted-cert -d -r trustRoot -k \/Library\/Keychains\/System.keychain https.crt\n<\/code><\/pre>\n<p>Some browsers, such as Chrome, require you to restart them before this trust will take affect.<\/p>\n<h4>Option 2: Keychain UI<\/h4>\n<p>If you open the &#8220;Keychain Access&#8221; app you can drag your <code>https.crt<\/code> into the Login keychain,<\/p>\n<h2>Configure Kestrel to use the certificate we generated<\/h2>\n<p>The following code will read a set of HTTP server endpoint configurations from your app configuration settings and then apply them to Kestrel including setting up HTTPS using the specified certificate.<\/p>\n<h3>Application Code<\/h3>\n<p>This code will read a set of HTTP server endpoint configurations from a custom section in your app configuration settings and then apply them to Kestrel. The endpoint configurations include settings for configuring HTTPS, like which certificate to use. Add the code for the <code>ConfigureEndpoints<\/code> extension method to your application and then call it when setting up Kestrel for your host in <em>Program.cs<\/em>:<\/p>\n<pre><code class=\"csharp\">public class Program\n{\n    public static void Main(string[] args)\n    {\n        BuildWebHost(args).Run();\n    }\n\n    public static IWebHost BuildWebHost(string[] args) =&gt;\n        WebHost.CreateDefaultBuilder(args)\n            .UseStartup&lt;Startup&gt;()\n            .UseKestrel(options =&gt; options.ConfigureEndpoints())\n            .Build();\n}\n\npublic static class KestrelServerOptionsExtensions\n{\n    public static void ConfigureEndpoints(this KestrelServerOptions options)\n    {\n        var configuration = options.ApplicationServices.GetRequiredService&lt;IConfiguration&gt;();\n        var environment = options.ApplicationServices.GetRequiredService&lt;IHostingEnvironment&gt;();\n\n        var endpoints = configuration.GetSection(\"HttpServer:Endpoints\")\n            .GetChildren()\n            .ToDictionary(section =&gt; section.Key, section =&gt;\n            {\n                var endpoint = new EndpointConfiguration(); \n                section.Bind(endpoint); \n                return endpoint;\n            });\n\n        foreach (var endpoint in endpoints)\n        {\n            var config = endpoint.Value;\n            var port = config.Port ?? (config.Scheme == \"https\" ? 443 : 80);\n\n            var ipAddresses = new List&lt;IPAddress&gt;();\n            if (config.Host == \"localhost\")\n            {\n                ipAddresses.Add(IPAddress.IPv6Loopback);\n                ipAddresses.Add(IPAddress.Loopback);\n            }\n            else if (IPAddress.TryParse(config.Host, out var address))\n            {\n                ipAddresses.Add(address);\n            }\n            else\n            {\n                ipAddresses.Add(IPAddress.IPv6Any);\n            }\n\n            foreach (var address in ipAddresses)\n            {\n                options.Listen(address, port,\n                    listenOptions =&gt;\n                    {\n                        if (config.Scheme == \"https\")\n                        {\n                            var certificate = LoadCertificate(config, environment);\n                            listenOptions.UseHttps(certificate);\n                        }\n                    });\n            }\n        }\n    }\n\n    private static X509Certificate2 LoadCertificate(EndpointConfiguration config, IHostingEnvironment environment)\n    {\n        if (config.StoreName != null &amp;&amp; config.StoreLocation != null)\n        {\n            using (var store = new X509Store(config.StoreName, Enum.Parse&lt;StoreLocation&gt;(config.StoreLocation)))\n            {\n                store.Open(OpenFlags.ReadOnly);\n                var certificate = store.Certificates.Find(\n                    X509FindType.FindBySubjectName,\n                    config.Host,\n                    validOnly: !environment.IsDevelopment());\n\n                if (certificate.Count == 0)\n                {\n                    throw new InvalidOperationException($\"Certificate not found for {config.Host}.\");\n                }\n\n                return certificate[0];\n            }\n        }\n\n        if (config.FilePath != null &amp;&amp; config.Password != null)\n        {\n            return new X509Certificate2(config.FilePath, config.Password);\n        }\n\n        throw new InvalidOperationException(\"No valid certificate configuration found for the current endpoint.\");\n    }\n}\n\npublic class EndpointConfiguration\n{\n    public string Host { get; set; }\n    public int? Port { get; set; }\n    public string Scheme { get; set; }\n    public string StoreName { get; set; }\n    public string StoreLocation { get; set; }\n    public string FilePath { get; set; }\n    public string Password { get; set; }\n}\n<\/code><\/pre>\n<h3>Windows Sample Configuration<\/h3>\n<p>To configure your endpoints and HTTPS settings on Windows you could then put the following into your <em>appsettings.Development.json<\/em>, which configures an HTTPS endpoint for your application using a certificate in a certificate store:<\/p>\n<pre><code class=\"json\">{\n    \"HttpServer\":{\n        \"Endpoints\":{\n            \"Http\":{\n                \"Host\": \"localhost\",\n                \"Port\": 8080,\n                \"Scheme\": \"http\"\n            },\n            \"Https\":{\n                \"Host\": \"localhost\",\n                \"Port\": 44340,\n                \"Scheme\": \"https\",\n                \"StoreName\": \"My\",\n                \"StoreLocation\": \"CurrentUser\"\n            }\n        }\n    }\n}\n<\/code><\/pre>\n<h3>Linux and Mac OS Sample Configuration<\/h3>\n<p>On Linux or Mac your <em>appsettings.Development.json<\/em> would look something like this, where your certificate is specified using a file path:<\/p>\n<pre><code class=\"json\">{\n    \"HttpServer\":{\n        \"Endpoints\":{\n            \"Http\":{\n                \"Host\": \"localhost\",\n                \"Port\": 8080,\n                \"Scheme\": \"http\"\n            },\n            \"Https\":{\n                \"Host\": \"localhost\",\n                \"Port\": 44340,\n                \"Scheme\": \"https\",\n                \"FilePath\": \"\/path\/to\/certificate\"\n            }\n        }\n    }\n}\n<\/code><\/pre>\n<p>You can then use the <a href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/security\/app-secrets\">user secret manager tool<\/a>, environment variables, or some secure store such as Azure KeyVault to store the password of your certificate using the <code>HttpServer:Endpoints:Https:Password<\/code> configuration key instead of storing the password in a file that goes into source control.<\/p>\n<p>For example, to store the certificate password as a user secret during development, run the following command from your project:<\/p>\n<pre><code>dotnet user-secrets set HttpServer:Endpoints:Https:Password &lt;password&gt;\n<\/code><\/pre>\n<p>To override the certificate password using an environment variable, create an environment variable named <code>HttpServer:Endpoints:Https:Password<\/code> (or <code>HttpServer__Endpoints__Https__Password<\/code> if your system does not allow <code>:<\/code>) with the value of the certificate password.<\/p>\n<h2>Run your application<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2017\/11\/https-run-app-1.png\" alt=\"firefox_ubuntu_https\" \/><\/p>\n<p>When running from Visual Studio you can change the default launch URL for your application to use the HTTPS address by modifying the <code>launchSettings.json<\/code> file:<\/p>\n<pre><code class=\"json\">{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http:\/\/localhost:2613\/\",\n      \"sslPort\": 44335\n    }\n  },\n  \"profiles\": {\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"https:\/\/localhost:44335\/\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"KestrelHttpsTest\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"launchUrl\": \"https:\/\/localhost:44340\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"applicationUrl\": \"http:\/\/localhost:8080\/\"\n    }\n  }\n}\n<\/code><\/pre>\n<h2>Redirect from HTTP to HTTPS<\/h2>\n<p>When you setup your site to use HTTPS by default, you typically want to allow HTTP requests, but have them redirected to the corresponding HTTPS address. In ASP.NET Core this can be accomplished using the URL rewrite middleware.<\/p>\n<pre><code class=\"csharp\">var httpsSection = Configuration.GetSection(\"HttpServer:Endpoints:Https\");\nif (httpsSection.Exists())\n{\n    var httpsEndpoint = new EndpointConfiguration();\n    httpsSection.Bind(httpsEndpoint);\n    app.UseRewriter(new RewriteOptions().AddRedirectToHttps(\n        statusCode: env.IsDevelopment() ? StatusCodes.Status302Found : StatusCodes.Status301MovedPermanently,\n        sslPort: httpsEndpoint.Port));\n}\n<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>With a little bit of work you can setup your ASP.NET Core 2.0 site to always use HTTPS. In our next release we are working to simplify setting up HTTPS for ASP.NET Core apps and we plan to enable HTTPS in the project templates by default. We will share more details on these improvements as they become publicly available.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As the web moves to be more secure by default it&#8217;s more important than ever to make sure your websites have HTTPS enabled. And if you&#8217;re going to use HTTPS in production its a good idea to develop with HTTPS enabled so that your development environment is as close to your production environment as possible. [&hellip;]<\/p>\n","protected":false},"author":417,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197,7509],"tags":[],"class_list":["post-36906","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","category-aspnetcore"],"acf":[],"blog_post_summary":"<p>As the web moves to be more secure by default it&#8217;s more important than ever to make sure your websites have HTTPS enabled. And if you&#8217;re going to use HTTPS in production its a good idea to develop with HTTPS enabled so that your development environment is as close to your production environment as possible. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/36906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=36906"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/36906\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=36906"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=36906"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=36906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}