This documentation shows you how to create test certificates with openssl. A typical certificate-chain has the following structure: root-ca > sub-ca > marketpartner.

The script documented here creates the whole certificate chain beginning with a self-signed Root-CA certificate.

This script is based on the following guide: https://jamielinux.com/docs/openssl-certificate-authority

You can find the openssl documentation here.

Overview

The Root-CA creates its key-pair. (1.2)

The Root-CA signs its certificate itself. (1.3)

The Sub-CA creates its key-pair. (2.2)

The Sub-CA creates its CSR. (2.3)

The Root-CA takes the CSR and signs the certificate of the Sub-CA. (2.4)

The marketpartner creates its key-pair. (3.2)

The marketpartner creates its CSR. (3.3)

The Sub-CA takes the CSR and signs the certificate of the marketpartner. (3.4)

Running the script

Install openssl

You have to install openssl 1.1.* or newer. This can be done either with a linux-shell or with cygwin under windows.

Copy two files

You will need to create two files: “openssl.cnf” and “CreateCerts.sh”.

You will find the contents of both files in this guide. Save them both into one folder.

CreateCerts.sh is the bash script, that calls openssl and does all the work.

openssl.cnf is the configuration file for openssl.

Run CreateCerts.sh

The shell script creates test certificates for marketpartners w2e@b2bbp.org, e2w@b2bbp.org, ago-energy@b2bbp.org and rsc-energy@b2bbp.org. There is no need to change the script.

Start the script with “./CreateCerts.sh”. The folder “result” will be created. There you will find:

  • CA certificate arvato-ca.cer
  • Sub-CA certificate arvato-sub-ca.cer
  • Marketpartner certificate <mp>.cer for each configured marketpartner
  • Marketpartner private key <mp>.p12 for each configured marketpartner
  • Sub-CA CRL with all certificates revoked crl-revoked/arvato-sub-ca.crl
  • Sub-CA CRL with none certificates revoked crl-ok/arvato-sub-ca.crl

You can then upload the keys and certificates in FSS. The password for the keys is b2bbp. The certificates can be zipped and uploaded in FSS all at once.

openssl.cnf

# OpenSSL configuration file.

