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 aed34b39d0..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, &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");
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);
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