How to Create SSL Certificates for Development
Automate the generation of SSL certificates

Last week I started implementing an MQ Telemetry (MQTT) broker. MQTT is a lightweight publish-subscribe protocol used with Internet of Thing (IoT) solutions. This broker had to receive sensor data and resend it to other MQTT clients. Communication had to be secure.
Secure, in this case, meant using certificates.
OK, certificates, sigh, here we go again. I exhaled through my nose and gathered myself. I never liked working with certificates. I searched on the internet and found how to generate self-signed certificates. “I really should automate this,” I remember having this thought a couple of times before…
But somehow, I always end up creating the certificate by typing commands in a terminal. I then continue with the project and never look back.
I always had a love-hate relationship with certificates. I never understand how they work but once in a while need them during development.
So, I ended up creating this article. It is a future reference for me. It contains just enough details to understand certificates from a developer’s perspective.
Besides this, I finally automated the creation of a locally trusted development certificate. See this GitHub repository.
I hope you will find it useful.
A Little Bit of Theory
I promise, only a little bit.
Do we need certificates?
Yes, we do. We need certificates for two main reasons.
- Secure transmission of data.
- Identification of the sender or recipient.
Secure transmission of data
You can use two types of encryption to transmit data securely, symmetric and asymmetric. Both have their pros and cons. Later, we will see that when a browser uses HTTPS to connect to a website, the browser uses both types of encryption.
What is the difference between symmetric and asymmetric encryption?
I find symmetric encryption the easiest to understand. Both the sender and recipient share a secret key. The sender uses this secret key to change the message to send. The recipient uses the same secret key to recover the original message.
Asymmetric encryption always feels a bit like magic to me. In this case, you create two keys that belong together. The first is the public key. You can share it with anyone that wants to send you a message. The second key is the secret key. Only you need to know it.
If you encrypt a message using the public key, it can only be decrypted with the private key. Also, if you encrypt a message using the private key, it can only be decrypted using the public key. Pure magic.
Both types of encryption are used when using SSL.
SSL communication (simplified)
When a browser connects to a website that is using SSL, the browser performs the following steps.
- You instruct the browser to connect to a website via HTTPS. The browser requests that the webserver identify itself.
- The webserver sends a copy of its certificate back to the browser.
- The browser validates the identity of the certificate. Does the name in the certificate match the domain name? Do I trust the certificate authority that signed the certificate?
- The browser generates a password and encrypts it using the public key. It retrieved the public key from the certificate. This is asymmetric encryption. The browser sends the encrypted password to the webserver.
- The web server decrypts the password using the private key.
- The browser sets up an encrypted connection. Both sides encrypt and decrypt data using the earlier generated password. This is symmetric encryption.
The reason the browser only uses asymmetric encryption for transmission of the password has to do with the performance of asymmetric encryption. Asymmetric encryption is significantly slower than symmetric encryption.
Identification of the sender or recipient
Certificates are also used for identification. This way, you can be sure that you are actually connecting to the correct website.
The validation is two ways. First, the browser validates if the certificate that is returned by the webserver can be trusted. This is checked by making sure that the certificate is signed by a certificate authority (CA).
The operating system of your device contains a list of certificates that are trusted.
For example, see the screenshot below that shows the IdenTrust root certificate. The IdenTrust root certificate is one of the most used CA certificates. Mostly because Let’s Encrypt certificates are cross-signed by IdenTrust.

This list of trusted certificates is used by the browser to validate that the received certificate is signed by a CA in this list.
The second type of identification is to validate if the common name of the certificate corresponds with the domain that serves the certificate.

Certificate file formats
There are a lot of different formats for storing certificates. Sometimes, I see file formats used incorrectly. The following formats are used for certificates.
PEM format
The Privacy Enhanced Mail (PEM) format is the most common format to store certificates of private keys. The content of the file is Base64 encoded ASCII.
The file can have the extensions .pem
, .crt
, .key
, or .ca-bundle
. To make sure that the file is actually a PEM file, open it and check to see that it starts with BEGIN CERTIFICATE
or BEGIN RSA PRIVATE KEY
.
P7B format
A file with the P7B or PKCS#7 format is also stored in Base64 encoded ASCII.
The file has the extension .p7b
or .p7c
. The P7B format can only be used to store certificates and not private keys. The file format is used on Windows and Java Tomcat.
DER format
The Distinguished Encoding Rules (DER) format stores certificates and private keys in a binary format. The file has the extension .der
or .cer
. The DER format is often used on Java platforms.
PKCS#12 format
The PKCS#12 or PFX format is stored in a binary file. The private key and certificate are stored in a single file.
The file has the extension .pfx
or .p12
. The files are used on Windows and macOS to import and export certificates and private keys.
Generating One-Off Self-Signed Certificates
A self-signed certificate can be generated on any computer. I use macOS with version 1.1.1 of OpenSSL. OpenSSL is a full-featured toolkit and cryptography library. For most operating systems there are ready-to-use OpenSSL binary packages.
The following steps are needed for generating a self-signed certificate
- Generate a private key.
- Create a certificate signing request.
- Generate the certificate.
1. Generating a private key
The first step is to create a private key by executing the following command.openssl genpkey -algorithm RSA -des3 -out private-key.pem -pkeyopt rsa_keygen_bits:4096
genpkey
— The OpenSSL command to execute, in this case, generate a private key
algorithm RSA
— Which public-key algorithm to use. Other options are available such as RSA-PSS, EC, X25519, X448, ED25519, and ED448.
-des3
— This option specified that OpenSSL must encrypt the private key using Triple-DES. Upon executing the command, it asks which password to use.
out privateKey.pem
— OpenSSL should store the private key in a file called privateKey.pem
.
pkeyopt rsa_keygen_bits:4096
— Specifies the number of bits that should be used by the generated key. I use 4096 bits.
The format of the output file privateKey.pem
is by default a PEM file. With genpkey
, OpenSSL uses the PKCS #8 syntax to store the key in the file.
I use genpkey
instead of genrsa
because it uses more sensible defaults.
For example, the default number of bits of the generated key of genrsa
is 512 bits. Which according to ars TECHNICA in 2015, could be broken in four hours using a $75 Amazon EC2.
2. Creating a Certificate Signing Request (CSR)
A CSR is like the order to create a certificate. If you need an official SSL certificate, you send it to an official certificate authority (CA). They use the CSR to generate an official certificate.
We, however, will use this request to generate a certificate ourselves, a self-signed certificate.
Previously, I described the two main reasons to use a certificate, encryption, and identification. OpenSSL will ask a number of questions to know what information to put in the CSR.
The question that asks for your Common Name
is important regarding identification. You should answer with the fully qualified domain name that you want to use the certificate for. This can also be an internal domain, localhost or an IP address.openssl req -new -key private-key.pem -out csr.pem
req
— Create a certificate request in PKCS#10 format.
-new
— Generate a new certificate request. Ask the user for all the needed information.
-key
— The filename of the private key to use. The private key was generated in the previous step.
-out
— The filename of the created certificate request.
OpenSSL will ask the following questions:
- Common name: The FQDN (fully-qualified domain name) you want to secure with the certificate.
- Organization: The full name of your organization. Only important for trusted certificates.
- Organization Unit (OU): Your department. Only important for trusted certificates.
- City or locality: The city where your organization is located.
- State or province: The state or province where your organization is located.
- Country: The official two-letter country code where your organization is located.
3. Creating the self-signed certificate
With the private key from step one and the certificate request from step two, you can create a self-signed certificate. The following OpenSSL command creates the certificate.openssl x509 -in csr.pem -out certificate.pem -req -signkey private-key.pem -days 365
x509
— Perform a certificate command.
-in
— Indicates the certificate signing request (csr.pem
).
-out
— The filename to use to save the generated certificate. In this case, certificate.pem
.
-req
— Indicate to OpenSSL that the input is a CSR.
-signkey
— Self-sign the certificate request using the given private-key.pem
file.
-days
— The number of days the generated certificate is valid. Normal values are 365, 730, and 1095 days, to specify a duration of one, two, or three years.
You now have a certificate.pem
file that can be used for local development. Depending on your development environment, you may have to convert the certificate to another format.
For Node.js development you can directly use the certificate.pem
file.
Creating Multiple Self-Signed Certificates
Simply generating a self-signed certificate works great for a single one-man project. If you have multiple projects or work with a development team, it can become cumbersome to create all these self-signed certificates for each project and import them into the certificate store of each developer.
Sure, you can create scripts for generating them but I think creating a certificate authority (CA) yourself is a better solution.
You can become a CA by creating a CA certificate and use this certificate to sign your self-signed certificates. Your development team can trust the CA certificate and will automatically trust all the generated certificates that are signed with this CA certificate.
To become a certificate authority and sign a self-signed certificate you have to perform the following steps:
- Generate a private key for the CA.
- Generate a root certificate.
- Create a private key for the certificate.
- Create a certificate signing request.
- Create a certificate and sign it with the CA private key
1. Generate a private key for the CA
The private key for the CA can be generated using the following command.openssl genpkey -algorithm RSA -des3 -out private-key-ca.pem -pkeyopt rsa_keygen_bits:4096
This command is no different than the command for generating a private key for a self-signed certificate. The only difference is the filename private-key-ca.pem
.
This command generates the file private-key-ca.pem
in the current directory.
2. Generate a root certificate
With the following command, we directly generate a root certificate based on the private key generated in the previous step.openssl req -x509 -new -key private-key-ca.pem -sha256 -days 3650 -out ca-certificate.pem
OpenSSL again asks the passphrase of the private key and asks what information to put in the root certificate.
In this case, the answers you provide are not really relevant. The only thing I recommend is to add information to one of the fields so that you can see that this is the root certificate.
The command generates the file ca-certificate.pem
in the current directory.
3. Create a private key for the certificate
With the following command, which we used before, we generate a private key for this specific certificate.openssl genpkey -algorithm RSA -des3 -out private-key.pem -pkeyopt rsa_keygen_bits:4096
This command generates the file private-key.pem
in the current directory.
4. Create a certificate signing request
With the following command, which we also used before, we generate the certificate signing request.openssl req -new -key private-key.pem -out csr.pem
OpenSSL again will ask questions to put in the certificate signing request. The one question that is important is the Common Name. Set this to the domain name or IP address where you want to use the certificate.
Newer browsers use the Subject Alternative Name (SAN) to store DNS names or IP addresses. See the part about SAN later in the article.
5. Create a certificate and sign it with the CA private key
The following command creates the self-signed certificate and signs it with the private key of the CA.openssl x509 -req -in csr.pem -CA ca-certificate.pem -CAkey private-key-ca.pem -CAcreateserial -out certificate.crt -days 3650
x509
— Perform a certificate command.
-req
— The x509
command expects a certificate as input. With -req
you indicate that you will provide a certificate request as input.
-in
— The certificate request (csr.pem
).
-CA
— The certificate of the CA to use to sign the created certificate. (ca-certificate.pem
)
-CAkey
— The private key of the CA to use to sign the created certificate. (private-key-ca.pem
)
-CAcreateserial
— Each certificate issued by a CA must contain a unique serial number. OpenSSL stores the used serial numbers in a file with the same name as the certificate with the .srl
extension. In this case, there will be a ca-certificate.srl
in your local directory.
-out
— The filename of the certificate to generate (certificate.crt
).
-days
— The number of days the generated certificate is valid (3650
).
Automating the Creation of the Local CA and Certificates

In the previous paragraphs, I showed you the OpenSSL commands that are necessary to create the local CA and the certificates.
I wouldn’t be a good developer if I also didn’t try to automate it.
Using scripts
I created two separate scripts. One for generating the local CA and another for generating the certificate signed by the local CA. Both can be found in this GitHub repository.
There are separate scripts for Windows and macOS. One difference with the commands of the previous paragraphs is that the scripts use config files to answer the questions.
For example, the following config file is used when generating the CA certificate. It specifies the answers to the various questions from OpenSSL. You can change the answers to your liking. The same type of config is also available for the generation of the self-signed certificate.
Subject Alternative Name
Previously, I described using the Common Name to store the fully qualified domain name. As of version 58, Chrome no longer supports Common Name checking. It will ignore the field.
Instead, the SAN (Subject Alternative Name) field is the correct place to specify your domain. In the GitHub repository, there is a config file called certificate-ext.conf
that contains the domains and IP addresses that are put into the SAN.
You can change this configuration file to add the domain names and IP addresses that you need.
After you generate the certificate using the ./generate-cert.sh
command, the generated certificate will have the SAN correctly filled.

Using mkcert
If you cannot use the previous script or want a more extensive solution, you can use mkcert. Mkcert is created by Filippo Valsorda, and he is part of the Google Go team.
Mkcert is a simple tool for making locally-trusted development certificates. It is implemented in Go and requires no configuration. It works on Linux, Windows, and macOS.
You can install mkcert using brew on macOS by executing the following command:brew install mkcert
After installation, we can use the tool to create our own CA with the following command:mkcert -install
The tool creates a new CA and directly installs it in the root store of your operating system. The name of the certificate starts with mkcert
and contains the name of the computer you used to execute mkcert.
The private CA key and certificate are also stored on the file system. You can get the location by executing the following command:mkcert -CAROOT
After installing the new CA. You can use mkcert
to generate certificates that are automatically signed by the CA. For example, the following command:mkcert dev.com *.dev.com localhost 127.0.0.1
Creates a certificate that is valid for the given names. Mkcert stores the given DNS names and IP address in the Subject Alternative Name (SAN) part of the certificate.
The generated private key and certificate are available in the directory where you ran the command.

Do I Still Have a Love-Hate Relationship With SSL Certificates?
After creating the automation scripts and writing this article, I appreciate them more. I still don’t like them but see them as a necessity for solid security.
Thank you for reading.