From 768a297c4216656a20c9483bbd003cd91e75ecc3 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 6 Jun 2025 12:31:40 -0400 Subject: [PATCH 1/3] openssl: add hash_algorithm parameter to pkcs7_new() The pkcs7_new() function currently uses the hash algorithm from the X509 certificate's signature, but the content signature may use a different hash algorithm. This adds a parameter to allow specifying what hash algorithm the content signature generation used. --- src/keyutil/keyutil.c | 2 +- src/sbsign/sbsign.c | 5 +++-- src/shared/openssl-util.c | 18 ++++++++++-------- src/shared/openssl-util.h | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index aed34b39d0..396bd1db1e 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -355,7 +355,7 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) { _cleanup_(PKCS7_freep) PKCS7 *pkcs7 = NULL; PKCS7_SIGNER_INFO *signer_info; - r = pkcs7_new(certificate, /* private_key= */ NULL, &pkcs7, &signer_info); + r = pkcs7_new(certificate, /* private_key= */ NULL, /* hash_algorithm= */ NULL, &pkcs7, &signer_info); if (r < 0) return log_error_errno(r, "Failed to allocate PKCS#7 context: %m"); diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index be11833d7d..68e1f24ac2 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -321,6 +321,7 @@ static int asn1_timestamp(ASN1_TIME **ret) { static int pkcs7_new_with_attributes( X509 *certificate, EVP_PKEY *private_key, + const char *hash_algorithm, STACK_OF(X509_ATTRIBUTE) *signed_attributes, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si) { @@ -336,7 +337,7 @@ static int pkcs7_new_with_attributes( _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL; PKCS7_SIGNER_INFO *si = NULL; /* avoid false maybe-uninitialized warning */ - r = pkcs7_new(certificate, private_key, &p7, &si); + r = pkcs7_new(certificate, private_key, hash_algorithm, &p7, &si); if (r < 0) return log_error_errno(r, "Failed to allocate PKCS# context: %m"); @@ -562,7 +563,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) { _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL; PKCS7_SIGNER_INFO *si = NULL; /* avoid false maybe-uninitialized warning */ - r = pkcs7_new_with_attributes(certificate, private_key, signed_attributes, &p7, &si); + r = pkcs7_new_with_attributes(certificate, private_key, /* hash_algorithm= */ NULL, signed_attributes, &p7, &si); if (r < 0) return r; diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 180d6f202f..57b638596a 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1126,14 +1126,15 @@ int digest_and_sign( return 0; } -int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si) { +int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, const char *hash_algorithm, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si) { assert(certificate); assert(ret_p7); /* This function sets up a new PKCS7 signing context. If a private key is provided, the context is * set up for "in-band" signing with PKCS7_dataFinal(). If a private key is not provided, the context * is set up for "out-of-band" signing, meaning the signature has to be provided by the user and - * copied into the signer info's "enc_digest" field. */ + * copied into the signer info's "enc_digest" field. If the signing hash algorithm is not provided, + * SHA-256 is used. */ _cleanup_(PKCS7_freep) PKCS7 *p7 = PKCS7_new(); if (!p7) @@ -1151,21 +1152,22 @@ int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, PKCS7 **ret_p7, PKCS7_SI return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 certificate: %s", ERR_error_string(ERR_get_error(), NULL)); - int x509_mdnid = 0, x509_pknid = 0; - if (X509_get_signature_info(certificate, &x509_mdnid, &x509_pknid, NULL, NULL) == 0) + int x509_pknid = 0; + if (X509_get_signature_info(certificate, NULL, &x509_pknid, NULL, NULL) == 0) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get X509 digest NID: %s", ERR_error_string(ERR_get_error(), NULL)); - const EVP_MD *md = EVP_get_digestbynid(x509_mdnid); + const EVP_MD *md = EVP_get_digestbyname(hash_algorithm ?: "SHA256"); if (!md) - return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get digest algorithm via digest NID"); + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get digest algorithm '%s'", + hash_algorithm ?: "SHA256"); _cleanup_(PKCS7_SIGNER_INFO_freep) PKCS7_SIGNER_INFO *si = PKCS7_SIGNER_INFO_new(); if (!si) return log_oom(); if (private_key) { - if (PKCS7_SIGNER_INFO_set(si, certificate, private_key, EVP_get_digestbynid(x509_mdnid)) <= 0) + if (PKCS7_SIGNER_INFO_set(si, certificate, private_key, md) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure signer info: %s", ERR_error_string(ERR_get_error(), NULL)); } else { @@ -1183,7 +1185,7 @@ int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, PKCS7 **ret_p7, PKCS7_SI return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info serial: %s", ERR_error_string(ERR_get_error(), NULL)); - if (X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(x509_mdnid), V_ASN1_NULL, NULL) == 0) + if (X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(EVP_MD_type(md)), V_ASN1_NULL, NULL) == 0) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info digest algorithm: %s", ERR_error_string(ERR_get_error(), NULL)); diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 8b051435e6..6bcb16af31 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -165,7 +165,7 @@ int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_s int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size); -int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si); +int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, const char *hash_algorithm, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si); int string_hashsum(const char *s, size_t len, const char *md_algorithm, char **ret); From 103fa98f8431eed799824e501abd88f4b9702380 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Sat, 8 Mar 2025 16:47:45 -0500 Subject: [PATCH 2/3] keyutil: add parameter to specify hash algorithm used for PKCS#1 signature --- man/systemd-keyutil.xml | 15 ++++++++++++++- src/keyutil/keyutil.c | 11 ++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/man/systemd-keyutil.xml b/man/systemd-keyutil.xml index a1e0bca43d..aaf760b948 100644 --- a/man/systemd-keyutil.xml +++ b/man/systemd-keyutil.xml @@ -72,7 +72,9 @@ in a PKCS#7 signature using the certificate given with and writes it to the file specified with in PKCS#7 format (p7s). If is provided it is included in the p7s, - otherwise a "detached" signature is created. + otherwise a "detached" signature is created. The option, which + defaults to SHA256, specifies what hash algorithm was used to generate the + signature. @@ -119,6 +121,17 @@ + + + + Hash algorithm used to generate the PKCS#1 signature for the pkcs7 + command. This should be a valid openssl digest algorithm; use openssl list + -digest-algorithms to see a list of valid algorithms on your system. Defaults to + SHA256. + + + + diff --git a/src/keyutil/keyutil.c b/src/keyutil/keyutil.c index 396bd1db1e..d94d2153c2 100644 --- a/src/keyutil/keyutil.c +++ b/src/keyutil/keyutil.c @@ -26,6 +26,7 @@ static char *arg_certificate_source = NULL; static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE; static char *arg_signature = NULL; static char *arg_content = NULL; +static char *arg_hash_algorithm = NULL; static char *arg_output = NULL; STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); @@ -66,6 +67,8 @@ static int help(int argc, char *argv[], void *userdata) { " from an OpenSSL provider\n" " --content=PATH Raw data content to embed in PKCS#7 signature\n" " --signature=PATH PKCS#1 signature to embed in PKCS#7 signature\n" + " --hash-algorithm=ALGORITHM\n" + " Hash algorithm used to create the PKCS#1 signature\n" " --output=PATH Where to write the PKCS#7 signature\n" "\nSee the %2$s for details.\n", program_invocation_short_name, @@ -87,6 +90,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_CERTIFICATE_SOURCE, ARG_SIGNATURE, ARG_CONTENT, + ARG_HASH_ALGORITHM, ARG_OUTPUT, }; @@ -99,6 +103,7 @@ static int parse_argv(int argc, char *argv[]) { { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, { "signature", required_argument, NULL, ARG_SIGNATURE }, { "content", required_argument, NULL, ARG_CONTENT }, + { "hash-algorithm", required_argument, NULL, ARG_HASH_ALGORITHM }, { "output", required_argument, NULL, ARG_OUTPUT }, {} }; @@ -164,6 +169,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_HASH_ALGORITHM: + arg_hash_algorithm = optarg; + break; + case ARG_OUTPUT: r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_output); if (r < 0) @@ -355,7 +364,7 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) { _cleanup_(PKCS7_freep) PKCS7 *pkcs7 = NULL; PKCS7_SIGNER_INFO *signer_info; - r = pkcs7_new(certificate, /* private_key= */ NULL, /* hash_algorithm= */ NULL, &pkcs7, &signer_info); + r = pkcs7_new(certificate, /* private_key= */ NULL, arg_hash_algorithm, &pkcs7, &signer_info); if (r < 0) return log_error_errno(r, "Failed to allocate PKCS#7 context: %m"); From db83bd7e36cd4332484e4e42dac8bad22b4333d0 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 6 Jun 2025 14:18:06 -0400 Subject: [PATCH 3/3] test: update keyutil test to verify new pkcs7 --hash-algorithm param --- test/units/TEST-74-AUX-UTILS.keyutil.sh | 52 +++++++++++++++++-------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/test/units/TEST-74-AUX-UTILS.keyutil.sh b/test/units/TEST-74-AUX-UTILS.keyutil.sh index efe19a0c0c..0c5c0d5d9b 100755 --- a/test/units/TEST-74-AUX-UTILS.keyutil.sh +++ b/test/units/TEST-74-AUX-UTILS.keyutil.sh @@ -47,31 +47,49 @@ testcase_public() { (! /usr/lib/systemd/systemd-keyutil public) } +verify_pkcs7() { + # Verify using internal certificate + openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify > /dev/null + # Verify using external (original) certificate + openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify -certfile /tmp/test.crt -nointern > /dev/null +} + +verify_pkcs7_fail() { + # Verify using internal certificate + (! openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify > /dev/null) + # Verify using external (original) certificate + (! openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify -certfile /tmp/test.crt -nointern > /dev/null) +} + testcase_pkcs7() { echo -n "test" > /tmp/payload - # Generate PKCS#1 signature - openssl dgst -sha256 -sign /tmp/test.key -out /tmp/payload.sig /tmp/payload + for hashalg in sha256 sha384 sha512; do + # shellcheck disable=SC2086 + openssl dgst -$hashalg -sign /tmp/test.key -out /tmp/payload.p1s /tmp/payload - # Generate PKCS#7 "detached" signature - /usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.sig pkcs7 + # Test with and without content in the PKCS7 + for content_param in "" "--content /tmp/payload"; do + # Test with and without specifying signing hash alg + for hashalg_param in "" "--hash-algorithm $hashalg"; do + # shellcheck disable=SC2086 + /usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.p1s $content_param $hashalg_param pkcs7 - # Verify using internal x509 certificate - openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify > /dev/null + # Should always pass, except when not specifying hash alg and hash alg != sha256 + if [ -z "$hashalg_param" ] && [ "$hashalg" != "sha256" ]; then + verify_pkcs7_fail + else + verify_pkcs7 + fi - # Verify using external (original) x509 certificate - openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -certificate /tmp/test.crt -nointern -noverify > /dev/null + rm -f /tmp/payload.p7s + done + done - rm -f /tmp/payload.p7s + rm -f /tmp/payload.p1s + done - # Generate PKCS#7 non-"detached" signature - /usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.sig --content /tmp/payload pkcs7 - - # Verify using internal x509 certificate - openssl smime -verify -binary -inform der -in /tmp/payload.p7s -noverify > /dev/null - - # Verify using external (original) x509 certificate - openssl smime -verify -binary -inform der -in /tmp/payload.p7s -certificate /tmp/test.crt -nointern -noverify > /dev/null + rm -f /tmp/payload } run_testcases