PKI


Some of my (and others) notes of managing PKI with the excellent openssl. Its simple and just works. To get going will create a root CA (Certificate Authority) and an intermediate signing CA. Using the CA’s will issue three keypairs; one for email protection, one for TLS, and one for digital signatures. The digital signature keypair will be presented in the form of a CSR (Certificate Signing Request), as if generated by a third party that would like a keypair, signed by our CA hierarchy.

Content

Create the Root CA

First setup a neat directory tree, to keep things clear.

mkdir -p ca/root-ca/private ca/root-ca/db crl certs
chmod 700 ca/root-ca/private

Create the various CA database files, which must exist prior to using the openssl ca command. These are all referenced in the configuration file passed in to openssl, which we’ll see shortly.

touch ca/root-ca/db/root-ca.db ca/root-ca/db/root-ca.db.attr
echo 01 > ca/root-ca/db/root-ca.crt.srl
echo 01 > ca/root-ca/db/root-ca.crl.srl

After this setup, should have a similar directory tree to this:

.
├── ca
│   └── root-ca
│       ├── db
│       │   ├── root-ca.db
│       │   └── root-ca.db.attr
│       └── private
├── certs
├── crl
├── email.conf
├── root-ca.conf
├── server.conf
└── signing-ca.conf

The Request (CSR)

Start by producing a new keypair, in the form of a Certificate Signing Request (CSR). A CSR, breaks out the private and public keys, so you can provide just the public key portion to another trusted party for signing. Once signed, and now part of a trust chain, you can combine the two pieces together again.

openssl req -new -config root-ca.conf -out ca/root-ca.csr -keyout ca/root-ca/private/root-ca.key

Refer to root-ca.conf below.

The Certificate

Using the ca command, can now issue a root CA based on the CSR. The root certificate is self signed, and is the base of all trust relationships in the PKI.

openssl ca -selfsign -config root-ca.conf -in ca/root-ca.csr -out ca/root-ca.crt -extensions root_ca_ext

Create the Intermediate CA

A very similar procedure for the root CA. Some setup:

mkdir -p ca/signing-ca/private ca/signing-ca/db crl certs
chmod 700 ca/signing-ca/private

And the the files (databases) themselves:

touch ca/signing-ca/db/signing-ca.db ca/signing-ca/db/signing-ca.db.attr
echo 01 > ca/signing-ca/db/signing-ca.crt.srl
echo 01 > ca/signing-ca/db/signing-ca.crl.srl

The Request (CSR)

As for the root CA, spawn a new keypair in the form of a CSR:

openssl req -new -config signing-ca.conf -out ca/signing-ca.csr -keyout ca/signing-ca/private/signing-ca.key

Refer to signing-ca.conf below.

The Certificate

Again use the ca command to issue a certificate based on a CSR.

openssl ca -config root-ca.conf -in ca/signing-ca.csr -out ca/signing-ca.crt -extensions signing_ca_ext

Notes:

  • Configure against the root CA root-ca.conf, which is signing the intermediate signing CA CSR.
  • The signing_ca_ext extension is used, as opposed to the root_ca_ext used earlier, and is defined as a section in root-ca.conf (see below)

signing_ca_ext:

[ signing_ca_ext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

Create Working Certificates

Email Protection Certificate

The Request (CSR)

The req -new command to create a new keypair (in the form of a CSR).

openssl req -new -config email.conf -out certs/ben-email.csr -keyout certs/ben-email.key

Generating a 2048 bit RSA private key
..+++
.............+++
writing new private key to 'certs/ben-email.key'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
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.
-----
1. Domain Component (DC=net)       []:DC=net
2. Domain Component (DC=bencode)   []:DC=bencode
3. Domain Component (DC=pki)       []:DC=pki
4. Organization Name (O=bEncode Labs)   []:O=bEncode Labs
5. Organizational Unit Name (OU=section)   []:
6. Common Name (CN=Benjamin Simmonds) []:CN=Benjamin Simmonds
7. Email Address (emailAddress=ben@bencode.net) []:emailAddress=ben@bencode.net

Refer to email.conf below.

The Certificate

Here’s where it all comes together. Given the email CSR from above, will now attempt sign it using the Intermediate Signing CA that was setup earlier.

openssl ca -config signing-ca.conf -in certs/ben-email.csr -out certs/ben-email.crt -extensions email_ext

Note that the signing CA config signing-ca.conf is used.

The extension email_ext is defined as a section in signing-ca.conf as follows, which stamps the certificate with certain usage properties relating to email validation and signing.

[ email_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = emailProtection,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

TLS Server Certificate

The Request (CSR)

Almost identical, but with some different usage properties. Note you can read environment variables within the OpenSSL configuration for example $ENV::SAN reads the SAN variable.

SAN=DNS:www.bencode.net
openssl req -new -config tls-server.conf -out certs/bencode.net.csr -keyout certs/bencode.net.key

Refer to tls-server.conf below.

The Certificate

Use the signing CA to issue the TLS server certificate.

openssl ca -config signing-ca.conf -in certs/bencode.net.csr -out certs/bencode.net.crt -extensions server_ext

Output Formats

OpenSSL supports a range of standard formats, including DER and PKCS#7 and PKCS#12 (standards based version of Microsoft’s PFX).

DER (RFC2585) Certificate

openssl x509 -in certs/ben-email.crt -out certs/ben-email.cer -outform der

DER (RFC2585) CRL (Revocation List)

Using the gencrl command to create a CRL, that is all revoked, but not yet expired certificates.

Tip: A new CRL must be generated at regular intervals.

openssl ca -gencrl -config signing-ca.conf -out crl/signing-ca.crl
cat crl/signing-ca.crl 
-----BEGIN X509 CRL-----
MIICBDCB7QIBATANBgkqhkiG9w0BAQUFADCBiTETMBEGCgmSJomT8ixkARkWA25l
dDEXMBUGCgmSJomT8ixkARkWB2JlbmNvZGUxFTATBgNVBAoMDGJFbmNvZGUgTGFi
...

This can be converted to DER by:

openssl crl -in crl/signing-ca.crl -out crl/signing-ca.crl -outform der
cat crl/signing-ca.crl 
0��10H��
...

PKCS#12 (p12) Bundle

A standards, cleaned up version of Microsofts PFX format. Typically used for bundling a certificate and its private key. Additional certificates (e.g. those needed to build the trust chain up to the Root CA) can be included. For example:

cat ca/signing-ca.crt ca/root-ca.crt certs/ben-email.crt > certs/ben-email-with-chain.pem
openssl pkcs12 -export -name "Ben Simmonds" -inkey certs/ben-email.key -in certs/ben-email-with-chain.pem -out certs/ben-email.p12
Enter pass phrase for certs/ben-email.key:
Enter Export Password:
Verifying - Enter Export Password:

PEM Bundle

Super simple. Involves concatenating other PEM formatted files together. This could be the trust chain certificates, or the private key and its certificate, or the private key its certificate and the trust chain certificates, or any other combination.

The trust chain of CA certificates:

cat ca/signing-ca.crt ca/root-ca.crt > ca/signing-ca-chain.pem

The private key and certificate:

cat certs/ben-email.key certs/ben-email.crt > certs/ben-email.pem

Viewing Artifacts

CSR (Signing Request)

openssl req -in certs/ben-email.csr -noout -text

DER Certificate

openssl x509 -in certs/ben-email.cer -noout -text -inform der

CRL (Revocation List)

openssl crl -in crl/signing-ca.crl -inform der -noout -text

PKCS#12 Bundle

openssl pkcs12 -in certs/ben-email.p12 -nodes -info

Enter Import Password:
MAC:sha1 Iteration 2048
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
Bag Attributes
    localKeyID: 4E CB 73 0B 90 E7 A4 6E 15 27 BD 3B B7 03 33 1A 8A C9 CF 88 
    friendlyName: Ben Simmonds
subject=/DC=net/DC=bencode/O=bEncode Labs/CN=Benjamin Simmonds
issuer=/DC=net/DC=bencode/O=bEncode Labs/OU=bEncode Labs Signing CA/CN=bEncode Labs Signing CA
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBATANBgkqhkiG9w0BAQUFADCBiTETMBEGCgmSJomT8ixk

Configuration Files

root-ca.conf

# Root CA OpenSSL Configuration
# Original source : https://pki-tutorial.readthedocs.io/en/latest/simple/root-ca.conf.html

# The [default] section contains global constants that can be referred to from
# the entire configuration file.

[ default ]
ca = root-ca  # CA name
dir = .  # Top dir

# The next part of the configuration file is used by the openssl req command.
# It defines the CA's key pair, its DN, and the desired extensions for the CA
# certificate.

[ req ]
default_bits = 2048  # RSA key size
encrypt_key = yes  # Protect private key
default_md = sha1  # MD to use
utf8 = yes  # Input is UTF-8
string_mask = utf8only  # Emit UTF-8 strings
prompt = no  # Don't prompt for DN
distinguished_name = ca_dn  # DN section
req_extensions = ca_reqext  # Desired extensions

[ ca_dn ]
0.domainComponent = "net"
1.domainComponent = "bencode"
organizationName = "bEncode Labs"
organizationalUnitName = "bEncode Labs Root CA"
commonName = "bEncode Labs Root CA"

[ ca_reqext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash

# The remainder of the configuration file is used by the openssl ca command.
# The CA section defines the locations of CA assets, as well as the policies
# applying to the CA.

[ ca ]
default_ca = root_ca  # The default CA section

[ root_ca ]
certificate = $dir/ca/$ca.crt  # The CA cert
private_key = $dir/ca/$ca/private/$ca.key  # CA private key
new_certs_dir = $dir/ca/$ca  # Certificate archive
serial = $dir/ca/$ca/db/$ca.crt.srl  # Serial number file
crlnumber = $dir/ca/$ca/db/$ca.crl.srl  # CRL number file
database = $dir/ca/$ca/db/$ca.db  # Index file
unique_subject = no  # Require unique subject
default_days = 3652  # How long to certify for
default_md = sha1  # MD to use
policy = match_pol  # Default naming policy
email_in_dn = no  # Add email to cert DN
preserve = no  # Keep passed DN ordering
name_opt = ca_default  # Subject DN display options
cert_opt = ca_default  # Certificate display options
copy_extensions = none  # Copy extensions from CSR
x509_extensions = signing_ca_ext  # Default cert extensions
default_crl_days = 365  # How long before next CRL
crl_extensions = crl_ext  # CRL extensions

# Naming policies control which parts of a DN end up in the certificate and
# under what circumstances certification should be denied.

[ match_pol ]
domainComponent = match  # Must match 'bencode.net'
organizationName = match  # Must match 'bEncode Labs'
organizationalUnitName = optional  # Included if present
commonName = supplied  # Must be present

[ any_pol ]
domainComponent = optional
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = optional
emailAddress = optional

# Certificate extensions define types of certificates the CA can create

[ root_ca_ext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

[ signing_ca_ext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

# CRL extensions point to the CA certificate that has issued the CRL

[ crl_ext ]
authorityKeyIdentifier = keyid:always

signing-ca.conf

# Intermediate Signing CA OpenSSL Configuration
# Original source: https://pki-tutorial.readthedocs.io/en/latest/simple/signing-ca.conf.html

# The [default] section contains global constants that can be referred to from
# the entire configuration file. It may also hold settings pertaining to more
# than one openssl command.

[ default ]
ca = signing-ca  # CA name
dir = .  # Top dir

# The next part of the configuration file is used by the openssl req command.
# It defines the CA's key pair, its DN, and the desired extensions for the CA
# certificate.

[ req ]
default_bits = 2048  # RSA key size
encrypt_key = yes  # Protect private key
default_md = sha1  # MD to use
utf8 = yes  # Input is UTF-8
string_mask = utf8only  # Emit UTF-8 strings
prompt = no  # Don't prompt for DN
distinguished_name = ca_dn  # DN section
req_extensions = ca_reqext  # Desired extensions

[ ca_dn ]
0.domainComponent = "net"
1.domainComponent = "bencode"
organizationName = "bEncode Labs"
organizationalUnitName = "bEncode Labs Signing CA"
commonName = "bEncode Labs Signing CA"

[ ca_reqext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash

# The remainder of the configuration file is used by the openssl ca command.
# The CA section defines the locations of CA assets, as well as the policies
# applying to the CA.

[ ca ]
default_ca = signing_ca  # The default CA section

[ signing_ca ]
certificate = $dir/ca/$ca.crt  # The CA cert
private_key = $dir/ca/$ca/private/$ca.key  # CA private key
new_certs_dir = $dir/ca/$ca  # Certificate archive
serial = $dir/ca/$ca/db/$ca.crt.srl  # Serial number file
crlnumber = $dir/ca/$ca/db/$ca.crl.srl  # CRL number file
database = $dir/ca/$ca/db/$ca.db  # Index file
unique_subject = no  # Require unique subject
default_days = 730  # How long to certify for
default_md = sha1  # MD to use
policy = match_pol  # Default naming policy
email_in_dn = no  # Add email to cert DN
preserve = no  # Keep passed DN ordering
name_opt = ca_default  # Subject DN display options
cert_opt = ca_default  # Certificate display options
copy_extensions = copy  # Copy extensions from CSR
x509_extensions = email_ext  # Default cert extensions
default_crl_days = 7  # How long before next CRL
crl_extensions = crl_ext  # CRL extensions

# Naming policies control which parts of a DN end up in the certificate and
# under what circumstances certification should be denied.

[ match_pol ]
domainComponent = match  # Must match 'bencode.net'
organizationName = match  # Must match 'bEncode Labs'
organizationalUnitName = optional  # Included if present
commonName = supplied  # Must be present

[ any_pol ]
domainComponent = optional
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = optional
emailAddress = optional

# Certificate extensions define types of certificates the CA can create

[ email_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = emailProtection,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

[ server_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

# CRL extensions point to the CA certificate that has issued the CRL

[ crl_ext ]
authorityKeyIdentifier = keyid:always

email.conf

# Email Protection Certificate OpenSSL Configuration

[ req ]
default_bits = 2048  # RSA key size
encrypt_key = yes  # Protect private key
default_md = sha1  # MD to use
utf8 = yes  # Input is UTF-8
string_mask = utf8only  # Emit UTF-8 strings
prompt = no  # Prompt for DN
distinguished_name = naming_section  # DN template
req_extensions = extensions_section  # Desired extensions

[ naming_section ]
0.domainComponent = "net"
1.domainComponent = "bencode"
organizationName = "bEncode Labs"
commonName = "Benjamin Simmonds"
emailAddress = "ben@bencode.net"

[ extensions_section ]
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = emailProtection,clientAuth
subjectKeyIdentifier = hash
subjectAltName = email:move

tls-server.conf

# TLS server certificate request OpenSSL Configuration

# This file is used by the openssl req command. The subjectAltName cannot be
# prompted for and must be specified in the SAN environment variable.

[ default ]
SAN = DNS:www.bencode.net  # Default value

[ req ]
default_bits = 2048  # RSA key size
encrypt_key = no  # Protect private key
default_md = sha1  # MD to use
utf8 = yes  # Input is UTF-8
string_mask = utf8only  # Emit UTF-8 strings
prompt = yes  # Prompt for DN
distinguished_name = server_dn  # DN template
req_extensions = server_reqext  # Desired extensions

[ server_dn ]
0.domainComponent = "net"
1.domainComponent = "bencode"
organizationName = "bEncode Labs"
commonName = "www.bencode.net"


[ server_reqext ]
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
subjectAltName = $ENV::SAN

Key usage extensions

Key usage extension Description
Digital signature Use when the public key is used with a digital signature mechanism to support security services other than non-repudiation, certificate signing, or CRL signing. A digital signature is often used for entity authentication and data origin authentication with integrity.
Non-repudiation Use when the public key is used to verify digital signatures used to provide a non-repudiation service. Non-repudiation protects against the signing entity falsely denying some action (excluding certificate or CRL signing).
Key encipherment Use when a certificate will be used with a protocol that encrypts keys. An example is S/MIME enveloping, where a fast (symmetric) key is encrypted with the public key from the certificate. SSL protocol also performs key encipherment.
Data encipherment Use when the public key is used for encrypting user data, other than cryptographic keys.
Key agreement Use when the sender and receiver of the public key need to derive the key without using encryption. This key can then can be used to encrypt messages between the sender and receiver. Key agreement is typically used with Diffie-Hellman ciphers.
Certificate signing Use when the subject public key is used to verify a signature on certificates. This extension can be used only in CA certificates.
CRL signing Use when the subject public key is to verify a signature on revocation information, such as a CRL.
Encipher only Use only when key agreement is also enabled. This enables the public key to be used only for enciphering data while performing key agreement.
Decipher only Use only when key agreement is also enabled. This enables the public key to be used only for deciphering data while performing key agreement.

Digital Signatures - how they work

This does have an XML flavour to it.

To validate (i.e. to prove)

  1. First the message can be normalised, and in the case of XML will use something like the “Exclusive XML Canonicalization” (XML-C14N), so we’re comparing apples with apples. This will disgard things like usage of white space.
  2. Using the normalised representation, compute a hash (e.g. SHA1) of the timestamp (contained WS-Security header) and entire message payload (the SOAP body).
  3. Using the public key from the partner organisation certificate, RSA decrypt the hash computed by partner organisation.
  4. If the two hashes are identical, we know the message has not been tampered with.
  5. (optional) Validate the timestampt (TTL) defined by partner organisation (typically 7 minutes).

To sign (i.e. to create)

  1. Wraps the response message in a SOAP envelope, which includes some WS-Security related headers including a timestamp.
  2. The timestamp is set to a configurable number of minutes (e.g. 10 minutes) in the future.
  3. Normalises the message using the “Exclusive XML Canonicalization” (XML-C14N)
  4. Using the noramlised message, computes a (e.g. SHA1) hash of the timestamp (WS-Security header) and entire response message payload (e.g. the SOAP body).
  5. Uses the private key of signing certificate, RSA signs the computed hash, and stores the result in the relevant security header (the SignatureValue header).
  6. The message is then delivered to partner organisation.

References

OpenSSL PKI Tutorial

IBM Knowledge Center: Key usage extensions and extended key usage