swtpm/samples/swtpm-create-tpmca
Stefan Berger dc2f5a673e samples: Remove usage of certtool from swtpm-create-tpmca
Remove the usage of certtool from swtpm-create-tpmca and use openssl
instead.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2026-05-06 07:56:13 -04:00

421 lines
9.6 KiB
Bash
Executable File

#!/usr/bin/env bash
FLAG_OVERWRITE=1
FLAG_TPM2=2
logit()
{
if [ -z "$LOGFILE" ]; then
echo "$@" >&1
else
echo "$@" >> "$LOGFILE"
fi
}
logerr()
{
if [ -z "$LOGFILE" ]; then
echo "Error: $*" >&2
else
echo "Error: $*" >> "$LOGFILE"
fi
}
# Get the size of a file in bytes
#
# @1: filename
function get_filesize()
{
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
stat -c%s "$1"
else
# OpenBSD
stat -f%z "$1"
fi
}
# Get the OpenSSL provider path for the given module searching a few well-known
# paths.
function get_provider_path()
{
local module="$1"
local p
for p in \
"/usr/lib64/ossl-modules" \
"/usr/lib/ossl-modules" \
"/usr/lib/$(uname -m)-linux-gnu/ossl-modules";
do
if [ -r "${p}/${module}" ]; then
echo "${p}"
return 0
fi
done
logerr "Could not find OpenSSL provider path for ${module} module."
return 1
}
# Create a config value by escaping the proper characters
#
# @param 1: The string to escape
function escape_pkcs11_url()
{
echo "${1//;/\\;}"
}
create_localca_cert() {
local flags=$1
local dir="$2"
local outfile="$3"
local owner="$4"
local pid="$5" # TPM2 parameter
local algorithm="$6" # RSA or EC-key signing
local cakey=${dir}/swtpm-localca-rootca-privkey.pem
local cacert=${dir}/swtpm-localca-rootca-cert.pem
local tpmkey=${dir}/swtpm-localca-tpmca-privkey.pem
local tpmpubkey=${dir}/swtpm-localca-tpmca-pubkey.pem
local tpmca=${dir}/swtpm-localca-tpmca-cert.pem
local pinfile=${dir}/pin
local passfile=${dir}/password
local tpmkeyurl
local msg output providerpath
export SWTPM_ROOTCA_PASSWORD
if ! [ -r "${cakey}" ] || ! [ -r "${cacert}" ]; then
# shellcheck disable=2086
if ! msg=$(openssl \
req \
-x509 \
-keyout "${cakey}" \
-newkey rsa:3072 \
${SWTPM_ROOTCA_PASSWORD:+-passout "env:SWTPM_ROOTCA_PASSWORD"} \
${SWTPM_ROOTCA_PASSWORD:--noenc} \
-out "${cacert}" \
-days 36500 \
-sha256 \
-subj "/CN=swtpm-localca-rootca" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign" \
2>&1);
then
logerr "Could not create root CA."
logerr "${msg}"
rm -f "${cakey}" "${passfile}"
return 1
fi
chmod 640 "${cakey}"
rm -f "${passfile}"
else
logit "Reusing existing root CA"
fi
rm -f "${tpmkey}" "${tpmpubkey}" "${tpmca}"
if [ $((flags & FLAG_TPM2)) -ne 0 ]; then
local tokenurl tpmkeyurl
local token="swtpm-tpmca-${pid}"
local label="${token}" # must be same
local keylabel="swtpm-tpmca-key"
local userpin="${SWTPM_PKCS11_PIN:-swtpm-tpmca}"
tokenurl=$(p11tool --list-tokens 2>&1 | \
grep -E ";token=${token}\$" | \
sed -n "s/.*URL: //p")
if [ -z "${tokenurl}" ]; then
if [ -z "${SWTPM_PKCS11_SO_PIN}" ]; then
logerr "The env. variable SWTPM_PKCS11_SO_PIN must be set to create token ${label}."
return 1
fi
if ! msg=$(tpm2_ptool addtoken \
--pid "${pid}" \
--sopin "${SWTPM_PKCS11_SO_PIN}" \
--userpin "${userpin}" \
--label "${label}" 2>&1);
then
logerr "Error: Could not create pkcs11 token"
logerr "${msg}"
return 1
fi
tokenurl=$(p11tool --list-tokens 2>&1 | \
grep -E ";token=${token}\$" | \
sed -n "s/.*URL: //p")
if [ -z "${tokenurl}" ]; then
logerr "Error: Could not get token URL for token '${token}'"
logerr "${msg}"
return 1
fi
if ! msg=$(tpm2_ptool config \
--key tcti \
--value tabrmd \
--label "${label}");
then
logerr "Error: Could not set config value for tcti key"
logerr "${msg}"
return 1
fi
fi
export GNUTLS_PIN="${userpin}"
# GNUTLS_SO_PIN not needed at this point
if msg="$(p11tool --login --list-keys "${tokenurl}" 2>&1)"; then
tpmkeyurl=$(echo "${msg}" | \
grep ";object=${keylabel}" | \
sed -n "s/.*URL: //p")
fi
if [ -z "${tpmkeyurl}" ]; then
if ! msg=$(tpm2_ptool addkey \
"--label=${label}" \
"--userpin=${userpin}" \
"--algorithm=${algorithm}" \
"--key-label=${keylabel}" \
--id 1 2>&1);
then
logerr "Error: Could not create create key under pkcs11 token ${token}"
logerr "${msg}"
return 1
fi
if ! msg="$(p11tool --login --list-keys "${tokenurl}" 2>&1)"; then
logerr "Error: Could not get TPM key URL for ${tokenurl}"
logerr "${msg}"
return 1
fi
tpmkeyurl=$(echo "${msg}" | \
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}"
if ! msg=$(p11tool --export-pubkey "${tpmkeyurl}" --login --outfile "${tpmpubkey}" 2>&1) || \
[ ! -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
logerr "TPM1.2 is not supported anymore."
return 1
fi
if ! providerpath=$(get_provider_path "pkcs11.so"); then
return 1
fi
# Write any PIN into a PIN file
echo "${SWTPM_PKCS11_PIN}" > "${pinfile}"
if ! msg=$(openssl \
req \
-provider-path "${providerpath}" \
-provider pkcs11 \
-x509 \
-key "${tpmkeyurl//%00/}${SWTPM_PKCS11_PIN:+?pin-source=${pinfile}}" \
-out "${tpmca}" \
-days 36500 \
-sha256 \
-CA "${cacert}" \
-CAkey "${cakey}" \
${SWTPM_ROOTCA_PASSWORD:+-passin "env:SWTPM_ROOTCA_PASSWORD"} \
-subj "/CN=swtpm-localca" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign" \
2>&1);
then
logerr "Could not create TPM CA"
logerr "${msg}"
rm -f "${pinfile}"
return 1
fi
rm -f "${pinfile}"
output="statedir = ${dir}
signingkey = $(escape_pkcs11_url "${tpmkeyurl//%00/}")
issuercert = ${tpmca}
certserial = ${dir}/certserial"
output+="$(echo -e "\nSWTPM_PKCS11_PIN = ${SWTPM_PKCS11_PIN}")"
# output+="$(echo -e "\nSWTPM_PKCS11_SO_PIN = ${SWTPM_PKCS11_SO_PIN}")"
if [ -n "${TPM_KEY_PASSWORD}" ]; then
output+="$(echo -e "\nsigningkey_password = ${TPM_KEY_PASSWORD}")"
fi
if [ -n "${outfile}" ]; then
echo "${output}" > "${outfile}"
chmod 640 "${outfile}"
fi
echo "${output}"
if [ "$(id -u)" -eq 0 ]; then
chown "${owner}:${group}" "${dir}"
if pushd "${dir}" &>/dev/null; then
chown "${owner}:${group}" ./*
popd &>/dev/null || return 1
fi
if [ -n "${outfile}" ]; then
chown "${owner}:${group}" "${outfile}"
fi
fi
return 0
} #create_localca_cert
usage() {
local flags=$2
cat << _EOF_
Create a TPM-based CA for signing EK and platform certificates.
Usage: $(basename "$1") [options]
THIS SCRIPT IS EXPERIMENTAL
The following options are supported:
--dir directory Directory where to write the CA files into; must not exist
unless --overwrite is passed
--overwrite Overwrite any data in an existing directory; tries to
reuse a root CA if one is found there
--key-password s Password for the newly created TPM key
--outfile file File to write the configuration to; if not passed it will be
written to stdout only
--owner owner The owner of the directory and the files; only set if this
script is run as root; recommended to be 'tss'
--group group The group owning the directory and the files;
recommended to be 'tss'
--tpm2 Setup a CA that uses a TPM 2.0
--algorithm <alg> Key algorithm for created TPM 2 CA. Default is rsa2048.
Possible values are: rsa2048, rsa3072, ecc256 or secp256r1,
ecc384 or secp384r1
--pid <pid> Pimary object Id used by tpm2_ptool; only valid if --tpm2
is used
--help, -h, -? Display this help screen and exit
The following environment variables are supported:
SWTPM_ROOTCA_PASSWORD The root CA's private key password
_EOF_
} #usage
main() {
local flags=0
local dir outfile owner group msg pid
local algorithm="rsa2048"
while [ $# -ne 0 ]; do
case "$1" in
--dir)
shift
dir="$1"
;;
--overwrite)
flags=$((flags | FLAG_OVERWRITE))
;;
--key-password)
shift
TPM_KEY_PASSWORD="$1"
;;
--outfile)
shift
outfile="$1"
;;
--owner)
shift
owner="$1"
;;
--group)
shift
group="$1"
;;
--tpm2)
flags=$((flags | FLAG_TPM2))
;;
--algorithm)
shift
algorithm="$1"
;;
--pid)
shift
pid="$1"
;;
--help|-h|-?)
usage "$0" "${flags}"
exit 0
;;
*)
logerr "Unsupported option $1"
exit 1
;;
esac
shift
done
if [ -z "${dir}" ]; then
logerr "Missing --dir option."
return 1
fi
# strip trailing '/' from dir
dir="$(echo "${dir}" | sed -n 's|[/]*$||p')"
if [ -d "${dir}" ] && [ $((flags & FLAG_OVERWRITE)) -eq 0 ]; then
logerr "Refusing to overwrite existing directory ${dir}."
return 1
fi
if [ $((flags & FLAG_TPM2)) -ne 0 ] && [ -z "${pid}" ]; then
logerr "--pid is required for TPM 2"
return 1
fi
if [ "$(id -u)" -eq 0 ]; then
if [ -n "${owner}" ]; then
if ! msg="$(id -u "${owner}" 2>&1)"; then
logerr "User ${owner} cannot be used: ${msg}"
return 1
fi
else
owner="root"
fi
if [ -n "${group}" ]; then
if ! msg="$(id -g "${group}" 2>&1)"; then
logerr "Group ${group} cannot be used: ${msg}"
return 1
fi
else
group="root"
fi
fi
if ! mkdir -p "${dir}"; then
logerr "Could not create directory ${dir}."
return 1
fi
if ! [[ "${algorithm}" =~ ^(rsa2048|rsa3072|ecc256|ecc384|secp256r1|secp384r1)$ ]]; then
logerr "Unsupported key algorithm for TPM CA '${algorithm}'. See --help."
return 1
else
case "${algorithm}" in
secp256r1) algorithm=ecc256;;
secp384r1) algorithm=ecc384;;
esac
fi
create_localca_cert "${flags}" "${dir}" "${outfile}" "${owner}" "${pid}" "${algorithm}"
return $?
} #main
main "$@"
exit $?