diff --git a/man/rules/meson.build b/man/rules/meson.build
index 7d2c62f5742..e76cb0223b5 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -992,6 +992,7 @@ manpages = [
'systemd-journald@.service',
'systemd-journald@.socket'],
''],
+ ['systemd-keyutil', '1', [], ''],
['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],
['systemd-machine-id-commit.service', '8', [], ''],
diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml
new file mode 100644
index 00000000000..99d4d903b4b
--- /dev/null
+++ b/man/systemd-keyutil.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+ systemd-keyutil
+ systemd
+
+
+
+ systemd-keyutil
+ 1
+
+
+
+ systemd-keyutil
+ Perform various operations on private keys and X.509 certificates
+
+
+
+
+ systemd-keyutil
+ OPTIONS
+ COMMAND
+
+
+
+
+ Description
+
+ systemd-keyutil can be used to perform various operations on private keys and
+ X.509 certificates.
+
+
+
+ Commands
+
+
+
+
+
+ Checks that we can load the private key and certificate specified with
+ and respectively.
+
+ As a side effect, if the private key is loaded from a PIN-protected hardware token, this
+ command can be used to cache the PIN in the kernel keyring. The
+ $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC and
+ $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE environment variables can be used to control
+ how long and in which kernel keyring the PIN is cached.
+
+
+
+
+
+
+ public
+
+ This commands prints the public key in PEM format extracted from either the
+ certificate given with or the private key given with
+ .
+
+
+
+
+
+
+
+ Options
+ The following options are understood:
+
+
+
+
+
+
+
+
+ Set the private key and certificate to use. The
+ option takes a path to a PEM encoded X.509 certificate or a URI that's passed to the OpenSSL provider
+ configured with . The
+ takes one of file or provider, with the latter being followed
+ by a specific provider identifier, separated with a colon, e.g. provider:pkcs11.
+ The option can take a path or a URI that will be passed to the
+ OpenSSL engine or provider, as specified by as a
+ type:name tuple, such as engine:pkcs11.
+
+
+
+
+
+
+
+
+
+
+ See Also
+
+ systemd-sbsign1
+ systemd-measure1
+
+
+
diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml
index 5ca373f1814..c7e5a5e9e21 100644
--- a/man/systemd-measure.xml
+++ b/man/systemd-measure.xml
@@ -104,16 +104,6 @@
-
-
- pcrpkey
-
- This commands prints the public key either given with ,
- or extracted from the certificate given with or the private key given
- with .
-
-
-
diff --git a/man/systemd-sbsign.xml b/man/systemd-sbsign.xml
index 1248377845f..57b685f8c38 100644
--- a/man/systemd-sbsign.xml
+++ b/man/systemd-sbsign.xml
@@ -49,22 +49,6 @@
-
-
-
-
- Checks that we can load the private key specified with
- .
-
- As a side effect, if the private key is loaded from a PIN-protected hardware token, this
- command can be used to cache the PIN in the kernel keyring. The
- $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC and
- $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE environment variables can be used to control
- how long and in which kernel keyring the PIN is cached.
-
-
-
-
diff --git a/meson.build b/meson.build
index 6b62dfa052d..fbab9300c54 100644
--- a/meson.build
+++ b/meson.build
@@ -2377,6 +2377,7 @@ subdir('src/integritysetup')
subdir('src/journal')
subdir('src/journal-remote')
subdir('src/kernel-install')
+subdir('src/keyutil')
subdir('src/locale')
subdir('src/login')
subdir('src/machine')
@@ -2698,7 +2699,7 @@ endif
mkosi_depends = public_programs
-foreach executable : ['systemd-journal-remote', 'systemd-measure']
+foreach executable : ['systemd-journal-remote', 'systemd-measure', 'systemd-sbsign', 'systemd-keyutil']
if executable in executables_by_name
mkosi_depends += [executables_by_name[executable]]
endif
diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c
new file mode 100644
index 00000000000..70176c76c73
--- /dev/null
+++ b/src/keyutil/keyutil.c
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+#include
+
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "build.h"
+#include "fd-util.h"
+#include "main-func.h"
+#include "memstream-util.h"
+#include "openssl-util.h"
+#include "parse-argument.h"
+#include "pretty-print.h"
+#include "verbs.h"
+
+static char *arg_private_key = NULL;
+static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
+static char *arg_private_key_source = NULL;
+static char *arg_certificate = NULL;
+static char *arg_certificate_source = NULL;
+static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+
+STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
+
+static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-keyutil", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] COMMAND ...\n"
+ "\n%5$sPerform various operations on private keys and certificates.%6$s\n"
+ "\n%3$sCommands:%4$s\n"
+ " validate Load and validate the given certificate and private key\n"
+ " public Extract a public key\n"
+ "\n%3$sOptions:%4$s\n"
+ " -h --help Show this help\n"
+ " --version Print version\n"
+ " --private-key=KEY Private key in PEM format\n"
+ " --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
+ " Specify how to use KEY for --private-key=. Allows\n"
+ " an OpenSSL engine/provider to be used for signing\n"
+ " --certificate=PATH|URI\n"
+ " PEM certificate to use for signing, or a provider\n"
+ " specific designation if --certificate-source= is used\n"
+ " --certificate-source=file|provider:PROVIDER\n"
+ " Specify how to interpret the certificate from\n"
+ " --certificate=. Allows the certificate to be loaded\n"
+ " from an OpenSSL provider\n"
+ "\nSee the %2$s for details.\n",
+ program_invocation_short_name,
+ link,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_PRIVATE_KEY,
+ ARG_PRIVATE_KEY_SOURCE,
+ ARG_CERTIFICATE,
+ ARG_CERTIFICATE_SOURCE,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
+ { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
+ { "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
+
+ case 'h':
+ help(0, NULL, NULL);
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_PRIVATE_KEY:
+ r = free_and_strdup_warn(&arg_private_key, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_PRIVATE_KEY_SOURCE:
+ r = parse_openssl_key_source_argument(
+ optarg,
+ &arg_private_key_source,
+ &arg_private_key_source_type);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_CERTIFICATE:
+ r = free_and_strdup_warn(&arg_certificate, optarg);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_CERTIFICATE_SOURCE:
+ r = parse_openssl_certificate_source_argument(
+ optarg,
+ &arg_certificate_source,
+ &arg_certificate_source_type);
+ if (r < 0)
+ return r;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (arg_private_key_source && !arg_certificate)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "When using --private-key-source=, --certificate= must be specified.");
+
+ return 1;
+}
+
+static int verb_validate(int argc, char *argv[], void *userdata) {
+ _cleanup_(X509_freep) X509 *certificate = NULL;
+ _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
+ int r;
+
+ if (!arg_certificate)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No certificate specified, use --certificate=");
+
+ if (!arg_private_key)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No private key specified, use --private-key=.");
+
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+ if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+ r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+ }
+
+ r = openssl_load_private_key(
+ arg_private_key_source_type,
+ arg_private_key_source,
+ arg_private_key,
+ &(AskPasswordRequest) {
+ .id = "keyutil-private-key-pin",
+ .keyring = arg_private_key,
+ .credential = "keyutil.private-key-pin",
+ },
+ &private_key,
+ &ui);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+
+ puts("OK");
+ return 0;
+}
+
+static int verb_public(int argc, char *argv[], void *userdata) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
+ int r;
+
+ if (arg_certificate) {
+ _cleanup_(X509_freep) X509 *certificate = NULL;
+
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+ public_key = X509_get_pubkey(certificate);
+ if (!public_key)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EIO),
+ "Failed to extract public key from certificate %s.",
+ arg_certificate);
+
+ } else if (arg_private_key) {
+ _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
+
+ if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+ r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+ }
+
+ r = openssl_load_private_key(
+ arg_private_key_source_type,
+ arg_private_key_source,
+ arg_private_key,
+ &(AskPasswordRequest) {
+ .id = "keyutil-private-key-pin",
+ .keyring = arg_private_key,
+ .credential = "keyutil.private-key-pin",
+ },
+ &private_key,
+ &ui);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+
+ _cleanup_(memstream_done) MemStream m = {};
+ FILE *tf = memstream_init(&m);
+ if (!tf)
+ return log_oom();
+
+ if (i2d_PUBKEY_fp(tf, private_key) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to extract public key from private key file '%s'.", arg_private_key);
+
+ fflush(tf);
+ rewind(tf);
+
+ if (!d2i_PUBKEY_fp(tf, &public_key))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "One of --certificate=, or --private-key= must be specified");
+
+ if (PEM_write_PUBKEY(stdout, public_key) == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key to stdout");
+
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "validate", VERB_ANY, 1, 0, verb_validate },
+ { "public", VERB_ANY, 1, 0, verb_public },
+ {}
+ };
+ int r;
+
+ log_setup();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/keyutil/meson.build b/src/keyutil/meson.build
new file mode 100644
index 00000000000..956f6039895
--- /dev/null
+++ b/src/keyutil/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+ libexec_template + {
+ 'name' : 'systemd-keyutil',
+ 'conditions' : [
+ 'HAVE_OPENSSL',
+ ],
+ 'sources' : files('keyutil.c'),
+ 'dependencies' : libopenssl,
+ },
+]
diff --git a/src/measure/measure.c b/src/measure/measure.c
index ac294d28b36..979426c18fd 100644
--- a/src/measure/measure.c
+++ b/src/measure/measure.c
@@ -77,7 +77,6 @@ static int help(int argc, char *argv[], void *userdata) {
" status Show current PCR values\n"
" calculate Calculate expected PCR values\n"
" sign Calculate and sign expected PCR values\n"
- " pcrpkey Extract the PCR public key\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
@@ -1174,100 +1173,12 @@ static int verb_status(int argc, char *argv[], void *userdata) {
return 0;
}
-static int verb_pcrpkey(int argc, char *argv[], void *userdata) {
- _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
- int r;
-
- if (arg_public_key) {
- _cleanup_fclose_ FILE *public_keyf = NULL;
-
- public_keyf = fopen(arg_public_key, "re");
- if (!public_keyf)
- return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
-
- public_key = PEM_read_PUBKEY(public_keyf, NULL, NULL, NULL);
- if (!public_key)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
-
- } else if (arg_certificate) {
- _cleanup_(X509_freep) X509 *certificate = NULL;
-
- if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
- r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
- if (r < 0)
- return r;
- }
-
- r = openssl_load_x509_certificate(
- arg_certificate_source_type,
- arg_certificate_source,
- arg_certificate,
- &certificate);
- if (r < 0)
- return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
-
- public_key = X509_get_pubkey(certificate);
- if (!public_key)
- return log_error_errno(
- SYNTHETIC_ERRNO(EIO),
- "Failed to extract public key from certificate %s.",
- arg_certificate);
-
- } else if (arg_private_key) {
- _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
- _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
-
- if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
- r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
- if (r < 0)
- return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
- }
-
- r = openssl_load_private_key(
- arg_private_key_source_type,
- arg_private_key_source,
- arg_private_key,
- &(AskPasswordRequest) {
- .id = "measure-private-key-pin",
- .keyring = arg_private_key,
- .credential = "measure.private-key-pin",
- },
- &private_key,
- &ui);
- if (r < 0)
- return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
-
- _cleanup_(memstream_done) MemStream m = {};
- FILE *tf = memstream_init(&m);
- if (!tf)
- return log_oom();
-
- if (i2d_PUBKEY_fp(tf, private_key) != 1)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to extract public key from private key file '%s'.", arg_private_key);
-
- fflush(tf);
- rewind(tf);
-
- if (!d2i_PUBKEY_fp(tf, &public_key))
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "One of --public-key=, --certificate=, or --private-key= must be specified");
-
- if (PEM_write_PUBKEY(stdout, public_key) == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key to stdout");
-
- return 0;
-}
-
static int measure_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "calculate", VERB_ANY, 1, 0, verb_calculate },
{ "sign", VERB_ANY, 1, 0, verb_sign },
- { "pcrpkey", VERB_ANY, 1, 0, verb_pcrpkey },
{}
};
diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c
index d65f28b4c4e..81970a7302b 100644
--- a/src/sbsign/sbsign.c
+++ b/src/sbsign/sbsign.c
@@ -45,7 +45,6 @@ static int help(int argc, char *argv[], void *userdata) {
"\n%5$sSign binaries for EFI Secure Boot%6$s\n"
"\n%3$sCommands:%4$s\n"
" sign EXEFILE Sign the given binary for EFI Secure Boot\n"
- " validate-key Load and validate the given certificate and private key\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
@@ -498,63 +497,10 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
return 0;
}
-static int verb_validate_key(int argc, char *argv[], void *userdata) {
- _cleanup_(X509_freep) X509 *certificate = NULL;
- _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
- _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
- int r;
-
- if (!arg_certificate)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "No certificate specified, use --certificate=");
-
- if (!arg_private_key)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "No private key specified, use --private-key=.");
-
- if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
- r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
- if (r < 0)
- return r;
- }
-
- r = openssl_load_x509_certificate(
- arg_certificate_source_type,
- arg_certificate_source,
- arg_certificate,
- &certificate);
- if (r < 0)
- return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
-
- if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
- r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
- if (r < 0)
- return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
- }
-
- r = openssl_load_private_key(
- arg_private_key_source_type,
- arg_private_key_source,
- arg_private_key,
- &(AskPasswordRequest) {
- .id = "sbsign-private-key-pin",
- .keyring = arg_private_key,
- .credential = "sbsign.private-key-pin",
- },
- &private_key,
- &ui);
- if (r < 0)
- return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
-
- puts("OK");
- return 0;
-}
-
static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "sign", 2, 2, 0, verb_sign },
- { "validate-key", VERB_ANY, 1, 0, verb_validate_key },
{}
};
int r;
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index 355e3f99f41..ae3e1d03b4a 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -1017,8 +1017,8 @@ def make_uki(opts: UkifyConfig) -> None:
pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
if pcrpkey is None:
- measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure')
- cmd = [measure_tool, 'pcrpkey']
+ measure_tool = find_tool('systemd-keyutil', '/usr/lib/systemd/systemd-keyutil')
+ cmd = [measure_tool, 'public']
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
# If we're using an engine or provider, the public key will be an X.509 certificate.
@@ -1026,11 +1026,11 @@ def make_uki(opts: UkifyConfig) -> None:
cmd += ['--certificate', opts.pcr_public_keys[0]]
if opts.certificate_provider:
cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
- else:
- cmd += ['--public-key', opts.pcr_public_keys[0]]
- print('+', shell_join(cmd))
- pcrpkey = subprocess.check_output(cmd)
+ print('+', shell_join(cmd))
+ pcrpkey = subprocess.check_output(cmd)
+ else:
+ pcrpkey = Path(opts.pcr_public_keys[0])
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
diff --git a/test/units/TEST-74-AUX-UTILS.keyutil.sh b/test/units/TEST-74-AUX-UTILS.keyutil.sh
new file mode 100755
index 00000000000..bbbbf9fd676
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.keyutil.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v /usr/lib/systemd/systemd-keyutil >/dev/null; then
+ echo "systemd-keyutil not found, skipping."
+ exit 0
+fi
+
+cat >/tmp/openssl.conf <