Skip to content

Using HTTPS in Local Development

As a web application developer, one of the most common challenge faced is, not having the local development environment close enough to the production environment.

Configuring HTTPS

Using mkcert for local development allows you to create locally-trusted development certificates, which can be very useful for testing HTTPS locally.

Installing mkcert

You can install mkcert using choco:

choco install mkcert
Or

This guide can be used for mkcert to create self-signed certificates.

Follow the installation guide to install mkcert.

Installing openssl

.NET libraries, such as X509Certificate2, provide built-in support for loading and using .pfx files therefore .NET Core clients often use .pfx (Personal Information Exchange) certificates instead of .pem (Privacy-Enhanced Mail) files.

We can use openssl for certificate operations.

You can install openssl using choco:

choco install openssl -y

Creating certificate with mkcert

Remember that mkcert is meant for development purposes, not production, so it should not be used on end users' machines, and that you should not export or share rootCA-key.pem.

mkcert -key-file key.pem -cert-file cert.pem pssx-authserver pssx-apihost pssx-web pssx-web-public pssx-rabbitmq pssx-redis pssx-sql-server docker.localhost "*.docker.localhost" localhost 127.0.0.1 ::1

Convert certificate to .pkcs12 format.

Openssl allows you to convert SSL-certificates in various formats.

openssl pkcs12 -export -out <p12FilePath> -inkey <keyFilePath> -in <certFilePath> -password pass:<p12Password> -certfile <destinationRootCACertFile>"

The CA certificate and its key are stored in an application data folder in the user home. The location is printed by mkcert -CAROOT

Example powershell script

Below is a sample powershell script to create a certificate and make it easy to use;

$slnFolder = $currentFolder = $PSScriptRoot

# Paths
$etcFolder = Join-Path -Path $currentFolder -ChildPath "etc"
$toolsFolder = Join-Path -Path $currentFolder -ChildPath "tools"
$certsFolder = Join-Path -Path $etcFolder -ChildPath "certs"
$mkcertFolder = Join-Path -Path $toolsFolder -ChildPath "mkcert"

Write-Host "********* Prerequisite Check *********" -ForegroundColor Magenta

# Check if Chocolatey is installed
$chocoCommand = Get-Command choco -ErrorAction SilentlyContinue

if ($null -eq $chocoCommand) {
    Write-Host "Chocolatey is not installed. Please install Chocolatey to proceed." -ForegroundColor Red
    Write-Host "Installing Chocolatey..." -ForegroundColor Yellow
    Set-ExecutionPolicy Bypass -Scope Process -Force; `
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `
    iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
    # Re-check if Chocolatey is installed after attempting installation
    $chocoCommand = Get-Command choco -ErrorAction SilentlyContinue
    if ($null -eq $chocoCommand) {
        Write-Host "Failed to install Chocolatey. Please install it manually." -ForegroundColor Red
        exit 1
    } else {
        Write-Host "Chocolatey installed successfully." -ForegroundColor Green
    }
} else {
    Write-Host "Chocolatey is installed at $($chocoCommand.Source)" -ForegroundColor Green
}

# Check if mkcert is installed
$mkcertCommand = Get-Command mkcert -ErrorAction SilentlyContinue

if ($null -eq $mkcertCommand) {
    Write-Host "mkcert is not installed. Please install mkcert to proceed." -ForegroundColor Red
    choco install mkcert
    exit 1
} else {
    Write-Host "mkcert is installed at $($mkcertCommand.Source)" -ForegroundColor Green
}

# Check if OpenSSL is installed
$opensslCommand = Get-Command openssl -ErrorAction SilentlyContinue

if ($null -eq $opensslCommand) {
    Write-Host "openssl is not installed. Please install openssl to proceed." -ForegroundColor Red
    choco install openssl -y
    exit 1
} else {
    Write-Host "openssl is installed at $($opensslCommand.Source)" -ForegroundColor Green
}

# Check if mkcert installed root CA
# Define the path to the mkcert root CA directory
$mkcertRootCADir = Join-Path -Path $env:USERPROFILE -ChildPath "AppData\Local\mkcert"

