OpenSSL Certificate Management

Encoding types

File contents

File extensions

Note: The PlcEngine uses PKCS12 files. These may:

Certificate Update

The sign tool wants .p12, the Certificate update is delivered as .p7b and has the same private key as the existing certificate

CSR

To generate a code signing request:

Other Conversions

Print certificate contents

More commands

Certificate Authority

ca.sh

Run this once to initialize the CA (Certificate Authority). The security of the CA will depend on keeping the private key file private!
#!/bin/bash

LIFETIME=3650 # 10 years
KEY_SIZE=2048

cat > openssl.conf <<END
[ca]
default_ca        = CA_default

[CA_default]
dir               = .
certs             = \$dir/certs
crl_dir           = \$dir/crl
new_certs_dir     = \$dir/newcerts
database          = \$dir/index.txt
serial            = \$dir/serial
RANDFILE          = \$dir/private/.rand
private_key       = \$dir/private/ca.key.pem
certificate       = \$dir/certs/ca.cert.pem
crlnumber         = \$dir/crlnumber
crl               = \$dir/crl/ca.crl.pem
crl_extensions    = crl_x509_extensions
default_crl_days  = 30
default_md        = sha256
name_opt          = ca_default
cert_opt          = ca_default
default_days      = $LIFETIME
preserve          = no
policy            = ca_policy
x509_extensions   = user_x509_extensions

[user_x509_extensions]
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid,issuer
subjectAltName                  = \$ENV::SUBJECT_ALTERNATIVE_NAME
basicConstraints                = CA:FALSE
keyUsage                        = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage                = serverAuth, clientAuth
#crlDistributionPoints           = URI:http://example.com/ca.crl.pem
#authorityInfoAccess             = OCSP;URI:http://ocsp.example.com

[ca_policy]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = optional
emailAddress            = optional
DC                      = optional

[req]
default_bits        = $KEY_SIZE
distinguished_name  = ca_distinguished_name
attributes          = ca_attributes
string_mask         = utf8only
default_md          = sha256
req_extensions      = ca_req_extensions
x509_extensions     = ca_x509_extensions

[ca_attributes]
challengePassword			= A challenge password
unstructuredName			= An optional company name

[ca_distinguished_name]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address
DC                              = DC

[ca_req_extensions]
basicConstraints = CA:FALSE

[ca_x509_extensions]
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid:always,issuer:always
basicConstraints                = CA:true, pathlen:0
#keyUsage                        = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
keyUsage                        = digitalSignature, keyCertSign, cRLSign

[self_signed_x509_extensions]
subjectKeyIdentifier            = hash
authorityKeyIdentifier          = keyid:always,issuer:always
subjectAltName                  = \$ENV::SUBJECT_ALTERNATIVE_NAME
basicConstraints                = CA:FALSE
keyUsage                        = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
extendedKeyUsage                = serverAuth, clientAuth

[crl_x509_extensions]
authorityKeyIdentifier          = keyid:always

END

if [[ -d certs ]] || [[ -d requests ]] || [[ -d crl ]] || [[ -d newcerts ]] || [[ -d private ]] || [[ -f index.txt ]] || [[ -f serial ]]; then
	echo "CA already generated (or fragments of old still present)"
	exit 1
fi

echo "intializing"
mkdir certs requests crl newcerts private
chmod 700 private
touch index.txt
echo "unique_subject = no" > index.txt.attr
openssl rand -hex 16 > serial
echo 00000001 > crlnumber
echo -n "secret_password_1234_ABCD" > private/password.txt
chmod 400 private/password.txt

echo "creating root key"
openssl genrsa -aes256 -passout file:private/password.txt -out private/ca.key.pem $KEY_SIZE
chmod 400 private/ca.key.pem

echo "generating CA certificate"
export SUBJECT_ALTERNATIVE_NAME="URI:urn:TANI_CA"
openssl req -config openssl.conf -batch -key private/ca.key.pem -passin file:private/password.txt -new -x509 -sha256 -days $LIFETIME -extensions ca_x509_extensions -subj "/CN=Tani Test CA" -out certs/ca.cert.pem
chmod 444 certs/ca.cert.pem
openssl x509 -inform PEM -in certs/ca.cert.pem -outform DER -out certs/ca.cert.der
chmod 444 certs/ca.cert.der

echo "generating CRL"
openssl ca -config openssl.conf -gencrl -passin file:private/password.txt -out crl/ca.crl.pem
openssl crl -inform PEM -in crl/ca.crl.pem -outform DER -out crl/ca.crl.der

# Hints:
# cert to CSR
# openssl x509 -x509toreq -in my.crt -out my.csr -signkey my.key
# test cert/key match
# openssl x509 -noout -modulus -in server.crt | openssl md5
# openssl rsa -noout -modulus -in server.key | openssl md5
# print crl
# openssl crl -noout -text -inform der -in crl/ca.crl.der

cert.sh

This will generate a new certificate and sign it with the CA certificate.
#!/bin/bash

LIFETIME=3650 # 10 years
KEY_SIZE=2048

if [[ ! -d certs ]] || [[ ! -d requests ]] || [[ ! -d crl ]] || [[ ! -d newcerts ]] || [[ ! -d private ]] || [[ ! -f index.txt ]] || [[ ! -f serial ]]; then
	echo "CA not initialized correctly"
	exit 1
fi

if [[ "$#" -ne 1 ]]; then
	echo "usage: cert.sh hostname"
fi

HOSTNAME=$1
IP=`dig $HOSTNAME | awk '/^;; ANSWER SECTION:$/ { getline ; print $5 }'`

if [[ "$IP" == "" ]]; then
	echo "can't resolve hostname $HOSTNAME"
	exit 1
fi

echo "creating key"
openssl genrsa -aes256 -passout file:private/password.txt -out private/$HOSTNAME.key.pem $KEY_SIZE
chmod 400 private/$HOSTNAME.key.pem

echo "generating CSR for client and server"
export SUBJECT_ALTERNATIVE_NAME="URI:urn:$HOSTNAME:TaniOpcUaClient,DNS:$HOSTNAME,IP:$IP"
openssl req -config openssl.conf -key private/$HOSTNAME.key.pem -passin file:private/password.txt -new -sha256 -subj "/CN=$HOSTNAME" -out requests/$HOSTNAME.Client.req.pem
chmod 444 requests/$HOSTNAME.Client.req.pem
export SUBJECT_ALTERNATIVE_NAME="URI:urn:$HOSTNAME:TaniPlcEngine,DNS:$HOSTNAME,IP:$IP"
openssl req -config openssl.conf -key private/$HOSTNAME.key.pem -passin file:private/password.txt -new -sha256 -subj "/CN=$HOSTNAME" -out requests/$HOSTNAME.Server.req.pem
chmod 444 requests/$HOSTNAME.Server.req.pem

echo "signing certificates for client and server"
export SUBJECT_ALTERNATIVE_NAME="URI:urn:$HOSTNAME:TaniOpcUaClient,DNS:$HOSTNAME,IP:$IP"
openssl ca -config openssl.conf -extensions user_x509_extensions -passin file:private/password.txt -batch -days $LIFETIME -notext -md sha256 -in requests/$HOSTNAME.Client.req.pem -out certs/$HOSTNAME.Client.cert.pem
chmod 444 certs/$HOSTNAME.Client.cert.pem
openssl x509 -inform PEM -in certs/$HOSTNAME.Client.cert.pem -outform DER -out certs/$HOSTNAME.Client.cert.der
chmod 444 certs/$HOSTNAME.Client.cert.der
export SUBJECT_ALTERNATIVE_NAME="URI:urn:$HOSTNAME:TaniPlcEngine,DNS:$HOSTNAME,IP:$IP"
openssl ca -config openssl.conf -extensions user_x509_extensions -passin file:private/password.txt -batch -days $LIFETIME -notext -md sha256 -in requests/$HOSTNAME.Server.req.pem -out certs/$HOSTNAME.Server.cert.pem
chmod 444 certs/$HOSTNAME.Server.cert.pem
openssl x509 -inform PEM -in certs/$HOSTNAME.Server.cert.pem -outform DER -out certs/$HOSTNAME.Server.cert.der
chmod 444 certs/$HOSTNAME.Server.cert.der

revoke.sh

This will revoke a generated certificate and regenerate the CRL (Certificate Revocation List).
#!/bin/bash

if [[ ! -d certs ]] || [[ ! -d requests ]] || [[ ! -d crl ]] || [[ ! -d newcerts ]] || [[ ! -d private ]] || [[ ! -f index.txt ]] || [[ ! -f serial ]]; then
	echo "CA not initialized correctly"
	exit 1
fi

if [[ "$#" -ne 1 ]]; then
	echo "usage: revoke.sh certs/certificate_name.pem"
fi

CERT=$1
export SUBJECT_ALTERNATIVE_NAME=

echo "revoking certificate"
openssl ca -config openssl.conf -revoke $1

echo "generating CRL"
openssl ca -config openssl.conf -gencrl -passin file:private/password.txt -out crl/ca.crl.pem
openssl crl -inform PEM -in crl/ca.crl.pem -outform DER -out crl/ca.crl.der

crl.sh

This will regenerate the CRL. A new CRL must be created every 30 days, even if nothing else has changed.
#!/bin/bash

export SUBJECT_ALTERNATIVE_NAME=

echo "generating CRL"
openssl ca -config openssl.conf -gencrl -passin file:private/password.txt -out crl/ca.crl.pem
openssl crl -inform PEM -in crl/ca.crl.pem -outform DER -out crl/ca.crl.der