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() {