Created
August 14, 2018 21:12
-
-
Save romen/6b8d8bd6b1df17567a62455d2fc1d7b8 to your computer and use it in GitHub Desktop.
Ed25519 PKI script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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:[email protected] | |
export subjectAltName=email:[email protected] | |
# 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 | |
[email protected] | |
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 | |
[email protected] | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
OSSL_APPS_OPT
to avoid passing-engine libsuola
openssl
binary, set doexport OPENSSL=/path/to/custom/openssl
before executing the script-aes256
to encrypt the private keys generated withgenpkey
, setOSSL_ENCRYPT_OPT
OPENSSL_ENGINES
env variable iflibsuola
is not found