One component of the security of a website is to ensure that client and server are communicating with each other without anyone intercepting the traffic. This is the reason for the ‘s’ tacked onto the http
of your URL. On the great wide web, there are a number of certificate authorities, but my intranet lacks a centralised body — and many company intranets lack a clear path for getting such a certificate.
One solution is to sign our own certificate. Here are some steps to do this in Powershell, where we will be creating a cert for a server with the DNS `vm001.example.org`. Please note that administrative/elevated privileges will be required for some steps.
First, we can begin by creating a root certificate. The root certificate is the certificate which certifies that the server’s cert is valid. Adding the root certificate to our machine’s certificate store will enable all server certificates to be recognised as valid.
$params = @{
DnsName = "My Root Cert"
KeyLength = 2048
KeyAlgorithm = 'RSA'
HashAlgorithm = 'SHA512'
KeyExportPolicy = 'Exportable'
NotAfter = (Get-Date).AddYears(10)
CertStoreLocation = 'Cert:\LocalMachine\My'
KeyUsage = 'CertSign','CRLSign' # to avoid invalid cert error
}
$rootCA = New-SelfSignedCertificate @params # invoke the command
Now that we have our root certificate (our certificate authority), we can generate the server cert:
$params = @{
DnsName = 'vm001.example.org' # this is the address we will connect to in our browser
Signer = $rootCA
KeyLength = 2048
KeyAlgorithm = 'RSA'
HashAlgorithm = 'SHA512'
KeyExportPolicy = 'Exportable'
NotAfter = (Get-date).AddYears(5)
CertStoreLocation = 'Cert:\LocalMachine\My'
}
$cert = New-SelfSignedCertificate @params
Third, we want to add the self-signed root certificate (our certificate authority) to the trusted root store on the client machine (i.e., the computer where someone will be using the web browser). This is needed to avoid the browser giving a warning about an untrusted connection since I am not a recognised certificate authority — the nerve!
We’ll do this in two steps: export the certificate to file, then import it into the trusted certificate store:
# write root cert to file
Export-Certificate -Cert $rootCA -FilePath "C:\certs\rootCA.crt"
# import root cert to trusted root store
Import-Certificate -CertStoreLocation 'Cert:\LocalMachine\Root' -FilePath "C:\certs\rootCA.crt"
Fourth, we’ll go ahead and export the certificate for use in whatever application is being run on the server. We’ll want to get a key
file and a crt
file. Before that, we’ll export the cert in .pfx
, which is used to store multiple cryptographic objects in the same file. Note that we’ll also be adding a password to protect it from malicious coworkers.
Export-PfxCertificate -Cert $cert -FilePath 'C:\certs\vm001.pfx' -Password (ConvertTo-SecureString -AsPlainText 'password2!' -Force)
Fifth, we can extract the relevant key and cert files from the .pfx
. I’m not sure how to do this within Powershell unless one installs openssl
. I usually have a WSL instance sitting around pre-installed with openssl
, so I’ll log into that and run these commands:
# get key file
openssl pkcs12 -in vm001.pfx -nocerts -out vm001.key
# get crt file
openssl pkcs12 -in vm001.pfx -clcerts -nokeys -out vm001.crt
# remove password from key file
openssl rsa -in vm001.key -out vm001.un.key
Finally, these files can be added to your web app. For example, with uvicorn
, we can add the ssl_keyfile
and ssl_certfile
parameters to the uvicorn.run
function:
uvicorn.run(
app,
host=host,
port=int(port),
[...]
ssl_keyfile=r'C:\certs\vm001.un.key',
ssl_certfile=r'C:\certs\vm001.crt',
)
A couple limitations are worth mentioning. First, any user lacking permissions to add certificates to their trusted cert store will be greeted with a stern warning from the browser and a message about https
not being trusted (since our certificate authority is not trusted).

To resolve this, the Import-Certificate
step needs to be performed on the client machine or, if that’s not possible, the user should be instructed to bypass this but only when using a trusted app. Ignoring such warnings on the world wide web is less than ideal.