From 8ecb7deafa7b336045949fbcb955b756216effa3 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 12 Dec 2025 14:14:20 +0100 Subject: [PATCH 1/4] openssl-util: Fix logging --- src/shared/openssl-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 31d1347f7d..8849ec89c4 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1763,7 +1763,7 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) { _cleanup_(memstream_done) MemStream m = {}; FILE *tf = memstream_init(&m); if (!tf) - return log_oom(); + return -ENOMEM; if (i2d_PUBKEY_fp(tf, private_key) != 1) return -EIO; From 8440492e90ac2af277df4480e5256b9a398d5176 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 12 Dec 2025 14:16:53 +0100 Subject: [PATCH 2/4] openssl-util: Cast to const type --- src/shared/openssl-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 8849ec89c4..0e54ae527a 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1774,7 +1774,7 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) { if (r < 0) return r; - const unsigned char *t = (unsigned char*) buf; + const unsigned char *t = (const unsigned char*) buf; if (!d2i_PUBKEY(ret, &t, len)) return -EIO; From 9fc537437b4ba6d9a31fe51de5d6686aafa51c94 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 12 Dec 2025 14:26:19 +0100 Subject: [PATCH 3/4] keyutil: Rename public to extract-public --- man/systemd-keyutil.xml | 2 +- src/keyutil/keyutil.c | 13 +++++++------ src/ukify/ukify.py | 2 +- test/units/TEST-74-AUX-UTILS.keyutil.sh | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml index aaf760b948..47ab28a055 100644 --- a/man/systemd-keyutil.xml +++ b/man/systemd-keyutil.xml @@ -56,7 +56,7 @@ - public + extract-public This commands prints the public key in PEM format extracted from either the certificate given with or the private key given with diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index 1c283d2aa7..0e4e5006c7 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -48,7 +48,7 @@ static int help(int argc, char *argv[], void *userdata) { "\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" + " extract-public Extract a public key\n" " pkcs7 Generate a PKCS#7 signature\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" @@ -247,7 +247,7 @@ static int verb_validate(int argc, char *argv[], void *userdata) { return 0; } -static int verb_public(int argc, char *argv[], void *userdata) { +static int verb_extract_public(int argc, char *argv[], void *userdata) { _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL; int r; @@ -399,10 +399,11 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) { 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 }, - { "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "validate", VERB_ANY, 1, 0, verb_validate }, + { "extract-public", VERB_ANY, 1, 0, verb_extract_public }, + { "public", VERB_ANY, 1, 0, verb_extract_public }, /* Deprecated but kept for backwards compat. */ + { "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 }, {} }; int r; diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index ad6560534a..c98f8e2a5d 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -1364,7 +1364,7 @@ def make_uki(opts: UkifyConfig) -> None: pcrpkey: Union[bytes, Path, None] = opts.pcrpkey if pcrpkey is None: keyutil_tool = find_tool('systemd-keyutil', '/usr/lib/systemd/systemd-keyutil') - cmd = [keyutil_tool, 'public'] + cmd = [keyutil_tool, 'extract-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. diff --git a/test/units/TEST-74-AUX-UTILS.keyutil.sh b/test/units/TEST-74-AUX-UTILS.keyutil.sh index 172939253e..7cc9fd05f9 100755 --- a/test/units/TEST-74-AUX-UTILS.keyutil.sh +++ b/test/units/TEST-74-AUX-UTILS.keyutil.sh @@ -37,14 +37,14 @@ testcase_validate() { /usr/lib/systemd/systemd-keyutil validate --certificate /tmp/test.crt --private-key /tmp/test.key } -testcase_public() { - PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --certificate /tmp/test.crt)" +testcase_extract_public() { + PUBLIC="$(/usr/lib/systemd/systemd-keyutil extract-public --certificate /tmp/test.crt)" assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)" - PUBLIC="$(/usr/lib/systemd/systemd-keyutil public --private-key /tmp/test.key)" + PUBLIC="$(/usr/lib/systemd/systemd-keyutil extract-public --private-key /tmp/test.key)" assert_eq "$PUBLIC" "$(openssl x509 -in /tmp/test.crt -pubkey -noout)" - (! /usr/lib/systemd/systemd-keyutil public) + (! /usr/lib/systemd/systemd-keyutil extract-public) } verify_pkcs7() { From 42b6f87a42061ae3d445a748e68c94cf8761059d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 1 Dec 2025 22:21:45 +0100 Subject: [PATCH 4/4] keyutil: Add extract-certificate Useful to extract a certificate from a hardware token to a file, for example in mkosi to ship the certificate from a hardware token in /usr/lib/verity.d in an image --- man/systemd-keyutil.xml | 11 +++++++ src/keyutil/keyutil.c | 39 +++++++++++++++++++++---- test/units/TEST-74-AUX-UTILS.keyutil.sh | 7 +++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml index 47ab28a055..c9e8cd5e8c 100644 --- a/man/systemd-keyutil.xml +++ b/man/systemd-keyutil.xml @@ -65,6 +65,17 @@ + + extract-certificate + + This command prints the X.509 certificate in PEM format extracted from the + certificate given with . This is useful when loading a certificate + from an OpenSSL provider (e.g. a hardware token) and wanting to output a standalone PEM certificate + that can be used without the provider. + + + + pkcs7 diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index 0e4e5006c7..f26b235d39 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -49,6 +49,7 @@ static int help(int argc, char *argv[], void *userdata) { "\n%3$sCommands:%4$s\n" " validate Load and validate the given certificate and private key\n" " extract-public Extract a public key\n" + " extract-certificate Extract a certificate\n" " pkcs7 Generate a PKCS#7 signature\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" @@ -314,6 +315,33 @@ static int verb_extract_public(int argc, char *argv[], void *userdata) { return 0; } +static int verb_extract_certificate(int argc, char *argv[], void *userdata) { + _cleanup_(X509_freep) X509 *certificate = NULL; + int r; + + if (!arg_certificate) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--certificate= must be specified."); + + 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 (PEM_write_X509(stdout, certificate) == 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write certificate to stdout."); + + return 0; +} + static int verb_pkcs7(int argc, char *argv[], void *userdata) { _cleanup_(X509_freep) X509 *certificate = NULL; _cleanup_free_ char *pkcs1 = NULL; @@ -399,11 +427,12 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) { 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 }, - { "extract-public", VERB_ANY, 1, 0, verb_extract_public }, - { "public", VERB_ANY, 1, 0, verb_extract_public }, /* Deprecated but kept for backwards compat. */ - { "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "validate", VERB_ANY, 1, 0, verb_validate }, + { "extract-public", VERB_ANY, 1, 0, verb_extract_public }, + { "public", VERB_ANY, 1, 0, verb_extract_public }, /* Deprecated but kept for backwards compat. */ + { "extract-certificate", VERB_ANY, 1, 0, verb_extract_certificate }, + { "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 }, {} }; int r; diff --git a/test/units/TEST-74-AUX-UTILS.keyutil.sh b/test/units/TEST-74-AUX-UTILS.keyutil.sh index 7cc9fd05f9..5e36392d27 100755 --- a/test/units/TEST-74-AUX-UTILS.keyutil.sh +++ b/test/units/TEST-74-AUX-UTILS.keyutil.sh @@ -47,6 +47,13 @@ testcase_extract_public() { (! /usr/lib/systemd/systemd-keyutil extract-public) } +testcase_extract_certificate() { + CERT="$(/usr/lib/systemd/systemd-keyutil extract-certificate --certificate /tmp/test.crt)" + assert_eq "$CERT" "$(cat /tmp/test.crt)" + + (! /usr/lib/systemd/systemd-keyutil extract-certificate) +} + verify_pkcs7() { # Verify using internal certificate openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify >/dev/null