Certificate Authority in TPM
Experimenting with storing Certificate Authority keys in a TPM 2.0
I experimented with storing certificate authority, SSH, and GPG keys in a TPM. This post will be about the certificate authority.
I've been using easyrsa which is a fancy shell script around openssl. I first imported my existing certificate authority key+x509 cert into the tpm using tpm2_ptool, which worked:
#!/bin/bash
set -xe
# "Security Officer" PIN - for managing the keys
read -p "SO PIN " -s SOPIN
# User PIN - for using the keys
read -p "USER PIN " -s USERPIN
# create a place to put the keys with the label "ca"
tpm2_ptool addtoken --pid 1 --sopin $SOPIN --userpin $USERPIN --label ca
# import the private key
tpm2_ptool import --label=ca --privkey=ca.key --userpin=$USERPIN --algorithm rsa
# find the keyid, looks like CKA_ID: '1234567890...'
tpm2_ptool listobjects --label ca
read -p "Key ID/CKA_ID " KEYID
# import the x509 certificate
tpm2_ptool addcert --label ca --key-id $KEYID ca.crt
After running that, I can remove the ca.key
file as everything is stored as tpm sealed secrets in tpm2_ptool's database. Of course, if the tpm or the computer with the tpm fails, I'll lose access to my certificate authority key so I'll want to consider how to deal with that.
- CKA_CLASS: CKO_PRIVATE_KEY
CKA_ID:
- '66346332326666636362366166656465'
CKA_KEY_TYPE: CKK_RSA
CKA_LABEL: ''
id: 4
- CKA_CLASS: CKO_PUBLIC_KEY
CKA_ID:
- '66346332326666636362366166656465'
CKA_KEY_TYPE: CKK_RSA
CKA_LABEL: ''
id: 5
- CKA_CLASS: CKO_CERTIFICATE
CKA_ID:
- '66346332326666636362366166656465'
CKA_LABEL: ''
id: 6
But I ran into problems adapting openssl into using this key. I'm not sure if there's something I'm missing, but this is what I tried:
#!/bin/sh
set -xe
read -r -s -p "Enter User PIN: " uspin
echo
token_uri="$(p11tool --list-token-urls | grep token=gpg)"
private_uri="$(p11tool --list-privkeys --login --only-urls --set-pin=${uspin} ${token_uri})"
openssl x509 -req -engine pkcs11 -in client.req -out client.crt -CA ca.crt -CAkey ${private_uri} -CAkeyform engine
The error message it gave me:
The private key was not found at: pkcs11:model=AMD%00%00%00%00%00%00%00%00%00%00%00%00%00;manufacturer=AMD;serial=0000000000000000;token=gpg;id=%38%34%31%37%62%62%32%62%34%37%61%38%30%66%66%30;object=gpg;type=private
PKCS11_get_private_key returned NULL
Could not find CA private key from org.openssl.engine:pkcs11:pkcs11:model=AMD%00%00%00%00%00%00%00%00%00%00%00%00%00;manufacturer=AMD;serial=0000000000000000;token=gpg;id=%38%34%31%37%62%62%32%62%34%37%61%38%30%66%66%30;object=gpg;type=private
40578BE2C37F0000:error:03000096:digital envelope routines:fromdata_init:operation not supported for this keytype:crypto/evp/pmeth_gn.c:354:
40578BE2C37F0000:error:41800005:PKCS#11 module:ERR_CKR_error:General Error:p11_slot.c:208:
40578BE2C37F0000:error:13000080:engine routines:ENGINE_load_private_key:failed loading private key:crypto/engine/eng_pkey.c:79:
Searching for relevant bug reports, I see this: https://github.com/tpm2-software/tpm2-pkcs11/issues/766
Which suggests that openssl version 3+ needs to use -provider tpm2
instead of -engine pkcs11
Adapting my shell script:
#!/bin/bash
KEYID=6
read -p "userpin " -s USERPIN
auth_rsa0=$(tpm2_ptool export --id $KEYID --format pem --userpin $USERPIN | awk '/^object-auth:/ {print $2}')
openssl x509 -req -provider tpm2 -in client.req -out client.crt -CA ~/ca/pki/ca.crt -CAkey ${KEYID}.pem -passin "pass:$auth_rsa0"
rm ${KEYID}.pem
It's a bit awkward to need to export the key to a different format, but it works.
Next I went looking for alternatives to easyrsa and found "xca". It discovered my already imported CA key and certificate in the Token -> Manage Security Token menu:

And I was also able to import all my existing signed certificates:

Generating a new certificate just worked, and it's nicer to work with compared to easyrsa. I'll be using xca instead now.