Install SSL TLS in your development machine

This one took me a lot of time to get my head over!

Introduction

Openssl is a TLS/SSL and crypto library https://www.openssl.org: it has many commands and lot of configuration options.
openssl is a command line tool that can be used for:

  • Creation of key parameters
  • Creation of X.509 certificates, CSRs and CRLs
  • Calculation of message digests
  • Encryption and decryption
  • SSL/TLS client and server tests
  • Handling of S/MIME signed or encrypted mail
  • And more…

Terminology

CA = Certificate Authority: entity that issues digital certificates

CSR = Certificate Signing Request

PKI = Public Key Infrastructure

PEM = Privacy Enhanced Mail

X509 File Extensions

The first thing we have to understand is what each type of file extension is.   There is a lot of confusion about what DER, PEM, CRT, and CER are and many have incorrectly said that they are all interchangeable.  While in certain cases some can be interchanged the best practice is to identify how your certificate is encoded and then label it correctly.  Correctly labeled certificates will be much easier to manipulat

Encodings (also used as extensions)

  • .DER = The DER extension is used for binary DER encoded certificates. These files may also bear the CER or the CRT extension.   Proper English usage would be “I have a DER encoded certificate” not “I have a DER certificate”.
  • .PEM = The PEM extension is used for different types of X.509v3 files which contain ASCII (Base64) armored data prefixed with a “—– BEGIN …” line.

Common Extensions

  • .CRT = The CRT extension is used for certificates. The certificates may be encoded as binary DER or as ASCII PEM. The CER and CRT extensions are nearly synonymous.  Most common among *nix systems
  • CER = alternate form of .crt (Microsoft Convention) You can use MS to convert .crt to .cer (.both DER encoded .cer, or base64[PEM] encoded .cer)  The .cer file extension is also recognized by IE as a command to run a MS cryptoAPI command (specifically rundll32.exe cryptext.dll,CryptExtOpenCER) which displays a dialogue for importing and/or viewing certificate contents.
  • .KEY = The KEY extension is used both for public and private PKCS#8 keys. The keys may be encoded as binary DER or as ASCII PEM.

The only time CRT and CER can safely be interchanged is when the encoding type can be identical.  (ie  PEM encoded CRT = PEM encoded CER)

First lets set up your CA

Create a folder to hold our config and keys: sudo mkdir /etc/apache2/ssl && cd /etc/apache2/ssl

Create a basic configuration file: touch openssl-ca.cnf

Put this inside sudo nano openssl-ca.cnf:

# openssl-ca.cnf

HOME = .
RANDFILE = $ENV::HOME/.rnd

####################################################################
[ ca ]
default_ca = CA_default # The default ca section

[ CA_default ]

default_days = 1000 # how long to certify for
default_crl_days = 30 # how long before next CRL
default_md = sha256 # use public key default MD
preserve = no # keep passed DN ordering

x509_extensions = ca_extensions # The extensions to add to the cert

email_in_dn = no # Don't concat the email in the DN
copy_extensions = copy # Required to copy SANs from CSR to cert

####################################################################
[ req ]
default_bits = 4096
default_keyfile = cakey.pem
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
string_mask = utf8only

