Skip to content

Instantly share code, notes, and snippets.

@romen
Created August 14, 2018 21:12
Show Gist options
  • Save romen/6b8d8bd6b1df17567a62455d2fc1d7b8 to your computer and use it in GitHub Desktop.
Save romen/6b8d8bd6b1df17567a62455d2fc1d7b8 to your computer and use it in GitHub Desktop.
Ed25519 PKI script
#!/bin/bash
# from https://tools.ietf.org/html/draft-moskowitz-ecdsa-pki-03 (adapted for EdDSA)
# Workdir is set to $1, $workdir or $PWD (in this order of precedence)
workdir=${1:-${workdir:-$PWD}}
OPENSSL=${OPENSSL:-$(which openssl)}
OSSL_APPS_OPT="-engine libsuola"
#OSSL_ENCRYPT_OPT="-aes256"
function tput_wrapper() {
local af=$1
local ab=$2
shift 2
local text="${@}"
echo "$(tput setaf $af)$(tput setab $ab)$text$(tput sgr0)"
}
function header() {
set +x
local title=$1
tput_wrapper 4 7 "#--------------------- ${title}"
}
function wrp() {
local whitespace="[[:space:]]"
local text="#"
for i in "$@"; do
if [[ $i =~ $whitespace ]]; then
text="$text \"$i\""
fi
text="$text $i"
done
tput_wrapper 6 0 "$text"
local cmd=$1
shift
$cmd "$@"
}
WOPENSSL="wrp $OPENSSL"
set -e
#-------------------------------------------- 4.1 Setting up the Environment for Root CA
header "4.1 Setting up the Environment for Root CA"
# Directory for certificate files
export dir=$workdir/ca
# Directory for Root certificate files
export cadir=$workdir/ca
# File encoding: PEM or DER
# At this time only PEM works
export format=pem
mkdir $dir
cd $dir
mkdir certs crl csr newcerts private
chmod 700 private
touch index.txt
touch serial
# Serial Number length in bytes. For a public CA the range is 8 to 19
sn=8
# The DN and SAN fields are examples. Change them to appropriate
# values. If you leave one blank, it will be left out of the
# Certificate. "OU" is an example of an empty DN object.
#countryName="/C=US"
countryName="/C=FI"
#stateOrProvinceName="/ST=MI"
stateOrProvinceName="/ST=TRE"
#localityName="/L=Oak Park"
localityName="/L=Tampere"
#organizationName="/O=HTT Consulting"
organizationName="/O=Tampere University of Technology"
##organizationalUnitName="/OU="
#organizationalUnitName=
organizationalUnitName="/OU=Pervasive Computing Lab - NISEC"
#commonName="/CN=Root CA"
commonName="/CN=Test Root CA (tuveri)"
DN=$countryName$stateOrProvinceName$localityName
DN=$DN$organizationName$organizationalUnitName$commonName
echo $DN
#export subjectAltName=email:postmaster@htt-consult.com
export subjectAltName=email:nicola.tuveri@tut.fi
# Create the file, $dir/openssl-root.cnf from the contents in Appendix A.1.
cat >$dir/openssl-root.cnf <<-'EOF'
# OpenSSL root CA configuration file.
# Copy to `$dir/openssl-root.cnf`.
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = $ENV::dir
cadir = $ENV::cadir
format = $ENV::format
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = $cadir/private/ca.key.$format
certificate = $cadir/certs/ca.cert.$format
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/ca.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
#default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_strict
copy_extensions = copy
[ 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 = 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 = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
req_extensions = req_ext
# SHA-1 is deprecated, so use SHA-2 instead.
#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
# Optionally, specify some defaults.
# countryName_default = US
# stateOrProvinceName_default = MI
# localityName_default = Oak Park
# 0.organizationName_default = HTT Consulting
# organizationalUnitName_default =
[ req_ext ]
subjectAltName = $ENV::subjectAltName
[ 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
keyUsage = critical, cRLSign, keyCertSign
subjectAltName = $ENV::subjectAltName
[ 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
keyUsage = critical, cRLSign, keyCertSign
[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOF
#-------------------------------------------- 4.2. Create the Root Certificate
header "4.2. Create the Root Certificate"
# Create passworded keypair file
#$OPENSSL genpkey -aes256 \
# -outform $format -out $dir/private/ca.key.$format \
# -algorithm ec -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve
$WOPENSSL genpkey ${OSSL_APPS_OPT} ${OSSL_ENCRYPT_OPT} \
-outform $format -out $dir/private/ca.key.$format \
-algorithm Ed25519
chmod 400 $dir/private/ca.key.$format
$WOPENSSL pkey ${OSSL_APPS_OPT} -inform $format -in private/ca.key.$format -text -noout
# Create Self-signed Root Certificate file
# 7300 days = 20 years; Intermediate CA is 10 years.
# remove -sha256 from cmdline and config file
$WOPENSSL req ${OSSL_APPS_OPT} -config $dir/openssl-root.cnf\
-set_serial 0x$($OPENSSL rand ${OSSL_APPS_OPT} -hex $sn)\
-keyform $format -outform $format\
-key $dir/private/ca.key.$format -subj "$DN"\
-new -x509 -days 7300 -extensions v3_ca\
-out $dir/certs/ca.cert.$format
$WOPENSSL x509 ${OSSL_APPS_OPT} -inform $format -in $dir/certs/ca.cert.$format\
-text -noout
$WOPENSSL x509 ${OSSL_APPS_OPT} -purpose -inform $format\
-in $dir/certs/ca.cert.$format -inform $format
#-------------------------------------------- 5.1. Setting up the Intermediate Certificate Environment
header "5.1. Setting up the Intermediate Certificate Environment"
export dir=$cadir/intermediate
mkdir $dir
cd $dir
mkdir certs crl csr newcerts private
chmod 700 private
touch index.txt
sn=8 # hex 8 is minimum, 19 is maximum
echo 1000 > $dir/crlnumber
export crlDP=
# For CRL support use uncomment these:
#crl=intermediate.crl.pem
#crlurl=www.htt-consult.com/pki/$crl
#export crlDP="URI:http://$crlurl"
export default_crl_days=30
export ocspIAI=
# For OCSP support use uncomment these:
#ocspurl=ocsp.htt-consult.com
#export ocspIAI="OCSP;URI:http://$ocspurl"
commonName="/CN=Test Signing CA"
DN=$countryName$stateOrProvinceName$localityName$organizationName
DN=$DN$organizationalUnitName$commonName
echo $DN
# Create the file, $dir/openssl-intermediate.cnf from the contents in Appendix A.2.
# Remove the crlDistributionPoints to drop CRL support and authorityInfoAccess to drop OCSP support.
cat >$dir/openssl-intermediate.cnf <<-'EOF'
# OpenSSL intermediate CA configuration file.
# Copy to `$dir/intermediate/openssl.cnf`.
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = $ENV::dir
cadir = $ENV::cadir
format = $ENV::format
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The Intermediate key and Intermediate certificate.
private_key = $dir/private/intermediate.key.$format
certificate = $dir/certs/intermediate.cert.$format
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/intermediate.crl.pem
crl_extensions = crl_ext
default_crl_days = $ENV::default_crl_days
# SHA-1 is deprecated, so use SHA-2 instead.
#default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_loose
copy_extensions = copy
[ 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 = 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 = optional
UID = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
req_extensions = req_ext
# SHA-1 is deprecated, so use SHA-2 instead.
#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
UID = User ID
# Optionally, specify some defaults.
# countryName_default = US
# stateOrProvinceName_default = MI
# localityName_default = Oak Park
# 0.organizationName_default = HTT Consulting
# organizationalUnitName_default =
[ req_ext ]
subjectAltName = $ENV::subjectAltName
[ 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
keyUsage = critical, 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
keyUsage = critical, 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 = $ENV::crlDP
#authorityInfoAccess = $ENV::ocspIAI
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
#crlDistributionPoints = $ENV::crlDP
#authorityInfoAccess = $ENV::ocspIAI
[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOF
#-------------------------------------------- 5.2. Create the Intermediate Certificate
header "5.2. Create the Intermediate Certificate"
# Create passworded keypair file
#openssl genpkey -aes256 \
# -outform $format -out $dir/private/intermediate.key.$format \
# -algorithm ec -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve
$WOPENSSL genpkey ${OSSL_APPS_OPT} ${OSSL_ENCRYPT_OPT} \
-outform $format -out $dir/private/intermediate.key.$format \
-algorithm Ed25519
chmod 400 $dir/private/intermediate.key.$format
$WOPENSSL pkey ${OSSL_APPS_OPT} -inform $format -in $dir/private/intermediate.key.$format -text -noout
# Create the CSR
# remove -sha256 from cmdline and config file
$WOPENSSL req ${OSSL_APPS_OPT} -config $dir/openssl-intermediate.cnf\
-key $dir/private/intermediate.key.$format \
-keyform $format -outform $format -subj "$DN" -new\
-out $dir/csr/intermediate.csr.$format
$WOPENSSL req ${OSSL_APPS_OPT} -text -noout -verify -inform $format\
-in $dir/csr/intermediate.csr.$format
# Create Intermediate Certificate file
$OPENSSL rand -hex $sn > $dir/serial # hex 8 is minimum, 19 is maximum
# Note 'openssl ca' does not support DER format
# replace "-md sha256" with "-md default"
$WOPENSSL ca ${OSSL_APPS_OPT} -config $cadir/openssl-root.cnf -days 3650\
-extensions v3_intermediate_ca -notext -md default \
-in $dir/csr/intermediate.csr.$format\
-out $dir/certs/intermediate.cert.pem
chmod 444 $dir/certs/intermediate.cert.$format
$WOPENSSL verify ${OSSL_APPS_OPT} -CAfile $cadir/certs/ca.cert.$format\
$dir/certs/intermediate.cert.$format
$WOPENSSL x509 ${OSSL_APPS_OPT} -noout -text -in $dir/certs/intermediate.cert.$format
# Create the certificate chain file
cat $dir/certs/intermediate.cert.$format\
$cadir/certs/ca.cert.$format > $dir/certs/ca-chain.cert.$format
chmod 444 $dir/certs/ca-chain.cert.$format
#-------------------------------------------- 5.3. Create a Server EE Certificate
header "5.3. Create a Server EE Certificate"
commonName=
DN=$countryName$stateOrProvinceName$localityName
DN=$DN$organizationName$organizationalUnitName$commonName
echo $DN
serverfqdn=www.example.com
emailaddr=postmaster@htt-consult.com
export subjectAltName="DNS:$serverfqdn, email:$emailaddr"
echo $subjectAltName
#openssl genpkey -out $dir/private/$serverfqdn.key.$format \
# -algorithm ec -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve
$WOPENSSL genpkey ${OSSL_APPS_OPT} ${OSSL_ENCRYPT_OPT} \
-out $dir/private/$serverfqdn.key.$format\
-algorithm Ed25519
chmod 400 $dir/private/$serverfqdn.key.$format
$WOPENSSL pkey ${OSSL_APPS_OPT} -in $dir/private/$serverfqdn.key.$format -text -noout
$WOPENSSL req ${OSSL_APPS_OPT} -config $dir/openssl-intermediate.cnf\
-key $dir/private/$serverfqdn.key.$format \
-subj "$DN" -new -out $dir/csr/$serverfqdn.csr.$format
$WOPENSSL req ${OSSL_APPS_OPT} -text -noout -verify -in $dir/csr/$serverfqdn.csr.$format
$OPENSSL rand -hex $sn > $dir/serial # hex 8 is minimum, 19 is maximum
# Note 'openssl ca' does not support DER format
# replace "-md sha256" with "-md default"
$WOPENSSL ca ${OSSL_APPS_OPT} -config $dir/openssl-intermediate.cnf -days 375\
-extensions server_cert -notext -md default \
-in $dir/csr/$serverfqdn.csr.$format\
-out $dir/certs/$serverfqdn.cert.$format
chmod 444 $dir/certs/$serverfqdn.cert.$format
$WOPENSSL verify ${OSSL_APPS_OPT} -CAfile $dir/certs/ca-chain.cert.$format\
$dir/certs/$serverfqdn.cert.$format
$WOPENSSL x509 ${OSSL_APPS_OPT} -noout -text -in $dir/certs/$serverfqdn.cert.$format
#-------------------------------------------- 5.4. Create a Client EE Certificate
header "5.4. Create a Client EE Certificate"
commonName=
UserID="/UID=rgm"
DN=$countryName$stateOrProvinceName$localityName
DN=$DN$organizationName$organizationalUnitName$commonName$UserID
echo $DN
clientemail=rgm@example.com
export subjectAltName="email:$clientemail"
echo $subjectAltName
#openssl genpkey -out $dir/private/$clientemail.key.$format\
# -algorithm ec -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve\
$WOPENSSL genpkey ${OSSL_APPS_OPT} ${OSSL_ENCRYPT_OPT} \
-out $dir/private/$clientemail.key.$format\
-algorithm Ed25519
chmod 400 $dir/private/$clientemail.key.$format
$WOPENSSL pkey ${OSSL_APPS_OPT} -in $dir/private/$clientemail.key.$format -text -noout
$WOPENSSL req ${OSSL_APPS_OPT} -config $dir/openssl-intermediate.cnf\
-key $dir/private/$clientemail.key.$format \
-subj "$DN" -new -out $dir/csr/$clientemail.csr.$format
$WOPENSSL req ${OSSL_APPS_OPT} -text -noout -verify\
-in $dir/csr/$clientemail.csr.$format
$OPENSSL rand -hex $sn > $dir/serial # hex 8 is minimum, 19 is maximum
# Note 'openssl ca' does not support DER format
# replace "-md sha256" with "-md default"
$WOPENSSL ca ${OSSL_APPS_OPT} -config $dir/openssl-intermediate.cnf -days 375\
-extensions usr_cert -notext -md default \
-in $dir/csr/$clientemail.csr.$format\
-out $dir/certs/$clientemail.cert.$format
chmod 444 $dir/certs/$clientemail.cert.$format
$WOPENSSL verify ${OSSL_APPS_OPT} -CAfile $dir/certs/ca-chain.cert.$format\
$dir/certs/$clientemail.cert.$format
$WOPENSSL x509 ${OSSL_APPS_OPT} -noout -text -in $dir/certs/$clientemail.cert.$format
@romen
Copy link
Author

romen commented Aug 14, 2018

The script is not complete: it stops at section 5.4 of https://tools.ietf.org/html/draft-moskowitz-ecdsa-pki-03

It also does not test OCSP and CRL support.

Instructions

  • if you modified the system openssl.cnf to load libsuola, comment out OSSL_APPS_OPT to avoid passing -engine libsuola
  • if you want to test a different openssl binary, set do export OPENSSL=/path/to/custom/openssl before executing the script
  • if you want to enable -aes256 to encrypt the private keys generated with genpkey, set OSSL_ENCRYPT_OPT
  • remember to set the OPENSSL_ENGINES env variable if libsuola is not found

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment