Create a OpenVPN Certificate Authority

Part of configuring OpenVPN involves the creation of a certificate authority (CA), also known as a public key infrastructure (PKI) (the public refers to public-key cryptography). You can not use an existing public key infrastructure; you would allow anyone with a certificate of that PKI to connect to your server (the  and   options can limit the allowed clients). You either need to set up your own certificate infrastructure, or limit OpenVPN to use password-based authentication (see the options,   and  ).

The easiest method it to use easy-rsa, as described in the OpenVPN How-to.

This article describes the steps taken by easy-rsa in more detail, but basically gives the same result. This article is partly based on the excellent tutorial by Phil Dibowitz on creating an CA.

Certificates, when deployed correctly, are much more secure than passwords, since the secret (key) does not need to be exchanged or shared between the different hosts. However, doing so requires a security hygiene which may not be required for a small-scale deployment.

Files
The following files are used by OpenSSL:

I'll use these directories to denote the files that are required on different host:

This is a compromise between the completely flat file structure of easy-rsa, and the recommendations set forth by most CA tutorials, which creates directories for certificate requests, signed certificates , certificate revokations , and private keys.

If you are some paranoid whacko serious about security, you should not run these commands on the same machine, but run commands in the server, client1, and client2 directory of respectively the server and client machines, and run commands in the CA directory on a machine that is not connected to the Internet.

Initialize
(roughly equivalent to )

mkdir ca server client1 client2 cd ca touch index.txt

CA Configuration Files
Create configuration files. In our setup,  contains the configuration for signing certificates. We only use it in conjunction with the  command. It described the folder structure within the ca directory, the location of support files for the CA, as well as properties of the signed certificates (duration, restricted usage) as well as the policy for the name ("distinguished name") of signed certificates. Finally, it lists the policy for certification revocation lists. For this small-scale CA, there is no public URL to download the CRL; I plan to distribute it manually.

[ ca ] default_ca             = CA_default            # The default ca section [ CA_default ] dir                    = /path/to/ca           # Where everything is kept certs                  = $dir/                 # Where the issued certs are kept crl_dir                = $dir/                 # Where the issued crl are kept new_certs_dir          = $dir/                 # default place for new certs private_key            = $dir/ca.key           # The private key certificate            = $dir/ca.crt           # The CA root certificate database               = $dir/index.txt        # List of signed certificates serial                 = $dir/serial           # The current serial number crlnumber              = $dir/crlnumber        # the current crl number crl                    = $dir/crl.pem          # The current CRL RANDFILE               = $dir/.rand            # private random number file unique_subject         = no                    # allow multiple certificates with same subject. default_md             = sha2                  # Use hash algorithm specified in the request default_days           = 730                   # client certificates last 2 years default_crl_days       = 30                    # How often clients should download the CRL x509_extensions        = X509_client           # The x509 extensions for a client certificate name_opt               = ca_default            # Subject Name options cert_opt               = ca_default            # Certificate field options copy_extensions        = copy                  # Accept requested extensions policy                 = policy_dn [ X509_ca ] basicConstraints       = CA:TRUE nsCertType             = sslCA                 # restrict the usage keyUsage               = keyCertSign, cRLSign  # restrict the usage subjectKeyIdentifier   = hash authorityKeyIdentifier = keyid:always,issuer:always [ X509_server ] basicConstraints       = CA:FALSE nsCertType             = server                # restrict the usage keyUsage               = digitalSignature, keyEncipherment extendedKeyUsage       = serverAuth            # restrict the usage subjectKeyIdentifier   = hash authorityKeyIdentifier = keyid,issuer [ X509_client ] basicConstraints       = CA:FALSE nsCertType             = client                # restrict the usage keyUsage               = digitalSignature      # restrict the usage extendedKeyUsage       = clientAuth            # restrict the usage subjectKeyIdentifier   = hash authorityKeyIdentifier = keyid,issuer [ policy_dn ] countryName            = supplied              # required parameter, any value allowed stateOrProvinceName    = optional localityName           = optional organizationName       = match                 # required, and must match root certificate organizationalUnitName = optional commonName             = supplied              # required parameter, any value allowed emailAddress           = optional              # email in DN is deprecated, use subjectAltName
 * 1) ca-sign.cnf
 * 2) This configuration file is used by the 'ca' command, to create signed certificates.
 * 1) x509_extensions        = X509_ca               # The x509 extensions for the root certificate
 * 2) x509_extensions        = X509_server           # The x509 extensions for a server certificate
 * 1) These options control what fields from the distinguished name to show before signing.
 * 2) They are required to make sure all fields are shown.
 * 1) X509v3 extensions for the root certificate
 * 1) subjectAltName         = email:move            # Move email address from DN to extensions
 * 2) crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
 * 1) X509v3 extensions for server certificates
 * 1) subjectAltName         = email:move            # Move email address from DN to extensions
 * 2) crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
 * 1) X509v3 extensions for client certificates
 * 1) subjectAltName         = email:move            # Move email address from DN to extensions
 * 2) crlDistributionPoints  = URI:http://www.example.com/example_ca.crl

The x509_extensions sections are not really required by openssl or openvpn, but allows extra security by telling OpenVPN that clients only may connect to servers only. nsCertType is required for the OpenVPN option ns-cert-type server|client; keyUsage and extendedKeyUsage are required for remote-cert-tls server|client.

defines the distinguished name for the certificate authority. It also contains the key length (2048 is recommended nowadays, over the default of 1024), and if the key should be encrypted.