####################################################################
[ ca_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = MA

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default =Drâa‐Tafilalet

localityName = Locality Name (eg, city)
localityName_default = Ouarzazate

organizationName = Organization Name (eg, company)
organizationName_default = Test CA, Limited

organizationalUnitName = Organizational Unit (eg, division)
organizationalUnitName_default = Server Research Department

commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Test CA

emailAddress = Email Address
emailAddress_default = test@mydomain.dev

####################################################################
[ ca_extensions ]

subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true
keyUsage = keyCertSign, cRLSign

Then, execute the following. The -nodes omits the password or passphrase so you can examine the certificate.

$ openssl req -x509 -config openssl-ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM

After the command executes, cacert.pem will be your certificate for CA operations, and cakey.pem will be the private key. Recall the private key does not have a password or passphrase.

You can dump the certificate with the following:

$ openssl x509 -in cacert.pem -text -noout

And test its purpose with the following:

$ openssl x509 -purpose -in cacert.pem -inform PEM

Second: lets sign an end entity certificate (a.k.a server or user)

For part two, I’m going to create another conf file that’s easily digestible.

First, touch the openssl-server.cnf (you can make one of these for user certificates also).

$ touch openssl-server.cnf

Then open it and add the following.

#openssl-server.cnf
HOME = .
RANDFILE = $ENV::HOME/.rnd

####################################################################
[ req ]
default_bits = 2048
default_keyfile = serverkey.pem
distinguished_name = server_distinguished_name
req_extensions = server_req_extensions
string_mask = utf8only

####################################################################
[ server_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = MD

localityName = Locality Name (eg, city)
localityName_default = Baltimore

organizationName = Organization Name (eg, company)
organizationName_default = Test CA, Limited

commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Test CA

emailAddress = Email Address
emailAddress_default = test@mydomain.dev

####################################################################
[ server_req_extensions ]

subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated Certificate"

####################################################################
[ alternate_names ]

DNS.1 = mydomain.dev
DNS.2 = www.mydomain.dev
DNS.3 = mail.mydomain.dev
DNS.4 = ftp.mydomain.dev

# IPv4 localhost
IP.1 = 127.0.0.1

# IPv6 localhost
IP.2 = ::1

Then, create the server certificate request.

Be sure to omit -x509*. Adding -x509 will create a certifcate, and not a request.
$ openssl req -config openssl-server.cnf -newkey rsa:2048 -sha256 -nodes -out servercert.csr -outform PEM

After this command executes, you will have a request in servercert.csr and a private key in serverkey.pem.

And you can inspect it again.

$ openssl req -text -noout -verify -in servercert.csr

Next, you have to sign it with your CA.

You are almost ready to sign the server’s certificate by your CA. The CA’s openssl-ca.cnf  needs two more sections before issuing the command.

First, open openssl-ca.cnf and add the following two sections:

####################################################################
[ signing_policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

####################################################################
[ signing_req ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment

Second, add the following to the [ CA_default ] section of openssl-ca.cnf. I left them out earlier because they can complicate things (they were unused at the time). Now you will see how they are used, so hopefully they will make sense.

base_dir = .
certificate = $base_dir/cacert.pem # The CA certifcate
private_key = $base_dir/cakey.pem # The CA private key
new_certs_dir = $base_dir # Location for new certs after signing
database = $base_dir/index.txt # Database index file
serial = $base_dir/serial.txt # The current serial number

unique_subject = no # Set to 'no' to allow creation of
# several certificates with same subject.

Third, touch index.txt and serial.txt:

$ touch index.txt
$ echo '01' > serial.txt

Then, perform the following:

$ openssl ca -config openssl-ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles servercert.csr

You should confirm the sign of the certificate.

After the command executes, you will have a freshly minted server certificate in servercert.pem. The private key was created earlier and is available in serverkey.pem .

Finally, you can inspect your freshly minted certificate with the following:
$ openssl x509 -in servercert.pem -text -noout

Add the certification to your virtual host

Edit your virtual host:

sudo nano /etc/apache2/sites-available/mydomain.dev.conf

<VirtualHost *:443>
    ServerName mydomain.dev
    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/servercert.pem
    SSLCertificateKeyFile /etc/apache2/ssl/serverkey.pem
    SSLCertificateChainFile /etc/apache2/ssl/cacert.pem
    DocumentRoot /var/www/mydomain/web
    <Directory /var/www/mydomain/web>
        AllowOverride None
        Order Allow,Deny
        Allow from All
        <IfModule mod_rewrite.c>
            Options -MultiViews
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^(.*)$ app_dev.php [QSA,L]
            RewriteEngine On
            RewriteCond %{HTTP:Authorization} ^(.*)
            RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
        </IfModule>
    </Directory>
    <Directory /var/www/mydomain/>
        Options FollowSymlinks
    </Directory>
    <Directory /var/www/mydomain/web/bundles>
        <IfModule mod_rewrite.c>
            RewriteEngine Off
        </IfModule>
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/mydomain.dev_error.log
    CustomLog ${APACHE_LOG_DIR}/mydomain.dev_access.log combined
</VirtualHost>

Add the certificate to Google Chrome

See this steps: https://css-tricks.com/trusting-ssl-locally-mac/

  1. Open chrome://settings-frame/
  2. Scroll down the page and click on: Show advanced settings…
  3. Scroll down and open: Manage certificates on the HTTPS/SSL section
  4. The Keychain Access screen will be displayed. Chrome uses the Keychain Access utility built into MAC OS manage digital certificate
  5. Under ‘Keychains’ on the left, select ‘Login’ and click ‘My Certificates’ in the ‘Category’ column.

Final notes

Earlier, you added the following to CA_default: copy_extensions = copy. This copies extension provided by the person making the request.

If you omit copy_extensions = copy, then your server certificate will lack the Subject Alternate Names (SANs) like www.mydomain.dev and mail.mydomain.dev.

If you use copy_extensions = copy but don’t look over the request, then the requester might be able to trick you into signing something like a subordinate root (rather than a server or user certificate). Which means he will be able to mint certificates that chain back to your trusted root. Be sure to verify the request with openssl req -verify before signing.

If you omit unique_subject or set it to yes, then you will only be allowed to create one certificate under the subject’s distinguished name.
unique_subject = yes # Set to 'no' to allow creation of
# several ctificates with same subject.

Trying to create a second certificate while experimenting will result in the following when signing your server’s certificate with the CA’s private key:
Sign the certificate? [y/n]:Y
failed to update database
TXT_DB error number 2

So unique_subject = no is perfect for testing.


If you want to ensure the Organizational Name is consistent between self-signed CAs, Subordinate CA and End-Entity certificates, then add the following to your CA configuration files:
[ policy_match ]
organizationName = match

If you want to allow the Organizational Name to change, then use:
[ policy_match ]
organizationName = supplied


There are other rules concerning the handling of DNS names in X.509/PKIX certificates. Refer to these documents for the rules:

RFC 6797 and RFC 7469 are listed because they are more restrictive than the other RFCs and CA/B documents. RFC’s 6797 and 7469 do not allow an IP address, either.

Thanks to Jeff at stackoverflow for his inspiring post