]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
keyutil: Add extract-certificate 39962/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 1 Dec 2025 21:21:45 +0000 (22:21 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 18 Dec 2025 12:57:32 +0000 (13:57 +0100)
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
src/keyutil/keyutil.c
test/units/TEST-74-AUX-UTILS.keyutil.sh

index 47ab28a055e6980324d403435461a4c573b1fbf2..c9e8cd5e8c2df8b76cbb72e9a73f801ba52fda54 100644 (file)
         <xi:include href="version-info.xml" xpointer="v257"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><command>extract-certificate</command></term>
+
+        <listitem><para>This command prints the X.509 certificate in PEM format extracted from the
+        certificate given with <option>--certificate=</option>. 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.</para>
+
+        <xi:include href="version-info.xml" xpointer="v260"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><command>pkcs7</command></term>
 
index 0e4e5006c7242fc9963b404c1151603570f51868..f26b235d39a0d91032c7361f33dc2e01cd13a0e3 100644 (file)
@@ -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;
index 7cc9fd05f9fc4b137c3df2bd6e58b6c09bc5d95e..5e36392d270aa48eb2194bcbd4790b8415802de2 100755 (executable)
@@ -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