Skip to content

Commit

Permalink
samples: Extend script to create a CA using a TPM 2.0 for signing
Browse files Browse the repository at this point in the history
Extend the script that creates a CA that uses a TPM 2.0 for signing.
For this we have to create tokens using the TPM 2.0 pkcs11 module's
tpm2_ptool and can then use the p11tool for creating keys.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
  • Loading branch information
stefanberger committed Nov 9, 2018
1 parent 305bc4e commit 35e976d
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 55 deletions.
43 changes: 41 additions & 2 deletions man/man8/swtpm-create-tpmca.8
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "swtpm-create-tpmca 8"
.TH swtpm-create-tpmca 8 "2018-10-17" "swtpm" ""
.TH swtpm-create-tpmca 8 "2018-10-30" "swtpm" ""
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
Expand Down Expand Up @@ -209,6 +209,14 @@ The hostname where tcsd is running on. The default hostname is 'localhost'.
.IX Item "-tss-tcsd-port"
The \s-1TCP\s0 port on which tcsd is listening for messages. The default port is
30003.
.IP "\fB\-\-tpm2\fR" 4
.IX Item "--tpm2"
The \s-1TPM\s0 to use for signing the certificates is a \s-1TPM 2\s0 and Intel's \s-1TSS 2.0\s0
stack must be running (tpm2\-abrmd) along with the \s-1TPM 2.0 PKCS11\s0 module.
The \s-1TPM 2.0 PKCS11\s0 module must have been initialized using the tpm2_ptool.
.Sp
The environment variables \s-1GNUTLS_PIN\s0 and \s-1GNUTLS_SO_PIN\s0 should be set to hold
the PINs. If they are not set the default \s-1PIN\s0's 'swtpm\-tpmca' will be used.
.IP "\fB\-help, \-h, \-?\fR" 4
.IX Item "-help, -h, -?"
Display the help screen and exit.
Expand All @@ -219,6 +227,9 @@ into /var/lib/swtpm\-localca and the swtpm-localca configuration to
/etc/swtpm\-localca.conf. It can then be used for signing certificates of
newly created \fBswtpm\fR TPMs.
.PP
If the host's \s-1TPM\s0 is a \s-1TPM 1.2,\s0 we need to start the tcsd first and can
then create the \s-1TPM\s0 key and \s-1TPM CA\s0 certificate:
.PP
.Vb 10
\& #> sudo systemctl start tcsd
\& #> sudo /usr/share/swtpm/swtpm\-create\-tpmca \e
Expand All @@ -237,10 +248,38 @@ newly created \fBswtpm\fR TPMs.
\& signingkey_password = password
\& parentkey_password = password
.Ve
.PP
Alternatively, if the host's \s-1TPM\s0 is a \s-1TPM 2\s0 and Intel's \s-1TPM 2\s0 stack is
installed, we need to start tpm2\-abrmd first and can then create the \s-1TPM\s0 key
and \s-1TPM CA\s0 certificate:
.PP
.Vb 11
\& #> sudo systemctl start tpm2\-abrmd
\& #> sudo GNUTLS_PIN=mypin GNUTLS_SO_PIN=mysopin /usr/share/swtpm/swtpm\-create\-tpmca \e
\& \-\-dir /var/lib/swtpm\-localca \e
\& \-\-overwrite \e
\& \-\-outfile /etc/swtpm\-localca.conf \e
\& \-\-group tss \e
\& \-\-tpm2
\& statedir = /var/lib/swtpm\-localca
\& signingkey = pkcs11:model=TPM2%20PKCS%2311\e;manufacturer=Intel\e;serial=0000000000000000\e;token=swtpm\-tpmca\e;id=%31\e;object=swtpm\-tpmca\-key\e;type=private
\& issuercert = /var/lib/swtpm\-localca/swtpm\-localca\-tpmca\-cert.pem
\& certserial = /var/lib/swtpm\-localca/certserial
.Ve
.PP
To test either one of the above \s-1TPM\s0 CAs, run the following command:
.PP
.Vb 5
\& #> /usr/share/swtpm/swtpm\-localca \e
\& \-\-type ek \-\-ek x=11,y=13 \e
\& \-\-dir /tmp \-\-vmid test \-\-tpm2 \e
\& \-\-tpm\-spec\-family 2.0 \-\-tpm\-spec\-revision 146 \-\-tpm\-spec\-level 00 \e
\& \-\-tpm\-model swtpm \-\-tpm\-version 20170101 \-\-tpm\-manufacturer IBM
.Ve
.SH "KNOWN ISSUES"
.IX Header "KNOWN ISSUES"
The interaction of GnuTLS certtool with the \s-1TPM TCSD\s0 daemon may cause so
many \s-1TPM\s0 (key) authentication failures, that the \s-1TPM\s0 refuses to accept any
many \s-1TPM\s0 (key) authentication failures that the \s-1TPM\s0 refuses to accept any
more authenticated commands until the \s-1TPM\s0's owner sends it the
TPM_ORD_ResetLockValue command. The reason for this is that certtool first
tries to use 20 zero bytes for the \s-1SRK\s0 password and only then prompts for
Expand Down
39 changes: 38 additions & 1 deletion man/man8/swtpm-create-tpmca.pod
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ The hostname where tcsd is running on. The default hostname is 'localhost'.
The TCP port on which tcsd is listening for messages. The default port is
30003.

=item B<--tpm2>

The TPM to use for signing the certificates is a TPM 2 and Intel's TSS 2.0
stack must be running (tpm2-abrmd) along with the TPM 2.0 PKCS11 module.
The TPM 2.0 PKCS11 module must have been initialized using the tpm2_ptool.

The environment variables GNUTLS_PIN and GNUTLS_SO_PIN should be set to hold
the PINs. If they are not set the default PIN's 'swtpm-tpmca' will be used.

=item B<-help, -h, -?>

Display the help screen and exit.
Expand All @@ -98,6 +107,9 @@ into /var/lib/swtpm-localca and the swtpm-localca configuration to
/etc/swtpm-localca.conf. It can then be used for signing certificates of
newly created B<swtpm> TPMs.

If the host's TPM is a TPM 1.2, we need to start the tcsd first and can
then create the TPM key and TPM CA certificate:

#> sudo systemctl start tcsd
#> sudo /usr/share/swtpm/swtpm-create-tpmca \
--dir /var/lib/swtpm-localca \
Expand All @@ -115,10 +127,35 @@ newly created B<swtpm> TPMs.
signingkey_password = password
parentkey_password = password


Alternatively, if the host's TPM is a TPM 2 and Intel's TPM 2 stack is
installed, we need to start tpm2-abrmd first and can then create the TPM key
and TPM CA certificate:

#> sudo systemctl start tpm2-abrmd
#> sudo GNUTLS_PIN=mypin GNUTLS_SO_PIN=mysopin /usr/share/swtpm/swtpm-create-tpmca \
--dir /var/lib/swtpm-localca \
--overwrite \
--outfile /etc/swtpm-localca.conf \
--group tss \
--tpm2
statedir = /var/lib/swtpm-localca
signingkey = pkcs11:model=TPM2%20PKCS%2311\;manufacturer=Intel\;serial=0000000000000000\;token=swtpm-tpmca\;id=%31\;object=swtpm-tpmca-key\;type=private
issuercert = /var/lib/swtpm-localca/swtpm-localca-tpmca-cert.pem
certserial = /var/lib/swtpm-localca/certserial

To test either one of the above TPM CAs, run the following command:

#> /usr/share/swtpm/swtpm-localca \
--type ek --ek x=11,y=13 \
--dir /tmp --vmid test --tpm2 \
--tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 00 \
--tpm-model swtpm --tpm-version 20170101 --tpm-manufacturer IBM

=head1 KNOWN ISSUES

The interaction of GnuTLS certtool with the TPM TCSD daemon may cause so
many TPM (key) authentication failures, that the TPM refuses to accept any
many TPM (key) authentication failures that the TPM refuses to accept any
more authenticated commands until the TPM's owner sends it the
TPM_ORD_ResetLockValue command. The reason for this is that certtool first
tries to use 20 zero bytes for the SRK password and only then prompts for
Expand Down
181 changes: 137 additions & 44 deletions samples/swtpm-create-tpmca
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ FLAG_OVERWRITE=1
FLAG_REGISTER_KEY=2
FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN=4
FLAG_SRK_WELL_KNOWN=8
FLAG_TPM2=16

TSS_TCSD_HOSTNAME_DEFAULT=localhost
TSS_TCSD_PORT_DEFAULT=30003
Expand Down Expand Up @@ -39,6 +40,14 @@ function get_filesize()
fi
}

# Create a config value by escaping the proper characters
#
# @param 1: The string to escape
function escape_pkcs11_url()
{
echo "$1" | sed 's/;/\\;/g'
}

# Use expect for automating the interaction with the tpmtool
#
# @param 1...: parameters to pass to tpmtool command line
Expand Down Expand Up @@ -86,7 +95,7 @@ create_localca_cert() {
local tpmpubkey=${dir}/swtpm-localca-tpmca-pubkey.pem
local tpmca=${dir}/swtpm-localca-tpmca-cert.pem
local template=${dir}/template
local tpmkeyurl params
local tpmkeyurl
local msg output

if ! [ -r "${cakey}" ] || ! [ -r ${cacert} ]; then
Expand Down Expand Up @@ -131,52 +140,124 @@ create_localca_cert() {

rm -f ${tpmkey} ${tpmpubkey} ${tpmca}

if [ $((flags & FLAG_SRK_WELL_KNOWN)) -ne 0 ]; then
unset GNUTLS_PIN
params="--srk-well-known"
else
export GNUTLS_PIN=${TPM_SRK_PASSWORD}
fi

if [ $((flags & FLAG_REGISTER_KEY)) -ne 0 ]; then
msg="$(run_tpmtool --generate-rsa --signing --register ${params})"
if [ $? -ne 0 ]; then
logerr "Could not generate registered signing key with tpmtool"
logerr "${msg}"
return 1
if [ $((flags & FLAG_TPM2)) -ne 0 ]; then
local tokenurl tpmkeyurl
local token="swtpm-tpmca"
local label="swtpm-tpmca"
local keylabel="swtpm-tpmca-key"
local userpin=${GNUTLS_PIN:-swtpm-tpmca}
local sopin=${GNUTLS_SO_PIN:-swtpm-tpmca}

export GNUTLS_PIN=${userpin}

tokenurl=$(p11tool --list-tokens 2>&1 | \
grep "token=${token}" | \
sed -n "s/.*URL: //p")
if [ -z "${tokenurl}" ]; then
msg=$(tpm2_ptool.py addtoken \
--pid 1 \
--sopin=${sopin} \
--userpin=${userpin} \
--label=${label} 2>&1)
if [ $? -ne 0 ]; then
logerr "Error: Could not create pkcs11 token"
logerr "${msg}"
return 1
fi
tokenurl=$(p11tool --list-tokens 2>&1 | \
grep ";token=${token}" | \
sed -n "s/.*URL: //p")
if [ -z "${tokenurl}" ]; then
logerr "Error: Could not get token URL for token '${token}'"
logerr "${msg}"
fi
fi
tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p')

tpmkeyurl=$(p11tool --list-keys ${tokenurl} 2>&1 | \
grep ";object=${keylabel}" | \
sed -n "s/.*URL: //p")
if [ -z "${tpmkeyurl}" ]; then
logerr "Could not parse tpmkey URL"
msg=$(tpm2_ptool.py addkey \
--label=${label} \
--userpin=${userpin} \
--algorithm=rsa2048 \
--key-label=${keylabel} \
--id 1 2>&1)
if [ $? -ne 0 ]; then
logerr "Error: Could not create create key under pkcs11 token ${token}"
logerr "${msg}"
return 1
fi
tpmkeyurl=$(p11tool --list-keys ${tokenurl} 2>&1 | \
grep ";object=${keylabel}" | \
sed -n "s/.*URL: //p")
if [ -z "${tpmkeyurl}" ]; then
logerr "Error: Could not get TPM key URL for ${tokenurl}"
logerr "${msg}"
return 1
fi
fi
rm -f "${tpmpubkey}"

msg=$(p11tool --export-pubkey ${tpmkeyurl} --login --outfile ${tpmpubkey} 2>&1)
if [ $? -ne 0 ] || \
[ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then
logerr "Error: Could not get TPM public key"
logerr "${msg}"
rm -f "${tpmkey}" "${tpmpubkey}"
return 1
fi
else
rm -f "${tpmkey}"
msg="$(run_tpmtool --generate-rsa --signing --outfile "${tpmkey}" ${params})"
if [ $? -ne 0 ]; then
logerr "Could not create signing key with tpmtool"
logerr "${msg}"
rm -f "${tpmkey}"
return 1
local params=""

if [ $((flags & FLAG_SRK_WELL_KNOWN)) -ne 0 ]; then
unset GNUTLS_PIN
params="--srk-well-known"
else
export GNUTLS_PIN=${TPM_SRK_PASSWORD}
fi
if [ ! -r "${tpmkey}" ] || [ $(get_filesize "${tpmkey}") -eq 0 ]; then
logerr "The TPM key file ${tpmkey} was not written properly"

if [ $((flags & FLAG_REGISTER_KEY)) -ne 0 ]; then
msg="$(run_tpmtool --generate-rsa --signing --register ${params})"
if [ $? -ne 0 ]; then
logerr "Could not generate registered signing key with tpmtool"
logerr "${msg}"
return 1
fi
tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p')
if [ -z "${tpmkeyurl}" ]; then
logerr "Could not parse tpmkey URL"
logerr "${msg}"
return 1
fi
else
rm -f "${tpmkey}"
return 1
msg="$(run_tpmtool --generate-rsa --signing --outfile "${tpmkey}" ${params})"
if [ $? -ne 0 ]; then
logerr "Could not create signing key with tpmtool"
logerr "${msg}"
rm -f "${tpmkey}"
return 1
fi
if [ ! -r "${tpmkey}" ] || [ $(get_filesize "${tpmkey}") -eq 0 ]; then
logerr "The TPM key file ${tpmkey} was not written properly"
logerr "${msg}"
rm -f "${tpmkey}"
return 1
fi
chmod 640 ${tpmkey}
tpmkeyurl=tpmkey:file=${tpmkey}
fi
chmod 640 ${tpmkey}
tpmkeyurl=tpmkey:file=${tpmkey}
fi

rm -f "${tpmpubkey}"
msg=$(run_tpmtool --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}" ${params})
if [ $? -ne 0 ] || \
[ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then
logerr "Error: Could not get TPM public key"
logerr "${msg}"
rm -f "${tpmkey}" "${tpmpubkey}"
return 1
rm -f "${tpmpubkey}"
msg=$(run_tpmtool --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}" ${params})
if [ $? -ne 0 ] || \
[ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then
logerr "Error: Could not get TPM public key"
logerr "${msg}"
rm -f "${tpmkey}" "${tpmpubkey}"
return 1
fi
fi

echo "cn=swtpm-localca" > ${template}
Expand All @@ -202,12 +283,17 @@ create_localca_cert() {
fi

output="statedir = ${dir}
signingkey = ${tpmkeyurl}
signingkey = $(escape_pkcs11_url ${tpmkeyurl})
issuercert = ${tpmca}
certserial = ${dir}/certserial
TSS_TCSD_HOSTNAME = ${TSS_TCSD_HOSTNAME}
TSS_TCSD_PORT = ${TSS_TCSD_PORT}"
certserial = ${dir}/certserial"

if [ $((flags & FLAG_TPM2)) -eq 0 ]; then
output+="$(echo -e "\nTSS_TCSD_HOSTNAME = ${TSS_TCSD_HOSTNAME}")"
output+="$(echo -e "\nTSS_TCSD_PORT = ${TSS_TCSD_PORT}")"
else
output+="$(echo -e "\nGNUTLS_PIN = ${GNUTLS_PIN}")"
output+="$(echo -e "\nGNUTLS_SO_PIN = ${GNUTLS_SO_PIN}")"
fi
if [ -n "${TPM_KEY_PASSWORD}" ]; then
output+="$(echo -e "\nsigningkey_password = ${TPM_KEY_PASSWORD}")"
fi
Expand Down Expand Up @@ -264,7 +350,8 @@ The following options are supported:
--overwrite Overwrite any data in an existing directory; tries to
reuse a root CA if one is found there
--register Create a registered TPM 1.2 key rather than a file that
contains the key
contains the key; this option has no effect if --tpm2 is
used
--key-password s Password for the newly created TPM key; required if
--register is not passed
Note: use the same as the --srk-password (bug in certtool)
Expand All @@ -280,6 +367,7 @@ The following options are supported:
on; default is '${TSS_TCSD_HOSTNAME_DEFAULT}'
--tss-tcsd-port p The TCP port on which tcsd is listening for connections;
default is ${TSS_TCSD_PORT_DEFAULT}
--tpm2 Setup a CA that uses a TPM2 rather than a TPM 1.2
--help, -h, -? Display this help screen and exit
Expand Down Expand Up @@ -353,6 +441,9 @@ main() {
shift
TSS_TCSD_PORT="$1"
;;
--tpm2)
flags=$((flags | FLAG_TPM2))
;;
--help|-h|-?)
usage "$0" "${flags}"
exit 0
Expand All @@ -376,13 +467,15 @@ main() {
return 1
fi

if [ -z "${TPM_SRK_PASSWORD}" ] && \
if [ -z "${TPM_SRK_PASSWORD}" ] && [ $((flags & FLAG_TPM2)) -eq 0 ] &&
[ $((flags & FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN)) -eq 0 ]; then
logerr "SRK password must be provided"
return 1
fi

if [ -z "${TPM_KEY_PASSWORD}" ] && [ $((flags & FLAG_REGISTER_KEY)) -eq 0 ]; then
if [ -z "${TPM_KEY_PASSWORD}" ] && \
[ $((flags & FLAG_REGISTER_KEY)) -eq 0 ] && \
[ $((flags & FLAG_TPM2)) -eq 0 ]; then
logerr "Key password is required"
return 1
fi
Expand Down
Loading

0 comments on commit 35e976d

Please sign in to comment.