SSL/TLS certificates are the cornerstone of making HTTPS work. A server certificate provides proof to the client (usually a browser) that the server it is connecting to is indeed the real thing and that communication between the two is secure. This article covers the basics and provides a prescriptive “cookbook” of openssl commands for creating your own certificates and testing them.
In the beginning there was nothing. Then a private key was immaculately conceived from the darkness. The private key begat a public key and combined with a “distinguished name” a certificate signing request is presented to the almighty Certificate Authority (CA). In its infinite wisdom the Certificate Authority signs the request making it a bonafide Certificate.
Should it fall from grace, a Certificate will end up on a Certificate Revocation List and all will shun it.
private key → public key + identity of public key owner “Distinguished Name”→ certificate signing request (CSR) → certificate
The client (identified by distinguished name) creates a CSR and a certificate authority (CA) signs it to create a certificate.
Stated another way in pseudocode,
certificate = CertificateAuthority.sign(public_key + identity_of_public_key_owner)
If the certificate is for a server, the Common Name (CN) part of the Distinguished Name (DN) should be the Fully Qualified Domain Name (FQDN) like google.com for example.
Certificates are a chain of trust. The intent of the root CA certificate is to be known and accepted by all. These certificates are preloaded into all browsers and potentially operating systems themselves. Any certificate that a client receives from a server as long as it has been (eventually) signed by a certificate the client trusts, will be accepted by that client.
- Create a self-signed certificate authority (CA) certificate
- Create a server certificate signed by our own certificate authority
- Alternatively, skip the first two steps and just create a self signed server certificate
- Create a client certificate signed by our own certificate authority
To avoid the expense of actually getting the CA to sign your CSR, let’s create our own CA, or self signed root certificate. It’s the same as other root certificates, except that nobody trusts it so except for our own limited purposes it’s not very useful.
Generate the CA private key. You will be prompted for a new password.
openssl genrsa -des3 -out ca.key 2048
Verify the private key
openssl rsa -check -in ca.key
Create a certificate request. You will be prompted for address, email the CA key password and the Common Name. These should all relate to your fictional certificate authority.
Optionally you can pass these in at the command line
-subj "/C=CA/ST=Ontario/L=Toronto/O=CertAuth Co/CN=someCA.com"
openssl req -key ca.key -new -out ca.csr
Sign the certificate request (csr) with the CAs own private key (self-signed certificate)
openssl x509 -signkey ca.key -in ca.csr -req -days 9999 \ -out ca.crt
You can skip the CSR step entirely if you simply want to create a self-signed certificate
openssl req -key ca.key -new -x509 -days 9999 -out ca.crt
Now create the server certificate.
Generate the server private key. You will be prompted for a new password.
openssl genrsa -des3 -out server.key 2048
(Optional) To avoid supplying a password when your listener process starts or supplying a password in application configuration, you can simply remove the password from the private key file (decrypts the private key file).
openssl rsa -in server.key -out nopass-server.key
Of course you can always just generate the private key and leave it unencrypted.
openssl genrsa -out server.key 2048
Create a certificate request. You will be prompted for address, email the server key password and the Common Name. The CN should be the FQDN of the server.
Optionally you can pass these in at the command line
-subj "/C=CA/ST=Ontario/L=Toronto/O=My Company/CN=myco.com"
openssl req -key server.key -new -out server.csr
Verify the signature
openssl req -in server.csr -noout -verify -key server.key
openssl req -in server.csr -noout -text
Now have your fictitious CA sign the server certificate request and issue a server certificate. You will need the CA private key password.
openssl x509 -CA ca.crt -CAkey ca.key -CAcreateserial \ -in server.csr -req -days 9999 -out server.crt
If all you really want is a self-signed certificate for your server without dragging in a certificate authority (CA) or even worry about the added complication of an encrypted private key (which requires the passphrase to be maintained somewhere), then the following skips all the above steps
openssl req -x509 -nodes -days 9999 -sha256 \ -subj ‘/C=US/ST=NY/L=NYC/CN=www.myserver.com’ \ -newkey rsa:2048 -keyout server.key -out server.crt
keyout and out may refer to the same file – this will result in the certificate and private key being contained within a single PEM formatted file.
Not that this private key is not encrypted and protected by a passphrase (nodes)
Client / User Certificate
The final step is to create the client certificate. This is necessary when configuring SSL for mutual authentication.
It starts with generating a private key for the client. You will be asked to provide a client key password (omit -des3 to leave unencrypted; no passphrase required).
openssl genrsa -des3 -out user.key 2048
Next is to create a certificate request for the client certificate.
You will be prompted for address, email the server key password and the Common Name. The CN should identify the client. Either a person’s legal name, or a machines domain name. Either way the server accepts and trusts whatever the CN is because it trusts the CA that signed the user certificate.
openssl req -new -key user.key -out user.csr
You can optionally combine the private key creation (without passphrase) and certificate signing request:
openssl req -new -newkey rsa:2048 -nodes \ -subj ‘/C=US/ST=NY/L=NYC/CN=some user identity’ \ -keyout user.key -out user.csr
The CA takes the client’s certificate request and signs it to generate a certificate.
openssl x509 -CA ca.crt -CAkey ca.key -CAcreateserial \ -in user.csr -req -days 9999 -out user.crt
Combine the private key and certificate into a single P12 formatted file.
You will be asked for the client’s private key password. You will also be asked for an export password.
openssl pkcs12 -export -clcerts -in user.crt \ -inkey user.key -out user.p12
PKCS#12 (P12) files define a file format for storing multiple cryptographic objects as a single file. The keystore should contain both a private and public certificate granted by the CA along with intermediate CA certificates used for signing.
Your P12 file can contain a maximum of 10 intermediate certificates.
Start a web server listening to SSL using your server certificate on port 4433. If the key is encrypted you will be prompted for the passphrase.
openssl s_server -cert server.crt -key server.key -www \ -accept 4433
With the server running, attempt to connect to it.
openssl s_client -connect localhost:4433
For mutual SSL authentication to work, the client certificate offered MUST be signed by any of the root or intermediary CAs.
openssl s_client -connect host:port -key user.key \ -cert user.crt -showcerts
0 s:/C=US/ST=New York/L=Armonk/O=International Business Machines Corporation/CN=*.mybluemix.net
i:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
1 s:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
subject=/C=US/ST=New York/L=Armonk/O=International Business Machines Corporation/CN=*.mybluemix.net
issuer=/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
Ensure that the server certificate can be verified by the CA certificate. Note that clients will additionally check that the FQDN in the server certificate matches the origin. This openssl check does not do that.
openssl s_client -connect host:port -CAfile ca.crt
Finally make a full request. For now let’s say that the server is installed locally on port 8443. A request to https://localhost:8443 will find the service, but unfortunately the server certificate refers to a domain other than localhost. A curl request will be rejected.
You can either disable the certificate check entirely, or you can pretend that the service is actually hosted on your production domain.
In curl to disable the certificate check include –insecure
The –cacert refers to the certificate authority certificates to use to verify the server certificate returned in the response.
curl --insecure --cacert ca.crt --request GET \ https://localhost:8443/orders
For example, myapp.mybluemix.net is your production domain, but you wish to test locally.
Add the following entry to your /etc/hosts file:
Now you can make the request without disabling the certificate check:
curl --cacert ca.crt --request GET \ https://myapp.mybluemix.net:8443/orders
|ca.key||Certificate authority private key (PEM format)|
|ca.csr||Certificate request for the authority (PKCS10 format)|
|ca.crt||Self signed CA Certificate (PEM format)|
|server.key||Server private key (PEM format)|
|server.csr||Certificate request for the server (PKCS10 format)|
|server.crt||Server certificate signed by the CA (PEM format)|
|user.key||Client/user private key (PEM format)|
|user.csr||Certificate request for the user/client (PKCS10 format)|
|user.crt||Client/user certificate signed by the CA (PEM format)|
|user.p12||Client/user certificate and private key (PKCS#12 (P12) format)|