Generate self-signed SSL/TLS certificate for local IP address or local domain
In real life, when we build our website and make it public, some paid or free Certificate Authority (CA) will help us sign a certificate for our website domain (IP address is not acceptable!) and enable SSL/TLS connections from user browser to our server.
Given the secure reasons, the browser will only admit those servers's certificates signed from the authorized CA, and the CA certificate is kept in our host system trust store. In Linux, you can view the CA certificate file like /etc/ssl/certs/ca-certificates.crt
.
One of the most popular Certificate Authorities is Let's Encrypt, which is a free and non-profit CA.
However, in many internal networks and development environments, we often need self-signed certificate more frequently.
Here is an example, we will generate a local server certificate that is signed by a local CA. Finally, let Chrome can visit our local website without security warning.
In brief, these steps we need to sign local sever certificate actually simulate how those CA sign certificates for public servers, as following:
- Create a local Root CA.
- Create a CSR(Certificate Signing Request) file for local server
127.0.0.1
. - The local Root CA use the CSR to generate a certificate for local server
127.0.0.1
. - Install the local Root CA into our system(Windows, Ubuntu or macOS) trust store.
- Run a simple https server to test local server certificate.
For those official CA, they have to validate the domain is owned by the server before the step 3
, and we can ignore step 4
as they are already installed into the system or the browser trust store.
And there is nice picture from How to create your own self-signed root Certificate Authority(CA) to show the relationship between CA
, server
and browser
.
1. Create a local CA
Generate a file RootCA.key
and a file RootCA.crt
of our local root CA:
openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.crt -subj "/C=US/CN=Example-Root-CA"
You can change Example-Root-CA
to others or add more fields to CA.
[Optional] Create a CA with a configuration file,
openssl req -x509 -nodes -new -keyout RootCA.key -out RootCA.crt -config <(cat <<EOF
[ req ]
default_bits = 2048
default_md = sha256
default_days = 3650
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = US
ST = California
L = San Francisco
O = Example Corp
OU = IT Department
CN = www.example.com
emailAddress = admin@example.com
EOF
)
process substitution does not work in bash scripts!
2. Create a signed certificate for the local server
Next, we should apply the local CA to sign a certificate for our local server using the server's Certificate Signing Request (CSR)
, which will be accessed through the localhost
or 127.0.0.1
from our local machine.
2.1. Generate the server's CSR
When generating the CSR file with OpenSSL, we can either specify certain details directly in the command line or use a configuration file. While you can provide some information via command-line arguments, complex configurations like specifying [v3_req]
and [alt_names]
in belowing are typically done through a configuration file.
subjectAltName
in [v3_req]
will let you specify more than domain/IP addresses as Subject Alternative Names (SANs). And now modern browser require the server certificate to include SANs. Usage of the Common Name (CN) alone is not considered secure enough, and omitting SANs may result a certificate validation error!
Here is an example for using pure arguments to specify -subj
:
openssl req -new -newkey rsa:2048 -nodes -keyout privkey.pem -out csr.pem -subj "/C=US/ST=California/L=San Francisco/O=Example Corp/OU=IT Department/CN=www.example.com/emailAddress=admin@example.com"
Here is an example to use the configuration file as -config
.
- [Optional] Customize DNS by editing
/etc/hosts
(Other machines also have to do this if they would like to visit the server),
127.0.0.1 localhost
127.0.0.1 fake1.local
127.0.0.1 fake2.local
- Create a configure file for CSR including typical sections as:
[req]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
countryName = XX
stateOrProvinceName = N/A
localityName = N/A
organizationName = Self-signed certificate
commonName = 127.0.0.1: Self-signed certificate
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = 127.0.0.1
DNS.1 = localhost
DNS.2 = fake1.local
DNS.3 = fake2.local
- Generates the CSR
To generate a CSR using the configuration file with OpenSSL, you can use the following command:
openssl req -new -nodes -keyout localhost.key -out localhost.csr -config localhost.conf
[Optional] mix -subj
and -config
, to be short like:
SAN_LIST="[SAN]\nsubjectAltName=DNS:localhost, DNS:*.localhost, IP:127.0.0.1"
openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example CORP/CN=localhost.local" -reqexts SAN -config <(echo -e "$SAN_LIST")
Here, localhost.conf
is the configuration file and the two outputs are:
localhost.key
is the private key file for the local server to communicate with the clients securely.localhost.csr
is the CSR file that the local server will use in the next step to sign its certificate from the local CA.
- Verify the CSR file
localhost.csr
:
openssl req -text -noout -verify -in localhost.csr
2.2. Sign the CSR with the local CA
Now, it's time for local CA to sign a certificate for our local server by using the server's CSR localhost.csr
file, thereby issuing a signed certificate.
- Generates
localhost.crt
by using CSRlocalhost.csr
,
openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.crt -CAkey RootCA.key -CAcreateserial -extensions v3_req -extfile localhost.conf -out localhost.crt
-extensions v3_req -extfile localhost.conf
: openssl x509
will contain subjectAltName
extension in the certificate.
If X509
extensions(subjectAltName
) are missing from the certificate, the browser will still report security issues, such as its security certificate does not specify Subject Alternative Names.
Or after OpenSSL v3, you can copy the extensions specified in the CSR to the certificate by openssl x509
as this:
openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.crt -CAkey RootCA.key -CAcreateserial -copy_extensions copyall -out localhost.crt
- View the generated
localhost.crt
:
openssl x509 -text -noout -in localhost.crt
- Verify the generated
localhost.crt
:
openssl verify -verbose -CAfile RootCA.crt localhost.crt
If the CA and Subject are the same one, the step of creating the local CA can be skipped.
openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -config localhost.conf
3. Use the signed certificate in the local server
Run up a node https server to use the signed certificate for the local sever.
npx http-server -p 8082 --ssl --cert localhost.crt --key localhost.key
Then visit:
The browser will give you security warning as the local root CA is not trusted in default.
4. Install the local CA
TO trust the root local CA, we must install the local CA certificate RootCA.crt
into each system trust store or each browser.
- Windows system trust store
- Ubuntu system trust store
- macOS system trust store
Then visit again, the browser will show green!
[Optional] Sign the CSR with openssl ca
For complicated configuration for CA to sign a certificate, you can use openssl ca
and the configuration file is like:
cd /path/to/your/ca/
mkdir -p newcerts
touch index.txt
echo 1000 > serial
cat <<EOF > /path/to/your/ca/openssl.cnf
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /path/to/your/ca
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/RootCA.crt
serial = $dir/serial
private_key = $dir/RootCA.key
default_days = 365
default_md = sha256
policy = policy_any
x509_extensions = usr_cert
copy_extensions = copy
[ policy_any ]
countryName = supplied
stateOrProvinceName = supplied
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ usr_cert ]
basicConstraints=CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
openssl ca -config /path/to/your/ca/openssl.cnf -in localhost.csr -out localhost.crt -batch
Troubleshooting
Chrome red security warning
- Go to
Developer Tools
. - Click
Security
tab. - Check
Security overview
issues.
Resources
How to create an HTTPS certificate for localhost domains · GitHub
How to add X.509 extensions to certificate OpenSSL | GoLinuxCloud