There are numerous articles I’ve written where a certificate is a prerequisite for deploying a piece of infrastructure.
This article will guide you through generating a self-signed certificate with SAN (Subject Alternative Name) and SAN wildcard entries, replacing the deprecated usage of CN=<FQDN>.
In addition to the operational benefits of managing SAN, it is also becoming more necessary at the client level with browsers like Chrome 58 and Firefox 48 that don’t trust certificates without this specification.
If you just need a simple self-signed certificate where the Subject CN is sufficient to denote your public hostname, then read my article here instead.
If you manage a larger internal environment and want to create your own trusted Certificate Authority so you can provide trusted SAN certificates for multiple groups/services, then read my article here. These also provide better support for full browser trust.
Overview
In this article we will create a single self-signed SAN certificate that covers “mydomain.com” as well as any of its subdomains, “*.mydomain.com”. SAN allows us to define multiple names for a single certificate.
If you are migrating from an older self-signed certificate that defines its name in the CN (e.g. CN=mydomain.com), then a self-signed SAN certificate is the closest replacement.
Note that this self-signed SAN certificate will not be fully trusted by all browsers, as explained later in this article. If you need full browser trust, you’ll need to read my article here on creating a trusted CA.
If you want to test the certs, I would recommend using HAProxy. Here is a page where I describe how to do a quick HAProxy install.
Prerequisite
As a prerequisite, ensure the SSL packages are installed:
$ sudo apt-get install libssl1.0.0 -y
Customized openssl.cnf
The first step is to grab the openssl.cnf template available on your system. On Ubuntu this can be found at “/usr/lib/ssl/openssl.cnf”. You may find this in “/System/Library/OpenSSL/” on MacOS, and “/etc/pki/tls” on Redhat variants.
export prefix="mydomain" cp /usr/lib/ssl/openssl.cnf $prefix.cnf
“$prefix.cnf” in the current directory needs to be modified with the specific information about the cert we are going to generate. Under the “[ v3_req ]” section, set the following along with all the valid alternative names for this certificate.
[ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] DNS.1 =mydomain.com
DNS.2 =*.dydomain.com
Also uncomment the following line under the “[ req ]” section so that certificate requests are created with v3 extensions.
req_extensions = v3_req
Create certificate
Now we will start using OpenSSL to create the necessary keys and certificates. First generate the private/public RSA key pair:
openssl genrsa -aes256 -out $prefix.key 2048
The “-aes256” parameter will wrap this in a passphrase which you may not care about for a self-signed certificate (-des3 could be used also). If encryption is added, then you need to export the private key.
openssl rsa -in $prefix.key -out $prefix.key.pem
Although this unwrapped version of the private key is not strictly necessary for the subsequent openssl commands (they could work with $prefix.key directly), you need the .pem version for creating the certs required for web servers.
Now create the server certificate request using the private key:
openssl req -new -subj "/CN=$prefix" -key $prefix.key.pem -out $prefix.csr
Then generate the certificate using the server certificate request, private key, and the options in our modified openssl.cnf file.
openssl x509 -req -extensions v3_req -days 3650 -in $prefix.csr -signkey $prefix.key.pem -out $prefix.crt -extfile $prefix.cnf
The “$prefix.key.pem” is the private key and “$prefix.crt” is the certificate. Now verify the certificate:
openssl x509 -in $prefix.crt -text -noout
This will show the certificate, and the ‘Issuer’ and ‘Subject’ will be the same since this is self-signed. Don’t worry about the Subject not being a URL, the ‘Subject Alternative Name’ field in the extensions overrides any value in the subject when evaluated by browsers and TLS clients.
Issuer: CN=mydomain
...
Subject: CN=mydomain
...
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
DNS:mydomain.com, DNS:*.mydomain.com
Server deployment
Servers like HAProxy want a concatenated certificate+key, and Windows IIS wants a .pfx file. Here is how you would generate those files.
cat $prefix.crt $prefix.key.pem > $prefix-all.pem openssl pkcs12 -export -out $prefix.pfx -inkey $prefix.key.pem -in $prefix.crt
Browser Evaluation
Chrome
Chrome on Windows uses the OS level certificate manager, which allows you to import this self-signed certificate into the trusted root authorities. Once this is done, it shows as fully trusted in the browser.
However, on Chrome for Linux an internal certificate manager is used and that will not allow self-signed certs to be added to the “Authorities” tab. The only way to avoid the interstitial “Your connection is not private” page is to use a SAN that is fully trusted, read my article here on creating a SAN based on a trusted CA.
FireFox
Firefox uses its own internal certificate store, and will not allow you to import a self-signed certificate into the “Authorities” tab. However, you can add a permanent exception, which will have the lock icon show as green with a yellow warning sign overlapping.
If you want a fully trusted cert, read my article here on creating a SAN based on a trusted CA.
REFERENCES
http://grokify.github.io/security/wildcard-subject-alternative-name-ssl-tls-certificates/
https://gist.github.com/jhamrick/ac0404839b5c7dab24b5 (script for CA and SAN)
http://wiki.cacert.org/FAQ/subjectAltName
https://github.com/stanzgy/wiki/blob/master/network/openssl-self-signed-certs-cheatsheet.md (exact commands for CA, intermediate, chain, server cert, validating cert+key)
https://gist.github.com/akailash/7ec96e39d6951dd2293308e1d8055307
https://gist.github.com/bitoiu/9e19962b991a71165268 (original source of quick SAN with no intermediate)
https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html (multiple pages of detailed lead through for CA, intermediate, and cert)
https://security.stackexchange.com/questions/38782/ssl-tls-distinction-between-self-signed-cert-and-self-signed-ca-and-other-que (discussion on CA vs Self signed)
https://stackoverflow.com/questions/5244129/use-rsa-private-key-to-generate-public-key (discussion on RSA public/private pair and info inside)
https://stackoverflow.com/questions/5935369/ssl-how-do-common-names-cn-and-subject-alternative-names-san-work-together (explains how RFC 6125 from 2011 says SAN checked first)
https://github.com/webpack/webpack-dev-server/issues/854 (self signed cert no longer valid chrome 58)
https://bugs.chromium.org/p/chromium/issues/detail?id=700595&desc=2 (chrome 58 needs SAN for self-certs)
https://gist.github.com/akailash/7ec96e39d6951dd2293308e1d8055307 (wildcard SAN with CA, also shows how to add as trusted cert at linux level)
https://www.chromium.org/administrators/policy-list-3#EnableCommonNameFallbackForLocalAnchors (temporary workaround for chrome 58)
Reddit discussion on Chrome 58
http://users.skynet.be/pascalbotte/art/server-cert.htm (use Jetty jar to transform pkcs12 to jks java keystore)
NOTES
If you don’t want to manually type the password, you can use passin/passout specification with a file, password, or env variable:
openssl genrsa -des3 -out CA.key -passout file:capass.txt 2048
Now use that CA to create the root CA certificate.
openssl rsa -in CA.key -passin file:capass.txt -out CA.pem
When you create an encrypted public/private pair (Proc-Type: 4,ENCRYPTED)
$ openssl genrsa -aes256|-des3 -out $prefix.key 2048
If you want to get out the public side (—BEGIN PUBLIC KEY–)
$ openssl rsa -in $prefix.key -out $prefix.key.pem -pubout
If you want to get out the private side (—BEGIN RSA PRIVATE KEY–)
$ openssl rsa -in $prefix.key -out $prefix.key.pem