OpenSSL Certificates

[Ref: OpenBSD 5.2, madboa's Command-Line HOWTO ]

OpenBSD (up to 5.5) ships with OpenSSL in the base.

Warning: Remember I know less than you about this stuff, these are notations of something that worked. It doesn't mean it works well. If you really want to know more about the tools, refer to the OpenSSL website, and madboa's Command-Line HOWTO

The OpenBSD base install defaults to storing OpenSSL configuration files and keys/certificates in the directory /etc/ssl.

Verify the path for OpenSSL files using

$ sudo openssl version -d
OPENSSLDIR: "/etc/ssl"

If the path for trusted certs does not exist, create it:

$ sudo mkdir -p /etc/ssl/certs

Certificate Authority (CA)

Normally, we use a 3rd party to provide some level of authentication/trust between your hosts and external users. In many cases, your external users are internal users and what you want is to provide the encryption of data transport, without the costs involved with using the above mentioned 3rd party.

What we want is:

  • our own Certificate Authority (CA) so that we can
  • sign multiple certificates.

Generate the CA Key

The long-form command-line req creates both a certificate and key:

$ sudo openssl req -days 365 -new -newkey rsa:4096 -sha1 -x509 \
    -keyout /path-to/private/ca.key.pem \
    -out    /path-to/certs/ca.crt.pem \
    -config /path-to/openssl.cnf
Generating a 4096 bit RSA private key
.........................................++
.........................++
writing new private key to 'ca.key.pem'
Enter PEM pass phrase: 1234
Verifying - Enter PEM pass phrase: 1234
-----
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:
Email Address []:

Validate the key creation by using OpenSSL.

$ openssl rsa -noout -text -in  /path-to/private/ca.key.pem
$ openssl x509 -noout -text -in /path-to/certs/ca.crt.pem

Put some security on the CA key.

$ sudo chmod 0600 /path-to/private/ca.key.pem

CA Configuration

The defaults for the above prompts, during the key generation, can be specified either in the configuration file (/etc/ssl/openssl.cnf) or on the command-line using the -subj option

$ sudo openssl req -days 365 -new -newkey rsa:4096 -sha1 -x509 \
    -keyout /path-to/private/ca.key.pem \
    -out    /path-to/certs/ca.crt.pem \
    -subj   '/CN=coco.nut.to/O=Coconut Games/C=TO/ST=Tongatapu/L=Nukualofa/emailAddress=samt@coco.nut.to' \
    -config /path-to/openssl.cnf

We could also have generated the CA Key by using OpenSSL's genrsa

$ sudo /usr/sbin/openssl genrsa -des3 -out /path-to/private/ca.key.pem 4096

The generated key acts as our SSL private key to be used as our 'internal' CA (Certificate Authority.)

Refer/Monitor the documentation for the minimum recommended keysize.

-des|-des3|-idea You must specify the method to encrypt the private key or no encryption is used.

New Host

When you have a new host, for which we need to encrypt transit traffic we need to:

  1. Create a Certificate Signing Request
  2. Use the CA Key to Self Sign the key

Signing Request (csr)

A certificate combines data identifying your host, and the authority that accepts authentication of your identity. Create a certificate request (also known as a certificate signing request) to be signed by the certificate authority.

$ sudo /usr/sbin/openssl req -days 365 -nodes -new -newkey rsa:4096 \
    -keyout /path-to/private/server.key.pem \
    -out    /path-to/private/server.csr.pem
Enter pass phrase for server.key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:mx.example.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Note that the -nodes option above creates the key and certificate request without requiring a password. The "password-less" nature of the key is required for Postfix to operate correctly (i.e. it needs to open the key at various stages)

The concept is that you send the above CSR for a trusted third party to sign, and record in their system, so users who recieve your key can validate from the trusted third party that you are who you are. But we don't want no third party saying who we are (for now anyway.)

Verify that the Certificate Signing Request has been created.

$ sudo openssl req -noout -text -in /path-to/private/server.csr.pem
$ sudo openssl rsa -noout -text -in /path-to/private/server.key.pem

Self Signed certificate (X509 structure.)

[Ref: x509(1)]

$ sudo /usr/sbin/openssl x509 -req -days 365 \
    -in      /path-to/private/server.csr.pem \
    -CA      /path-to/certs/ca.crt.pem \
    -CAkey   /path-to/private/ca.key.pem \
    -CAcreateserial \
    -out    /path-to/certs/server.crt.pem
Signature ok
subject=/C=TO/ST=Nukualofa/L=Tongatapu/O=Coconut Farmers/OU=IT Department/CN=mx.coco.nut.to/emailAddress=spam@coco.nut.to
Getting Private key
Enter pass phrase for /path-to/private/ca.key.pem:

Verify

You can verify from you have created valid openssl keys through the following commands

$ sudo openssl rsa -noout -text -in /path-to/private/ca.key.pem
$ sudo openssl req -noout -text -in /path-to/private/server.csr.pem
$ sudo openssl x509 -noout -text -in /path-to/certs/server.crt.pem

To verify that the key is paired with the certificate you just created, compare modulus (they should be the same)

$ sudo openssl rsa -noout -modulus -in /path-to/private/server.key.pem | openssl md5
$ sudo openssl x509 -noout -modulus -in /path-to/certs/server.crt.pem | openssl md5

According to notes out there (?) The modulus and "public exponent" need to be the same on the certificate and key file. If you've created the KEY and self-signed the certificate correctly, then the above commands will return the same value.

Subject Alternative Names

[Ref: x509v3_config(5), Creating an SSL Certificate with Multiple Hostnames, Add an Subject Alternative Name to SSL certificate, SSL Certificate request with subject alternative names(SAN) ]

In practise, we often have to use SSL certificates on a single host but we need to be using different domain names (for example my host email or web pages for multiple family domains such as http://samt.nomoa.com and http://john.nomoa.com etc.)

e.g. My Web Host will serve some subdomains, as well as a few domains from a friend

Host: my.nomoa.com

  • subdomain: www
  • subdomain: ofa
  • friends domain: www.example.com
  • friends domain: www.example.net

The simplest way forward is to specify your alternative domain names within a configuration file, because this information will be required twice:-

  1. When generating certificate signing request
  2. When self-signing the above request

File: /etc/ssl/example.cnf

[ req ]
...
req_extensions      = req_ext
x509_extensions     = v3_ca

...
[req_ext]
subjectAltName      = @alt_names

[ alt_names ]
DNS.1               = www.nomoa.com
DNS.2               = ofa.nomoa.com
DNS.3               = www.example.com
DNS.4               = www.example.net

[v3_ca]
basicConstraints    = CA:FALSE
keyUsage            = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage    = serverAuth

@alt_names uses the @section syntax where we can use the OID.nnn=value pair to simplify creating multiple domain names instead of using the comma separated solutions such as:

subjectAltName = www.nomoa.com, ofa.nomoa.com, www.example.com, www.example.net

Choose your preferred mechanism, and below is a poor interpretation of the documentation for the short and long form configuration format.

[Ref: Multi-valued extensions]

Multi-valued extensions have a short form and a long form. The short form is a list of names and values separated by a comman ",":

For example:

   basicConstraints=critical,CA:true,pathlen:1

The long form allows the values to be placed in a separate section, and must be used when the field value includes a comma "," which would be misinterpreted as a field separator. This is done by using the @section syntax instead of a literal field value:

For example:

   basicConstraints=critical,@bs_section

   [bs_section]
   CA=true
   pathlen=1

Both forms are equivalent.

For example:

    subjectAltName=URI:ldap://somehost.com/CN=foo,OU=bar

will produce an error but the equivalent form:

   subjectAltName=@subject_alt_section

   [subject_alt_section]
   subjectAltName=URI:ldap://somehost.com/CN=foo,OU=bar

is valid.

...

The same field name can only occur once in a section. This is worked around using the form:

   OID.nnn=value

For Example:

   subjectAltName=@alt_section

   [alt_section]
   email.1=samt@example.com
   email.2=samt@example.net

Signing Request

Now, the signing request we're going to make below presumes we haven't created the server private key:

sudo /usr/sbin/openssl req -days 365 -nodes -new -newkey rsa:4096 \
    -config /path-to/example.cnf
    -keyout /path-to/private/server.key.pem \
    -out    /path-to/private/server.csr.pem

You may already have an existing key that you need to use:

sudo /usr/sbin/openssl req -days 365 -nodes -new \
    -config /path-to/example.cnf
    -key    /path-to/private/server.key.pem \
    -out    /path-to/private/server.csr.pem

The key difference from the standard certificate request, is now we have explicit request for X509v3 Subject Alternative Name:

$ sudo openssl req -noout -text -in /path-to/private/server.csr.pem
Certificate Request:
   Data:
      ...
      Attributes:
      Requested Extensions:
         X509v3 Subject Alternative Name:
            DNS:www.nomoa.com, DNS:ofa.nomoa.com, DNS:www.example.com, DNS:www.example.net
   Signature Algorithm: sha1WithRSAEncryption
      ...     

Self Signed

$ sudo /usr/sbin/openssl x509 -req -days 365 \
    -in      /path-to/private/server.csr.pem \
    -extfile /path-to/example.cnf -extensions req_ext \
    -CA      /path-to/certs/ca.crt.pem \
    -CAkey   /path-to/private/ca.key.pem \
    -CAcreateserial \
    -out    /path-to/certs/server.crt.pem

Verify the contents of your certificates with:

$ sudo openssl x509 -noout -text -in /path-to/certs/server.crt.pem

Which should now successfully output the Subject Alternative names as we've requested in our above Certificate Request:

Certificate:
   Data:
      ...
      X509v3 extensions:
         X509v3 Subject Alternative Name:
            DNS:www.nomoa.com, DNS:ofa.nomoa.com, DNS:www.example.com, DNS:www.example.net
   Signature Algorithm: sha1WithRSAEncryption
      ...

Combined Certificates

Ref: Postfix HOWTO TLS/SSL

To enable a remote SMTP client to verify the Postfix SMTP server certificate,
the issuing CA certificates must be made available to the client.

A simple way of providing the CA certificates, is to bundle it together with your server certificate (such as in the sample below: bundle = server cert + ca cert) where the order of inclusion is significant.

# cat /path-to/certs/server.crt.pem /path-to/certs/ca.crt.pem > /path-to/server.pem

After which you should test the resulting PEM file to ensure that it will work for us, using 'openssl verify'

# openssl verify -purpose sslserver /path-to/server.pem
server.pem: C=TO,ST=Tongatapu,L=Nukualofa,O=Coconut Games, CN=coco.nut.to, emailAddress=samt@coco.nut.to
error 18 at 0 depth lookup:self signed certificate

The test failed!! Kind-of?

From the manpage

The first line contains the name of the certificate being verified followed 
by the subject name of the certificate. The second line contains the error 
number and the depth. The depth is number of the certificate being verified 
when a problem was detected starting with zero for the certificate being verified 
itself then 1 for the CA that signed the certificate and so on. Finally a text 
version of the error number is presented. 

18 X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: self signed certificate

    the passed certificate is self signed and the same certificate cannot be found in the list of trusted certificates.

Review madboa's Command-Line HOWTO to remove the error message, which is stripped from the sample code at the above page as below root level command-lines.

# cd /path-to/certs
# HASH=$(openssl x509 -noout -hash -in server.crt.pem)
# echo $HASH
# ln -s server.crt.pem ${HASH}.0

If we've understood madboa's notes, and you've understood our mis-interpretation, then you should get the following response:

# openssl verify -purpose sslserver /path-to/server.pem
server.pem: OK

Wooo hoo, It works.

Diffie Helman

$ sudo openssl dhparam -out /path-to/dh_512.pem  -2 512
$ sudo openssl dhparam -out /path-to/dh_1024.pem -2 1024

Converting between formats.

[Ref: IE9 Help - Certificate File Formats, The Most Common OpenSSL Commands ]

Because we can't agree on the "one-size-fits-all" file format for the SSL certificates, different applications/services use different formats for certificates. Thus, we need a brief overview of these formats, and hints for converting files between the different formats.

Personal Information Exchange (PKCS #12)

The Personal Information Exchange format (PFX, also called PKCS #12) supports secure storage of certificates, private keys, and all certificates in a certification path.

The PKCS #12 format is the only file format that can be used to export a certificate and its private key.

Cryptographic Message Syntax Standard (PKCS #7)

The PKCS #7 format supports storage of certificates and all certificates in the certification path.

DER-encoded binary X.509

The Distinguished Encoding Rules (DER) format supports storage of a single certificate. This format does not support storage of the private key or certification path.

Base64-encoded X.509

The Base64 format supports storage of a single certificate. This format does not support storage of the private key or certification path.

Converting Certificates

Conventions exist for the extensions, such as:-

  • DER (.crt, .cer, .der)
  • PKCS#12 (.pfx, .p12)

Where PEM is used as container file containing potentially multiple certificates, certificate chains, and keys. Certificate file types | pem(3)

From / To Command
DER to PEM
openssl x509 -in input.crt -inform DER -out output.crt -outform PEM
PEM to PKCS#12
openssl pkcs12 -export -in input.pem -inkey key.pem -out output.p12
PEM to PKCS#12 May contain a private key.
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key \
-in certificate.crt -certfile CACert.crt
PKCS#12 to PEM
openssl pkcs12 -in input.p12 -out output.pem -nodes -clcerts
PKCS#12 to PEM pkcs12(1)

The pkcs12 command allows PKCS#12 files (sometimes referred to as PFX files)
to be created and parsed. PKCS#12 files are used by several programs including
Netscape, MSIE and MS Outlook.

May contain a private key and certificates
openssl pkcs12 -in keyStore.pfx -out keyStore.pem -nodes
We want to explictly extract the key and certificate into separate files.
openssl pkcs12 -in keyStore.pfx -out keyStore.key -nocerts -nodes
openssl pkcs12 -in keyStore.pfx -out keyStore.crt -nokeys -nodes

Converting KEYS

[Ref: rsa(1)]

The rsa command processes RSA keys. They can be converted between various forms and their components printed out. Note this command uses the traditional SSLeay compatible format for private key encryption: newer applications should use the more secure PKCS#8 format using the pkcs8(1) utility.

From / To Command
DER to PEM
openssl rsa -in input.key -inform DER -out output.key -outform PEM
NET to PEM
openssl rsa -in input.key -inform NET -out output.key -outform PEM

Other