209 lines
7.5 KiB
Plaintext
209 lines
7.5 KiB
Plaintext
Certificate Authority with NEO
|
|
------------------------------
|
|
|
|
This document explains how to set up a Certificate Authority (CA) with
|
|
Sub-CA private keys stored on YubiKey NEOs. Typical use for this is
|
|
to generate HTTPS certificates for internal servers.
|
|
|
|
Considerations
|
|
--------------
|
|
|
|
For our example, we have chosen to use one root CA with a private key
|
|
stored in an offline machine, that signs sub-CAs with private keys
|
|
stored on YubiKey NEOs, which signs end-entity (EE) certs. We'll
|
|
generate the Sub-CA private keys on an offline host and save a copy of
|
|
those keys.
|
|
|
|
We have chosen to use a RSA 3744 bit root CA key, and RSA 2048 bit
|
|
keys for the NEO Sub-CAs and EE certificates. The NEO is limited to
|
|
RSA 1k and 2k keys (it supports ECDSA too but we chose to not use that
|
|
here).
|
|
|
|
By setting some name constraints, we are trying to limit to powers of
|
|
this CA. This is not fully supported by all environments, but it
|
|
should do no harm, and may be useful in some environments.
|
|
|
|
The root also has a path length constraint of 1 to prevent the Sub-CAs
|
|
from issuing further Sub-Sub-CAs.
|
|
|
|
We also set a expiry date far away in the future on the root CA
|
|
(expiring in 1000000 days) and use datefudge to set an arbitrary start
|
|
date for the CA, to avoid leaking the time of CA creation which would
|
|
leak some bits if information going into the randomness generation.
|
|
|
|
Preparations
|
|
------------
|
|
|
|
We use OpenSSL to generate keys and certificates. This is done on an
|
|
offline machine, booted from a LiveCD. Some additional packages may
|
|
be required (pcscd, etc, see below) and will have to be transferred on
|
|
a USB stick.
|
|
|
|
You need a YubiKey NEO with the PIV applet on, which you can purchase
|
|
from Yubico.
|
|
|
|
You need to install the PKCS#11 Engine:
|
|
|
|
sudo dpkg -i libengine-pkcs11-openssl*
|
|
|
|
or if you are on a connected machine, more simpler:
|
|
|
|
sudo apt-get install libengine-pkcs11-openssl
|
|
|
|
Creating a Root CA
|
|
-------------------
|
|
|
|
Generate the private key as follows:
|
|
|
|
openssl genrsa -out yubico-internal-https-ca-key.pem 3744
|
|
|
|
Generate the Root CA certificate and initialize the CA serial number
|
|
counter as follows:
|
|
|
|
cat>yubico-internal-https-ca.conf<<EOF
|
|
[ req ]
|
|
x509_extensions = v3_ca
|
|
distinguished_name = req_distinguished_name
|
|
prompt = no
|
|
[ req_distinguished_name ]
|
|
CN=Yubico Internal HTTPS CA
|
|
[ v3_ca ]
|
|
subjectKeyIdentifier=hash
|
|
basicConstraints=critical,CA:true,pathlen:1
|
|
keyUsage=critical,keyCertSign,cRLSign
|
|
nameConstraints=critical,@nc
|
|
[ nc ]
|
|
permitted;otherName=1.3.6.1.5.5.7.8.7;IA5:yubico.com
|
|
permitted;email.0=yubico.com
|
|
permitted;email.1=.yubico.com
|
|
permitted;DNS=yubico.com
|
|
permitted;URI.0=yubico.com
|
|
permitted;URI.1=.yubico.com
|
|
permitted;IP.0=0.0.0.0/255.255.255.255
|
|
permitted;IP.1=::/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
|
EOF
|
|
datefudge "2014-01-01 UTC" openssl req -new -sha256 -x509 -set_serial 1 -days 1000000 -config yubico-internal-https-ca.conf -key yubico-internal-https-ca-key.pem -out yubico-internal-https-ca-crt.pem
|
|
echo 01 > yubico-internal-https-ca-crt.srl
|
|
|
|
You may inspect the newly generated root CA with:
|
|
|
|
openssl x509 -text < yubico-internal-https-ca-crt.pem
|
|
|
|
Preparing a Sub-CA NEO
|
|
----------------------
|
|
|
|
We need to change the management key, PIN and PUK code following the
|
|
YubiKey-NEO-PIV-Introduction.txt document. We also want to save a
|
|
copy of these values. Here are the steps that are needed to be done
|
|
for each new Sub-CA NEO.
|
|
|
|
This step is parametrized with the name of the YubiKey NEO user.
|
|
Generate new management code, PIN and PUK as follows:
|
|
|
|
user=Simon
|
|
key=`dd if=/dev/random bs=1 count=24 2>/dev/null | hexdump -v -e '/1 "%02X"'`
|
|
echo $key > yubico-internal-https-$user-key.txt
|
|
pin=`dd if=/dev/random bs=1 count=6 2>/dev/null | hexdump -v -e '/1 "%u"'|cut -c1-6`
|
|
echo $pin > yubico-internal-https-$user-pin.txt
|
|
puk=`dd if=/dev/random bs=1 count=6 2>/dev/null | hexdump -v -e '/1 "%u"'|cut -c1-8`
|
|
echo $puk > yubico-internal-https-$user-puk.txt
|
|
|
|
Configure a fresh NEO with these parameters as follows:
|
|
|
|
yubico-piv-tool -a set-mgm-key -n $key
|
|
yubico-piv-tool -k $key -a change-pin -P 123456 -N $pin
|
|
yubico-piv-tool -k $key -a change-puk -P 12345678 -N $puk
|
|
|
|
Creating a Sub-CA
|
|
-----------------
|
|
|
|
This step is parametrized with the name of the YubiKey NEO user. This
|
|
means we will have one Sub-CA for every person authorized to sign
|
|
certificates in our CA.
|
|
|
|
user=Simon
|
|
|
|
We first need to load the management key and PIN code from the
|
|
previous section.
|
|
|
|
key=`cat yubico-internal-https-$user-key.txt`
|
|
pin=`cat yubico-internal-https-$user-pin.txt`
|
|
|
|
Generate the private key:
|
|
|
|
openssl genrsa -out yubico-internal-https-subca-$user-key.pem 2048
|
|
|
|
Generate the Sub-CA certificate request:
|
|
|
|
cat>yubico-internal-https-subca-$user-csr.conf<<EOF
|
|
[ req ]
|
|
distinguished_name = req_distinguished_name
|
|
prompt = no
|
|
[ req_distinguished_name ]
|
|
CN=Yubico Internal HTTPS $user Sub-CA
|
|
EOF
|
|
openssl req -sha256 -new -config yubico-internal-https-subca-$user-csr.conf -key yubico-internal-https-subca-$user-key.pem -nodes -out yubico-internal-https-subca-$user-csr.pem
|
|
|
|
Generate the Sub-CA certificate:
|
|
|
|
cat>yubico-internal-https-subca-$user-crt.conf<<EOF
|
|
basicConstraints = critical, CA:true, pathlen:0
|
|
keyUsage=critical, keyCertSign
|
|
EOF
|
|
openssl x509 -sha256 -CA yubico-internal-https-ca-crt.pem -CAkey yubico-internal-https-ca-key.pem -req -in yubico-internal-https-subca-$user-csr.pem -extfile yubico-internal-https-subca-$user-crt.conf -out yubico-internal-https-subca-$user-crt.pem
|
|
echo 00 > yubico-internal-https-subca-$user-crt.srl
|
|
|
|
You may inspect the newly generated EE cert with this command:
|
|
|
|
openssl x509 -text < yubico-internal-https-subca-$user-crt.pem
|
|
|
|
Import Sub-CA key to NEO:
|
|
|
|
yubico-piv-tool -k $key -a import-key -s 9c < yubico-internal-https-subca-$user-key.pem
|
|
|
|
Import Sub-CA cert to NEO:
|
|
|
|
yubico-piv-tool -k $key -a import-certificate -s 9c < yubico-internal-https-subca-$user-crt.pem
|
|
|
|
Creating End-Entity Certificates
|
|
--------------------------------
|
|
|
|
This step is parametrized with the hostname, and the name of the
|
|
Sub-CA used to sign the EE, so set it first:
|
|
|
|
host=munin
|
|
user=Simon
|
|
|
|
We first need to load the PIN code from the previous section.
|
|
|
|
pin=`cat yubico-internal-https-$user-pin.txt`
|
|
|
|
Then generate a new private key and certificate request:
|
|
|
|
openssl genrsa -out yubico-internal-https-ee-$host-key.pem 2048
|
|
cat>yubico-internal-https-ee-$host-csr.conf<<EOF
|
|
[ req ]
|
|
distinguished_name = req_distinguished_name
|
|
prompt = no
|
|
[ req_distinguished_name ]
|
|
CN=$host.yubico.com
|
|
EOF
|
|
openssl req -sha256 -new -config yubico-internal-https-ee-$host-csr.conf -key yubico-internal-https-ee-$host-key.pem -nodes -out yubico-internal-https-ee-$host-csr.pem
|
|
|
|
Then sign the certificate using the NEO:
|
|
|
|
cat>yubico-internal-https-ee-$host-crt.conf<<EOF
|
|
basicConstraints = critical,CA:false
|
|
keyUsage=critical,digitalSignature,keyEncipherment
|
|
extendedKeyUsage=critical,serverAuth
|
|
subjectAltName=critical,DNS:$host.yubico.com
|
|
EOF
|
|
openssl << EOF
|
|
engine dynamic -pre SO_PATH:/usr/lib/engines/engine_pkcs11.so -pre ID:pkcs11 -pre NO_VCHECK:1 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so -pre VERBOSE
|
|
x509 -engine pkcs11 -CAkeyform engine -CAkey slot_1-id_2 -sha256 -CA yubico-internal-https-subca-$user-crt.pem -req -passin pass:$pin -in yubico-internal-https-ee-$host-csr.pem -extfile yubico-internal-https-ee-$host-crt.conf -out yubico-internal-https-ee-$host-crt.pem
|
|
EOF
|
|
|
|
You may inspect the newly generated EE cert with this command:
|
|
|
|
openssl x509 -text < yubico-internal-https-ee-$host-crt.pem
|