[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
certs             = certs
crl_dir           = crl
new_certs_dir     = newcerts
database          = index.txt
RANDFILE          = private/.rand
keyCertName       = ${ENV::fullName}
private_key       = private/$keyCertName.key
certificate       = certs/$keyCertName.cer
serial            = serial

# For certificate revocation lists.
crl               = crl/$keyCertName.crl
crl_extensions    = crl_ext
# default_crl_days would be shorter in production environment
default_crl_days = 3650

default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no

[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ca

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
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

# Optionally, specify some defaults.
countryName_default             = GB
stateOrProvinceName_default     = England
localityName_default            =
0.organizationName_default      = Alice Ltd
organizationalUnitName_default  =
emailAddress_default            =

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
crlDistributionPoints  = URI:${ENV::crlUrl}
subjectAltName = @alt_names

[alt_names]
# this puts the email as the alternative name "RFC822 Name" which is required by BDEW
email = ${ENV::email}

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

CreateCerts.sh

# preconditions
# * (On Windows) Cygwin
# * openssl version 1.1.* or newer installed
# * openssl.cnf is available in your working directory

# Run in Cygwin with "./CreateCerts.sh"
# One ca (root) and one sub-ca certificate will be created. Multiple client certificates will be signed with the same sub-ca certificate

# Certificates and keys will be created for each of these clients:
clients=( w2e e2w ago-energy rsc-energy )

# See the created certificates and private keys in folder "results"

baseDir=temp
resultDir=result

rm -r $baseDir $resultDir
mkdir $baseDir $resultDir
cd $baseDir

######################################
# 0. Global variables
######################################
country=DE
stateOrProvinceName=NRW
organization=arvato
export crlUrl="will be set later"
export email="will be set later"
passw=b2bbp

######################################
# 1. Root-CA Key & Certificate
######################################

unit=ca
fullNameCA=$organization-$unit
export fullName=$fullNameCA
subject="/C=$country/ST=$stateOrProvinceName/O=$organization/OU=$unit/CN=$fullNameCA"

######################################
# 1.1 install root-ca directory & database
######################################

mkdir $fullNameCA
cd $fullNameCA
cp ../../openssl.cnf .
mkdir private certs newcerts
# database file indext.txt
touch index.txt

######################################
# 1.2 create private key
######################################

# create the keypair. Algorithm is RSA-PSS. The key size is 4096; the exponent is 65537.
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:4096 -pkeyopt rsa_keygen_pubexp:65537 -pass pass:$passw -out private/$fullNameCA.key

######################################
# 1.3 create self-signed certificate
######################################

# create the certificate
openssl req -config openssl.cnf -key private/$fullNameCA.key -new -x509 -days 3650 -sha256 -extensions v3_ca -passin pass:$passw -subj $subject -out certs/$fullNameCA.cer

# verify
echo
echo $fullNameCA certificate:
openssl x509 -noout -text -in certs/$fullNameCA.cer

######################################
# 1.4 copy certificate to results
######################################
cp certs/$fullNameCA.cer ../../$resultDir

######################################
# 2. Sub-CA Key & Certificate
######################################

unit=sub-ca
fullNameSubCA=$organization-$unit
export fullName=$fullNameSubCA
subject="/C=$country/ST=$stateOrProvinceName/O=$organization/OU=$unit/CN=$fullNameSubCA"
export crlUrl=http://localhost:8080/sample/$fullNameSubCA.crl

######################################
# 2.1 install sub-ca directory & database
######################################

cd ..
mkdir $fullNameSubCA
cd $fullNameSubCA
cp ../../openssl.cnf .
mkdir private certs newcerts csr crl crl/ok crl/revoked
# database file indext.txt
touch index.txt

######################################
# 2.2 create private key
######################################

# create the keypair. Algorithm is RSA-PSS. The key size is 4096; the exponent is 65537.
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:4096 -pkeyopt rsa_keygen_pubexp:65537 -pass pass:$passw -out private/$fullNameSubCA.key

######################################
# 2.3 request certificate signing
######################################

# create the request
openssl req -config openssl.cnf -key private/$fullNameSubCA.key -new -sha256 -passin pass:$passw -subj $subject -out csr/$fullNameSubCA.csr

# verify
echo
echo $fullNameSubCA certificate sign request:
openssl req -config openssl.cnf -noout -text -in csr/$fullNameSubCA.csr -verify

######################################
# 2.4 sign certificate
######################################

cd ../$fullNameCA
export fullName=$fullNameCA

# sign the certificate
# use policy_strict to sign intermediate certificates
# expires after 5 years
openssl ca -config openssl.cnf -days 1825 -notext -md sha256 -batch -extensions v3_intermediate_ca -policy policy_strict -rand_serial -in ../$fullNameSubCA/csr/$fullNameSubCA.csr -passin pass:$passw -out ../$fullNameSubCA/certs/$fullNameSubCA.cer

# verify
echo
echo $fullNameSubCA certificate:
openssl x509 -noout -text -in ../$fullNameSubCA/certs/$fullNameSubCA.cer

######################################
# 2.5 create empty CRL 
######################################
cd ../$fullNameSubCA
export fullName=$fullNameSubCA
openssl ca -config openssl.cnf -gencrl -passin pass:$passw -batch -out crl/ok/$fullNameSubCA.crl

# verify
echo
echo $fullNameSubCA crl:
openssl crl -in crl/ok/$fullNameSubCA.crl -noout -text

######################################
# 2.6 copy certificate and crl to results
######################################
cp certs/$fullNameSubCA.cer ../../$resultDir
mkdir ../../$resultDir/crl-ok
cp crl/ok/$fullNameSubCA.crl ../../$resultDir/crl-ok


######################################
# 3. Market partner keys, certificates & related CRLs
######################################

for fullNameClient in "${clients[@]}"
do

    export fullName=$fullNameClient
    # best practise: unique name for every certificate -> solved here with current date
    date=$(date +%Y%m%d-%H%M%S)
    cn=$fullNameClient-$date
    subject="/C=$country/ST=$stateOrProvinceName/O=$fullNameClient/CN=$cn"
    export email=$fullNameClient@b2bbp.org


    ######################################
    # 3.1 install market partner directory
    ######################################

    cd ..
    mkdir $fullNameClient
    cd $fullNameClient
    cp ../../openssl.cnf .
    mkdir private certs csr

    ######################################
    # 3.2 create private key
    ######################################

    # create the keypair. The key size is 4096; the exponent is 65537
    # New versions of openssl use a longer salt. With key size 3072 Bouncy Castle produces an error "key too small for specified hash and salt lengths". 
    # Therefore the key size cannot be less than 4096
    openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:4096 -pkeyopt rsa_keygen_pubexp:65537 -pass pass:$passw -out private/$fullNameClient.key

    ######################################
    # 3.3 request certificate signing
    ######################################

    # create the request
    openssl req -config openssl.cnf -key private/$fullNameClient.key -new -sha256 -passin pass:$passw -subj $subject -out csr/$fullNameClient.csr

    # verify
    echo
    echo $fullNameClient certificate sign request:
    openssl req -config openssl.cnf -noout -text -in csr/$fullNameClient.csr -verify

    ######################################
    # 3.4 sign certificate
    ######################################

    cd ../$fullNameSubCA
    export fullName=$fullNameSubCA

    # sign the certificate
    # use policy_loose to sign client certificates
    # expires after 3 years
    openssl ca -config openssl.cnf -days 1095 -notext -md sha256 -batch -extensions usr_cert -policy policy_loose -rand_serial -in ../$fullNameClient/csr/$fullNameClient.csr -passin pass:$passw -out ../$fullNameClient/certs/$fullNameClient.cer

    # verify
    echo
    echo $fullNameClient certificate:
    openssl x509 -noout -text -in ../$fullNameClient/certs/$fullNameClient.cer

    ######################################
    # 3.5 p12 key format
    ######################################

    cd ../$fullNameClient
    openssl pkcs12 -export -in certs/$fullNameClient.cer -inkey private/$fullNameClient.key -passin pass:$passw -passout pass:$passw > private/$fullNameClient.p12

    #verify
    echo
    echo $fullNameClient p12 certificate:
    openssl pkcs12 -noout -info -in private/$fullNameClient.p12 -passin pass:$passw

    ######################################
    # 3.7 revoke client certificate
    ######################################

    cd ../$fullNameSubCA
    openssl ca -config openssl.cnf -passin pass:$passw -batch -revoke ../$fullNameClient/certs/$fullNameClient.cer
    
    ######################################
    # 3.8 copy certificate and keys to results
    ######################################
    cd ../$fullNameClient
    cp private/$fullNameClient.p12 ../../$resultDir
    cp certs/$fullNameClient.cer ../../$resultDir

done

######################################
# 4 create CRL with revoked certificates
######################################

# create
cd ../$fullNameSubCA
openssl ca -config openssl.cnf -gencrl -passin pass:$passw -batch -out crl/revoked/$fullNameSubCA.crl

# verify
echo
echo $fullNameSubCA revoked crl:
openssl crl -in crl/revoked/$fullNameSubCA.crl -noout -text

# copy
mkdir ../../$resultDir/crl-revoked
cp crl/revoked/$fullNameSubCA.crl ../../$resultDir/crl-revoked

Certificate Revocation Lists

The script creates two CRLs for test purposes: one has all marketpartner certificates revoked, the other has none revoked certificates.

For simplicity the CRLs are only for certificates, that are signed by the Sub-CA. The CRL, where the Sub-CA-certificate itself could be revoked, will not be created.

The CRLs need to be reachable over the URL http://localhost:8080/sample/arvato-sub-ca.crl

You can use a tomcat to host a CRL under this address. Configure the Tomcat for the port 8080. You need to deploy a war that does not use a password (B2B does unfortunately). A possible war can be found here. and put the arvato-sub-ca.crl file into the unpacked war.

Notes for further maintenance of the script

BDEW conformity

Algorithm RSA-PSS

Use RSA-PSS to create the private key. Therefore instead of

openssl genrsa ...

use

openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -pass pass:$passw -out private/$cn.key

Openssl 1.0.* does not support RSA-PSS algorithm. The version need to be at least 1.1.*

Extension SubjectAlternativeName

How to add the SubjectAlternativeName to the certificate:

openssl.cnf

[ usr_cert ]
subjectAltName = @alt_names

[alt_names]
email = ago-energy@b2bbp.org

Don’t forget to use the extension usr_cert in query.

Private key size

New versions of openssl use a longer salt. With key size 3072 Bouncy Castle produces an error “key too small for specified hash and salt lengths”. Therefore the key size cannot be less than 4096.

View Me   Edit Me