# Define the expected root CA certificate files
$rootCAKeyFile = Join-Path -Path $mkcertRootCADir -ChildPath "rootCA-key.pem"
$rootCACertFile = Join-Path -Path $mkcertRootCADir -ChildPath "rootCA.pem"

# Check if the root CA certificate files exist
$rootCAKeyExists = Test-Path -Path $rootCAKeyFile -PathType Leaf
$rootCACertExists = Test-Path -Path $rootCACertFile -PathType Leaf
Write-Host ""

# run mkcert
Write-Host "********* mkcert Installation *********" -ForegroundColor Magenta
mkcert --install

if ($rootCAKeyExists -and $rootCACertExists) {
    Write-Host "Root certificate found, moving to cert folder..." -ForegroundColor Green
    # Copy the root CA certificate and key files to the certs folder
    $destinationRootCACertFile = Join-Path -Path $certsFolder -ChildPath "rootCA.pem"
    Copy-Item -Path $rootCACertFile -Destination $destinationRootCACertFile -Force
} else {
    Write-Host "Root certificate NOT found,." -ForegroundColor Red
    exit 1
}
Write-Host ""

# Certificate creation
Write-Host "********* Creating Hosting Certificate *********" -ForegroundColor Magenta
if (-Not (Test-Path -Path $etcFolder)) {
    New-Item -ItemType Directory -Force -Path $etcFolder
} else {
    if (-Not (Test-Path -Path $certsFolder)) {
        New-Item -ItemType Directory -Force -Path $certsFolder
    } else {
        Set-Location $certsFolder
        mkcert -key-file key.pem -cert-file cert.pem pssx-authserver pssx-apihost pssx-web pssx-web-public pssx-rabbitmq pssx-redis pssx-sql-server docker.localhost "*.docker.localhost" localhost 127.0.0.1 ::1
        Set-Location $slnFolder
    }
}

# Check if cert.pem and key.pem are created
$certFilePath = Join-Path -Path $certsFolder -ChildPath "cert.pem"
$keyFilePath = Join-Path -Path $certsFolder -ChildPath "key.pem"
$p12FilePath = Join-Path -Path $certsFolder -ChildPath "cert.p12"
$p12Password = "cert"  # Set your desired password if needed
$certExists = Test-Path -Path $certFilePath -PathType Leaf
$keyExists = Test-Path -Path $keyFilePath -PathType Leaf
Write-Host ""

Write-Host "********* Converting Certificates *********" -ForegroundColor Magenta
if ($certExists -and $keyExists) {
    Write-Host "Certificate files: cert.pem and key.pem are found" -ForegroundColor Green
    $opensslCommand = "openssl pkcs12 -export -out $p12FilePath -inkey $keyFilePath -in $certFilePath -password pass:$p12Password -certfile $destinationRootCACertFile"
    Invoke-Expression $opensslCommand

    # Verify the conversion
    if (Test-Path $p12FilePath) {
        Write-Host "Conversion successful. The .p12 file is located at: $p12FilePath" -ForegroundColor Green
        if (-not [string]::IsNullOrEmpty($p12Password)) {
            Write-Host "Certificate password is $p12Password" -ForegroundColor Yellow
        }
    } else {
        Write-Host "Conversion failed. Please check the file paths and try again." -ForegroundColor Red
    }
} else {
    Write-Host "cert.pem or key.pem not found. Please ensure the certificates are created correctly." -ForegroundColor Red
}
Write-Host ""

Configuring SSL certificate

A website needs an SSL certificate to keep user data secure, verify ownership, prevent attackers from creating a fake version of the site, and gain user trust.

This document introduces how use an SSL certificate (HTTPS) for your application.

Get an SSL Certificate from a Certificate Authority

You can get a SSL certificate from a your company's certificate authority (CA), public certificate authorities such as Let's Encrypt or Cloudflare and so on or create locally

Once you have a certificate, you need to configure your web server to use it. The following references show how to configure your web server to use a certificate.

Common Exceptions

If you encounter the following exceptions, it means your certificate is not trusted by the client or the certificate is not valid. You will may see the following SSL certificate errors in your browser when you try to access the website.

---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot