Self-Signed Certificate usage on Local Development


Most of the time, when developing an application on our local machine, we use only HTTP traffic to simplify development and focus on application logic. However, there are important scenarios where enabling encrypted traffic locally becomes necessary. For example, certificate lifecycle testing, developing security features such as mutual TLS (mTLS), or implementing certificate validation logic all require running an application with a certificate.
Unless you plan to purchase a real certificate from a Certificate Authority (CA) such as GoDaddy or DigiCert, you will usually create a self-signed certificate using tools like OpenSSL or PowerShell. In this article, I’ll give an overview of the key components involved, whether you use a self-signed certificate on the hosting server (server-side) or the client application (client-side).
Certificate Overview:
When creating a self-signed certificate, you typically follow these steps (note: there are other optional steps, but we’ll keep it simple here):
Generate a self-signed certificate, which includes both a public and private key.
Export the public certificate (without the private key) for distribution to clients or other systems.
As an analogy, generating a certificate is like creating a custom lock for your house and assigning it a unique barcode. The private key is the key to that lock, which you keep secure and never share. The public certificate is the barcode you distribute to anyone who might visit. When someone arrives, they can scan the barcode on your lock and compare it to the one you provided. If it matches, they know they’re at the correct house and not at an imposter house.
While this analogy helps explain what a certificate is, it doesn’t fully capture all the benefits certificates provide:
Identity Authentication: Certificates prove that a party is who it claims to be.
Encryption: They ensure that data in transit is obscured and only understandable to the intended parties.
Integrity: Certificates guarantee that the message received is in its original form and has not been altered.
Non-repudiation: They provide proof that a message was sent by a specific party, preventing that party from denying their involvement later.
Certificates, therefore, play a crucial role in establishing trust, securing communications, and maintaining the integrity of digital interactions.
Certificate File Types:
Let’s go over what are all the file types associated with a certificate:
*.key
- A base64 file contains the certificate’s private key for decryption & signing.*.cert
/*.crt
- A binary or base64 file, it is simply the certificate itself without the private key.*.pfx
- A binary file which contains both the certificate and its private key.*.pem
- Similar to the pfx file but it is in base64 format.
Certificate Store Locations:
When you create a certificate or when you import a certificate over the web, it will be stored in the Window certificate store locations. Let’s go over them to have a better understanding of the type of storage we work with. Windows certificate stores are organized by location (Local Machine & Current User) and type (Personal & Trusted Root).
The Local Machine location holds system-wide certificates, requires administrative rights to modify, and is used by services and applications running as SYSTEM—making it ideal for hosted applications and web servers. This can be opened by running certlm.msc
command.
In contrast, the Current User location contains certificates specific to each user, does not require admin rights, and is only accessible to the logged-in user, making it suitable for client authentication scenarios. This can be opened by running the certmgr.msc
command.
Within these locations, the Personal (My) store contains certificates with private keys used for authentication and signing, and is typically where SSL server certificates reside. The Trusted Root (Root) store contains trusted certificate authorities (CAs); for a certificate chain to be trusted, all certificates must link to a root certificate here. Self-signed certificates must also be placed in the Trusted Root store to be recognized as trusted.
Generate Self-Signed Certificate:
Let’s see how a self signed certificate works on a server application and a client application.
First, let’s generate a self-signed certificate named ServerCertificate
in the Local Machine's Personal (My) certificate store - cert:\LocalMachine\My
. This command also generates the certificate private key.
New-SelfSignedCertificate `
-Subject "CN=ServerCertificate" `
-CertStoreLocation "cert:\LocalMachine\My" `
-KeyExportPolicy Exportable `
-KeyUsage DigitalSignature,KeyEncipherment `
-KeySpec KeyExchange
This command creates
If you opencertlm.msc
and browse to [Console Root\Certificates (Local Computer)\Personal\Certificates]
you should see the certificate and it is available for you to bind it to specific HTTPS port of your application.
At this point, if your application exposes a web endpoint, you should be able to access it using HTTPS instead of HTTP. However, your browser will likely display a warning indicating the connection is not secure, because it cannot verify the certificate authority of your self-signed certificate and therefore it will not trust it.
Public Certificate Installation:
As we pointed out earlier in this blog, when working with Self-Signed certificate it must be stored in the Trusted Root store to be recognized as trusted and depends on where you make this request from:
If you are on a client machine connecting to a server that uses a self-signed certificate:
You should import the server’s public certificate into your Current User > Trusted Root Certification Authorities store. This tells your system to trust that certificate when connecting to the server.If you are on the same machine as the server (e.g., testing locally):
You can import the public certificate into the Local Computer > Trusted Root Certification Authorities store. This will make the certificate trusted for all users and services on that machine.
In 90% of the case we are developing on the same machine where the application hosted so we would fall into the second scenario. We can run the following Powershell command to generate the Public Certificate from the certificate we created earlier by looking up its subject value:
#Find the certificate on our Local Machine store using the subject value.
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=ServerCertificate" }
#Generate the public certificate to be used by the client side.
Export-Certificate -Cert $cert -FilePath "C:\Build\PublicCertificate.cer"
Next we import this into the Server’s Local Machine trusted Root store.
#If testing on the server import to local computer's trusted root
Import-Certificate -FilePath "C:\Build\PublicCertificate.cer" -CertStoreLocation "Cert:\LocalMachine\Root"
Now, if you are on a different machine then ensure you install this on the Current User trusted Root store.
#if testing on remote client, import to current user trusted root.
Import-Certificate -FilePath "C:\Build\PublicCertificate.cer" -CertStoreLocation "Cert:\CurrentUser\Root"
Once your public certificate is installed in the appropriate certificate store on your machine, you should no longer see certificate errors when browsing to the application endpoint. This is because your machine now explicitly trusts the certificate, even though it is not signed by a well-known or recognized Certificate Authority.
While generating a self-signed certificate for local development or testing is straightforward and can be done with a few commands or tools, the process for obtaining and managing a real (publicly trusted) certificate is more involved.
A real certificate is typically issued by a trusted Certificate Authority (CA) and requires you to generate a Certificate Signing Request (CSR) with accurate organizational and domain information. The CA will validate your identity and domain ownership before issuing the certificate. The parameters required for a real certificate—such as subject details, key length, and usage—may be dictated by the CA’s policies and industry standards.
Additionally, real certificates come with important maintenance responsibilities:
Renewal: Certificates have expiration dates and must be renewed before they expire to avoid service disruptions.
Revocation: If a private key is compromised, the certificate must be revoked and replaced immediately.
Chain of Trust: Real certificates are part of a trust chain, including intermediate and root certificates, which must be properly installed and maintained.
Security: Private keys must be securely stored and protected from unauthorized access.
In summary, while self-signed certificates are useful for development and internal testing, real certificates require careful management, adherence to best practices, and ongoing maintenance to ensure secure and trusted communications in production environments.
Happy reading.
Subscribe to my newsletter
Read articles from Quang Phan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