[ req ] default_bits           = 2048                  # default strength of client certificates default_md             = sha2 encrypt_key            = yes                   # "no" is equivalent to -nodes prompt                 = no string_mask             = utf8only distinguished_name     = ca_distinguished_name     # root certificate name req_extensions         = req_cert_extensions [ ca_distinguished_name ] countryName            = NL localityName            = Hometown organizationName       = My Organisation commonName             = OpenVPN-CA [ req_cert_extensions ] subjectAltName         = email:hostmaster@example.org
 * 1) ca.cnf
 * 2) This configuration file is used by the 'req' command when the root certificates is created.
 * 1) attributes              = req_attributes
 * 1) root certificate name
 * 1) stateOrProvinceName   = Utrecht
 * 1) organizationalUnitName = My Department Name
 * 1) emailAddress           = hostmaster@example.org   # email in DN is deprecated, use subjectAltName
 * 1) nsCertType              = server

Note that in the above examples, the email address is specified in the subjectAltName, instead of in the distinguished name. This is in accordance with PKIX standards.

Build CA certificate
(roughly equivalent to  or  )

If your CA should be valid after the year 2038, be sure to use openssl 0.9.9 or higher.

First create a request with the correct name, and then self-sign a certificate and create a serial number file.

cd ca openssl req -new -config ca.cnf -keyout ca.key -out ca.req openssl ca -config ca-sign.cnf -extensions X509_ca -days 3650 -create_serial -selfsign \ -keyfile ca.key -in ca.req -out ca.crt chmod 400 ca.key chmod 444 ca.crt

These actions create ca.key and ca.crt. ca.key should be kept secret. ca.crt should be distributed to all servers and clients.

cp ca/ca.crt server/ cp ca/ca.crt client1/ cp ca/ca.crt client2/

Generate Prime Numbers
(roughly equivalent to )

cd server openssl dhparam -out dh2048.pem 2048

Build server certificate
(roughly equivalent to  or  )

First create a configuration for the server, similar to :

[ req ] default_bits           = 2048 default_md             = sha2 encrypt_key            = no prompt                  = no string_mask             = utf8only distinguished_name     = server_distinguished_name req_extensions         = req_cert_extensions [ server_distinguished_name ] countryName            = NL localityName            = HomeTown organizationName       = My Organisation commonName             = vpnserver.example.org emailAddress           = hostmaster@example.org [ req_cert_extensions ] nsCertType             = server subjectAltName         = email:hostmaster@example.org
 * 1) server.cnf
 * 2) This configuration file is used by the 'req' command when the server certificate is created.
 * 1) attributes              = req_attributes
 * 1) stateOrProvinceName   = Utrecht
 * 1) organizationalUnitName = My Department Name

Create the server request and private key: cd server openssl req -new -config server.cnf -keyout server.key -out server.req chmod 400 server.key

Copy server.req to the CA machine, and run on the CA machine: cp server/server.req ca/ cd ca openssl ca -config ca-sign.cnf -extensions X509_server -in server.req -out server.crt

Copy server.crt to the client machine. cp ca/server.crt server/

You can delete server.req. Only server.crt and server.key are required on the server.

If you have multiple servers, repeat this step for all servers.

Build client certificate
(roughly equivalent to )

Create a  similar to  :

[ req ] default_bits           = 2048 default_md             = sha2 encrypt_key            = no prompt                  = no string_mask             = utf8only distinguished_name     = client1_distinguished_name req_extensions         = req_cert_extensions [ client1_distinguished_name ] countryName            = NL localityName            = HomeTown organizationName       = My Organisation commonName             = client1.example.com [ req_cert_extensions ] nsCertType             = client subjectAltName         = email:hostmaster@example.com
 * 1) client1.cnf
 * 2) This configuration file is used by the 'req' command when a certificate is created for client 1.
 * 1) attributes              = req_attributes
 * 1) stateOrProvinceName   = Utrecht
 * 1) organizationalUnitName = My Department Name

On the client machine cd client1/ openssl req -new -config client1.cnf -keyout client1.key -out client1.req chmod 400 client1.key

Copy client1.req to the CA machine, and run on the CA machine: cp client1/client1.req ca/ cd ca/ openssl ca -config ca-sign.cnf -out client1.crt -in client1.req

Copy client1.crt to the client machine: cp ca/client1.crt client1/

You can delete client1.req. Only client1.crt and client1.key are required on the client.

Repeat this step for all client hosts.

Revoke a client certificate
If the file does not exist, create the crlnumber with a serial number: echo '0000000000000001' > crlnumber

openssl ca -config ca-sign.cnf -crl_reason superseded -revoke 5FE840894254A22.crt openssl ca -config ca-sign.cnf -gencrl -out crl.pem

where the reason is one of the following
 * unspecified
 * keyCompromise
 * CACompromise
 * affiliationChanged
 * superseded
 * cessationOfOperation
 * certificateHold

Test the revocation: cat ca.crt crl.pem > revoke-test.pem openssl verify -CAfile revoke-test.pem -crl_check 5FE840894254A22.crt

If the certificate is revoked, you should get the error error 23 at 0 depth lookup:certificate revoked

Be sure to distribute the certificate revocation list to your server and clients.

cp ca/crl.pem server/ cp ca/crl.pem client1/ cp ca/crl.pem client2/

Change password
To change the password of your private key: openssl rsa -des3 -in ca.key -out ca_new.key mv ca_new.key ca.key