A toolkit to build your own private root CA and create a self-signed certificate signed by the CA.
Creating a self-signed certificate signed by a private authority is tricky, but it's useful when you're running local dev server with SSL/TLS. This article will go through the process of generating, signing and installing a self-signed certificate using a private authority on macOS.
In this example, certificate chain will be:
[Root CA] --> [Intermediate CA] --> [Server Certificate]
And each certificate has:
Root CA: Subject: C=JP, ST=Tokyo, O=Fixture, CN=Fixture Root CA
Intermediate CA: Subject: C=JP, ST=Tokyo, O=Fixture, CN=Fixture Intermediate CA
Server Certificate: Subject: C=JP, ST=Tokyo, O=Fixture, CN=${FQDN}
Please replace ${FQDN}
to the hostname of your server.
Install Docker, clone this repository, cd into it and run:
./script/build.sh
export DEFAULT_COUNTRY="JP"
export DEFAULT_PROVINCE="Tokyo"
export DEFAULT_ORG="Fixture"
export FQDN="your.example.com"
./script/run.sh
This will build and run a docker container that has:
- The
openssl
command and the patched CA.pl script - Custom OpenSSL config files in the /etc/ssl/conf.d directory
You're in the container. Let's get started by creating a new private authority:
OPENSSL_CONFIG="-config /etc/ssl/conf.d/openssl-rootca.cnf" CA.pl -newca
Since you have default values in the conf files, just keep hitting enter key except the common name:
Common Name (e.g. server FQDN or YOUR name) []:Fixture Root CA
Check the certificate:
openssl x509 -in /home/CA/cacert.pem -text
Not After
date is ten years after the current date- Issuer and Subject should be identical
X509v3 Basic Constraints
should beCA:TRUE
Next, create an intermediate CA certificate. Don't have to set OPENSSL_CONFIG
.
cd intermediateCA
CA.pl -newreq
Again, you have default values in the conf files, so just keep hitting enter key except the common name:
Common Name (e.g. server FQDN or YOUR name) []:Fixture Intermediate CA
Check the request
openssl req -in ./newreq.pem -text
Sign the intermediate certificate request by the root CA
OPENSSL_CONFIG="-config /etc/ssl/conf.d/openssl-signca.cnf" CA.pl -signCA
Check the certificate
openssl x509 -in ./newcert.pem -text
Not After
date is ten years after the current date
Reorganize the intermediate CA directory so that it can sign a server certificate in the next step.
mkdir private
mv newkey.pem private/cakey.pem
mv newcert.pem cacert.pem
mkdir newcerts
touch index.txt
echo 00 > serial
Move back to the workdir and create a directory for the FQDN
mkdir /home/domains/${FQDN} && cd /home/domains/${FQDN}
Create a server certificate request.
OPENSSL_CONFIG="-config /etc/ssl/conf.d/openssl-server-req.cnf" CA.pl -newreq
Again, you have default values in the conf files, so just keep hitting enter key except the common name:
Common Name (e.g. server FQDN or YOUR name) []:${FQDN}
Check the request
openssl req -in ./newreq.pem -text
Check the X509v3 Subject Alternative Name
field:
- Make sure you have the wildcard (
*.${FQDN}
) in theX509v3 Subject Alternative Name
field. - If you are using Google Chrome, make sure you have the FQDN (
${FQDN}
) in theX509v3 Subject Alternative Name
field- Otherwise you'll see the
ERR_CERT_COMMON_NAME_INVALID
error - More info: Support for commonName matching in Certificates (removed)
- Otherwise you'll see the
Sign the server certificate request by the intermediate CA
OPENSSL_CONFIG="-config /etc/ssl/conf.d/openssl-sign.cnf" CA.pl -sign
Check the certificate
openssl x509 -in ./newcert.pem -text
This Issuer should be the intermediate CA and it should have X509v3 Subject Alternative Name
field.
Create a certificate chain:
openssl x509 -in newcert.pem > cert.pem
openssl x509 -in ../../intermediateCA/cacert.pem >> cert.pem
Remove passphrase from private key and rename it:
openssl rsa -in newkey.pem -out key.pem
Ref: Removing the passphrase from your key file
Verify the certificate and private key:
[ "$(openssl rsa -pubout -in key.pem 2> /dev/null)" = "$(openssl x509 -pubkey -in cert.pem -noout)" ] && echo "OK" || echo "Verification Failed"
Done, now let's exit from the container.
exit
Next, let's install the root certificate to your macOS's Keychain app.
- Add the certificate to Keychain by double clicking
CA/cacert.pem
file - Add it to the "System" keychain
- In the Keychain, select the certificate
- Right click -> Get Info
- Change to "Always Trust"
Note that Firefox doesn't use Keychain so you have to add the root certificate manually if you're using Firefox.
If you're using GitHub Enterprise Server, run the following:
./script/add-root.sh [hostname]
Use the domains/${FQDN}/cert.pem
and domains/${FQDN}/key.pem
files.
Run the following:
./script/ghes-install.sh [hostname] password
Lastly, confirm the SSL connection with the openssl
command.
openssl s_client -connect ${FQDN}:443 -verify 0 -CAfile ./CA/cacert.pem
Done!
Ref: