Hi,
these are the steps to build your own CA (Certification Authority) and all requiered certificates for a OpenVPN instance (Client and Server) on Linux.
Define your environment. Always set these variables in the shell before executing openssl commands. Adjust it to your needs.
# Root Directory of the CA export CA_ROOT_DIRECTORY=${HOME}/openvpn/CA # openssl binary export OPENSSL_BIN=`which openssl` # Your CA Name, no spaces in Name are allowed!! export CA_NAME=MyOpenVPNCA # The common directory export CA_COMMON_DIR=${CA_ROOT_DIRECTORY}/common # Directory for Client Certificates export CA_CLIENTS_DIR=${CA_ROOT_DIRECTORY}/Clients # Directory for Server Certificate export CA_SERVER_DIR=${CA_ROOT_DIRECTORY}/Server # export CA_CERTS_DIR=${CA_ROOT_DIRECTORY}/certs # Your CA Password export CA_PASSWORD="YourSecurePassword" # Default Key Bitsize export KEY_BITSIZE=4096 # Cerdificate Lifetime in Days export CERTIFICATE_LIFETIME=3650 # Files # The Environment variable where openssl looking for its config export OPENSSL_CONF=${CA_COMMON_DIR}/openssl.cfg # x509_v3 Server Extensions export OPENSSL_CONF_X509_V3_EXT_SERVER=${CA_COMMON_DIR}/x509v3_server.ext # x509_v3 Client Extensions export OPENSSL_CONF_X509_V3_EXT_CLIENT=${CA_COMMON_DIR}/x509v3_client.ext # The CRL list (Client revocation) export CA_CRL=${CA_COMMON_DIR}/crl.pem
Initialise the CA
# Create all directories mkdir -p "${CA_COMMON_DIR}" mkdir -p "${CA_CLIENTS_DIR}" mkdir -p "${CA_SERVER_DIR}" # Create empty database index touch "${CA_ROOT_DIRECTORY}/index.txt" # Set first serial echo 1 > "${CA_ROOT_DIRECTORY}/serial"
Create a default openvpn config and alter the sections req_distinguished_name to your environment. Simply copy the following lines to your shell.
cat > $OPENSSL_CONF <<EOF dir = . [ ca ] default_ca = $CA_NAME # The default ca section #################################################################### [ $CA_NAME ] # dir = ./CA # Where everything is kept dir = ${CA_ROOT_DIRECTORY} certs = \$dir/certs # Where the issued certs are kept crl_dir = \$dir/crl # Where the issued crl are kept database = \$dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = \$dir/newcerts # default place for new certs. certificate = \$dir/common/ca.cer # The CA certificate serial = \$dir/serial # The current serial number # crlnumber = \$dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = \$dir/crl/crl.pem # The current CRL private_key = \$dir/common/ca.key # The private key RANDFILE = \$dir/common/.rand # private random number file default_days = ${CERTIFICATE_LIFETIME} # how long to certify for default_crl_days= 30 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] default_bits = $KEY_BITSIZE # Size of keys default_keyfile = key.pem # name of generated keys default_md = sha256 # message digest algorithm string_mask = nombstr # permitted characters distinguished_name = req_distinguished_name req_extensions = v3_req [ req_distinguished_name ] # Variable name Prompt string #------------------------- ---------------------------------- 0.organizationName = MyCompany organizationalUnitName = MyOrganisation emailAddress = your-e-mail@MyOrganisation.org emailAddress_max = 40 localityName = Nuremburg stateOrProvinceName = Frankonia countryName = DE countryName_min = 2 countryName_max = 2 commonName = openvpnhost.MyOrganisation.org commonName_max = 64 # Default values for the above, for consistency and less typing. # Variable name Value #------------------------ ------------------------------ 0.organizationName_default = MyCompany localityName_default = Nurenburg stateOrProvinceName_default = Frankonia countryName_default = DE [ v3_server ] basicConstraints = CA:FALSE nsCertType = server nsComment = "Server Certificate for $CA_NAME" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always extendedKeyUsage = serverAuth [ v3_client ] basicConstraints = CA:FALSE nsComment = "Client Certificate for $CA_NAME" nsCertType = client extendedKeyUsage = clientAuth keyUsage = digitalSignature, keyAgreement [ v3_ca ] basicConstraints = CA:TRUE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always [ v3_req ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash EOF
Builting the selfsigned Certification Authority with a 4096Bit RSA Key
# Creating a Selfsigned CA with a RSA 4096Bit Key $OPENSSL_BIN req -new -x509 -days ${CERTIFICATE_LIFETIME} -extensions v3_ca -newkey rsa:${KEY_BITSIZE} -keyout "${CA_COMMON_DIR}/ca.key" -out "${CA_COMMON_DIR}/ca.cer" -batch -passout pass:$CA_PASSWORD
Creating the certificate for the OpenVPN server
# Create Server Key $OPENSSL_BIN genrsa -out "${CA_SERVER_DIR}/server.key" ${KEY_BITSIZE} -aes256 # Create s signing request $OPENSSL_BIN req -nodes -new -key "${CA_SERVER_DIR}/server.key" -out "${CA_SERVER_DIR}/server.req" -extensions v3_server -batch -subj "/C=DE/ST=Frankonia/L=Nurenburg/O=YourCompany/OU=Department/CN=OpenVPN Server/emailAddress=yourMail@yourCompany.org" # Sign server certificate $OPENSSL_BIN x509 -req -days ${CERTIFICATE_LIFETIME} -extfile $OPENSSL_CONF -extensions v3_server -in "${CA_SERVER_DIR}/server.req" -CA "${CA_COMMON_DIR}/ca.cer" -CAkey "${CA_COMMON_DIR}/ca.key" -CAcreateserial -out "${CA_SERVER_DIR}/server.cer" -passin pass:$CA_PASSWORD # Check if x509 v3 extensions are set $OPENSSL_BIN x509 -text -in "${CA_SERVER_DIR}/server.cer"|grep "X509v3 extensions" -A 15 X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Cert Type: SSL Server Netscape Comment: Server Certificate for MyOpenVPNCA X509v3 Subject Key Identifier: 71:AD:6D:4A:37:9B:A6:4A:7C:70:59:14:98:54:C5:6E:4B:36:6E:E3 X509v3 Authority Key Identifier: keyid:35:34:FB:FC:32:5D:09:57:9B:F7:97:91:6F:91:E4:A0:74:98:E7:0C DirName:/O=MyCompany/L=Nurenburg/ST=Frankonia/C=DE serial:BE:6B:FA:A0:67:AD:2A:6F X509v3 Extended Key Usage: TLS Web Server Authentication # Write Client Key and Certificate and CA Certificate to a pkcs12 File with no password $OPENSSL_BIN pkcs12 -password pass: -export -in "${CA_SERVER_DIR}/server.cer" -inkey "${CA_SERVER_DIR}/server.key" -certfile "${CA_COMMON_DIR}/ca.cer" -out "${CA_SERVER_DIR}/server.p12"
Create the crl revocation list. This list contains all certificates you have revoked.
$OPENSSL_BIN ca -gencrl -out ${CA_CRL} -passin pass:$CA_PASSWORD
Create an diffie hellmann file for a secure KeyExchange
$OPENSSL_BIN gendh -out "${CA_SERVER_DIR}/dh.pem" 2048
Copy the pkcs12 ( ${CA_SERVER_DIR}/server.p12 ) , the CRL ( ${CA_CRL} ) and Diffie Hellmann ( ${CA_SERVER_DIR}/dh.pem ) files to your OpenVPN Server and add this options to the config file:
pkcs12 "$PATH_TO/server.p12"
dh "$PATH_TO/dh.pem"
crl-verify "$PATH_TO/crl.pem"
mode server
tls-server
OpenVPN Clients
Do these steps for each Client you want to connect
# Set the Client Computername. This is the common name export CLIENT_COMPUTERNAME=ClientComputerName1
Create a the key
# Create a 4096Bit RSA key $OPENSSL_BIN genrsa -out "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key" ${KEY_BITSIZE} -aes256 # Signing request $OPENSSL_BIN req -new -key "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key" -out "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.reg" -extensions v3_client -batch -subj "/C=DE/ST=Frankonia/L=Nurenburg/O=YourCompany/OU=Department/CN=${CLIENT_COMPUTERNAME}/emailAddress=yourMail@yourCompany.org" # Sign client certificate</pre> <pre> $OPENSSL_BIN x509 -req -days ${CERTIFICATE_LIFETIME} -extfile $OPENSSL_CONF -extensions v3_client -in "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.reg" -CA "${CA_COMMON_DIR}/ca.cer" -CAkey "${CA_COMMON_DIR}/ca.key" -CAcreateserial -out "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.cer" -extensions v3_client -passin pass:$CA_PASSWORD # Export the bundle to a pkcs12 file $OPENSSL_BIN pkcs12 -password pass: -export -in "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.cer" -inkey "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key" -certfile "${CA_COMMON_DIR}/ca.cer" -out "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.p12" # Diffie Hellmann $OPENSSL_BIN gendh -out "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.dh" 2048
Copy the pkcs12 and diffie hellmann file to the Client and add
dh "$Path_To/clientname.dh"
pkcs12 "$Path_To/clientname.p12"
ns-cert-type server
tls-client
to the openvpn config.
Revoke a Client certificate
export CLIENT_COMPUTERNAME=ClientComputerName1 # Revoke a certificate $OPENSSL_BIN ca -revoke "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.cer" -passin pass:$CA_PASSWORD # Generate a new CRL $OPENSSL_BIN ca -gencrl -out ${CA_CRL} -passin pass:$CA_PASSWORD
and copy the CRL to the openvpn server
To renew the CA public key create an signing request. If you have built your CA within two steps and still have the original csr then you can specify it by parameter -in
$OPENSSL_BIN req -new -key "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key" -out "${CA_COMMON_DIR}/renew_csr.pem" -extensions v3_ca -passin pass:$CA_PASSWORD
And create the new certificate
$OPENSSL_BIN x509 -req -days $CERTIFICATE_LIFETIME -in "${CA_COMMON_DIR}/renew_csr.pem" -signkey "${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key" -out "${CA_COMMON_DIR}/renewed_ca.cer" -passin pass:$CA_PASSWORD
Michael
Hi!
Greate tutorial and about the only one i could find that went through the whole process with OpenSSL instead of EasyRSA (which I dont want to use). There is just one thing missing. In the openssl config, the row “keyUsage = digitalSignature, keyEncipherment” is missing from the “[ v3_server ]” section.
After adding that, everything worked!
Hi,
Thank you for your nice article. I also cofirm the above argument, otherwise you are going to get
Certificate does not have key usage extension from OpenVPN
Thank you for the article
Also you need to add to [v3_client]
keyUsage = digitalSignature, keyAgreement
Hi Alexander,
post altered. Thank you.
Michael
Hi,
in Debian 9 an Ubuntu 18.04
“gendh” must be replaced with “dhparam -outform PEM”
Thanks!
In addition to the changes suggested in previous comments, I needed to
1) remove the options “-aes256” in both the “# Create Server Key” and “# Create a 4096Bit RSA key” steps.
2) edit the “subj” option in the client section so that it matched what was in the variable names at the top of the instructions. Perhaps it would be possible to instead change the PolicyMatch settings that are currently “match” to be “optional”.
It’s not necessary to remove -aes256. Just write before the BITSIZE:
$OPENSSL_BIN genrsa -out “${CA_SERVER_DIR}/server.key” -aes256 ${KEY_BITSIZE}
$OPENSSL_BIN genrsa -out “${CA_CLIENTS_DIR}/${CLIENT_COMPUTERNAME}.key” -aes256 ${KEY_BITSIZE}
It does not appear to update the index and the serial file from the code given. I’m in a doubt whether I’m doing something wrong, but as it was mostly copy paste, I doubt it. I do not have any functionality issues aside from this.
I found it! The serial does end up being the ANSI format openssl expects, but ASCII.
I changed it to echo “01” > “${CA_ROOT_DIRECTORY}/serial”