diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml index aaf760b948..c9e8cd5e8c 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 @@ -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 1c283d2aa7..f26b235d39 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -48,7 +48,8 @@ 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" + " 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" @@ -247,7 +248,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; @@ -314,6 +315,33 @@ static int verb_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,10 +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 }, - { "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. */ + { "extract-certificate", VERB_ANY, 1, 0, verb_extract_certificate }, + { "pkcs7", VERB_ANY, VERB_ANY, 0, verb_pkcs7 }, {} }; int r; diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 31d1347f7d..0e54ae527a 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; @@ -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; 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..5e36392d27 100755 --- a/test/units/TEST-74-AUX-UTILS.keyutil.sh +++ b/test/units/TEST-74-AUX-UTILS.keyutil.sh @@ -37,14 +37,21 @@ 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) +} + +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() {