mirror of
https://github.com/systemd/systemd.git
synced 2025-03-14 04:58:28 +03:00
systemd-keyutil: add verb to conver PKCS#1 to PKCS#7
Add verb that takes a PKCS#1 signature (plain rsa) as input and a certificates, and outputs a PKCS#7 binary detached signature (p7s), which is what the kernel dm-verity driver expects. Co-authored-by: Luca Boccassi <bluca@debian.org>
This commit is contained in:
parent
012fd82d43
commit
af69ea714d
@ -64,6 +64,16 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>pkcs7</command></term>
|
||||
|
||||
<listitem><para>This command generates a PKCS#7 signature that embeds the PKCS#1 signature (RSA)
|
||||
provided with <option>--signature=</option> to an <option>--output=</option> file in PKCS#7 format
|
||||
(p7s) using the certificate given with <option>--certificate=</option>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
@ -90,6 +100,22 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--signature=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>Input PKCS#1 signature for the <command>pkcs7</command> command.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--output=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>Output PKCS#7 signature for the <command>pkcs7</command> command.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
<xi:include href="standard-options.xml" xpointer="version"/>
|
||||
</variablelist>
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ask-password-api.h"
|
||||
#include "build.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "main-func.h"
|
||||
#include "memstream-util.h"
|
||||
#include "openssl-util.h"
|
||||
@ -20,11 +21,15 @@ static char *arg_private_key_source = NULL;
|
||||
static char *arg_certificate = NULL;
|
||||
static char *arg_certificate_source = NULL;
|
||||
static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
|
||||
static char *arg_signature = NULL;
|
||||
static char *arg_output = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_signature, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_output, freep);
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@ -39,6 +44,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"
|
||||
" public Extract a public key\n"
|
||||
" pkcs7 Generate a PKCS#7 signature\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n"
|
||||
@ -53,6 +59,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" Specify how to interpret the certificate from\n"
|
||||
" --certificate=. Allows the certificate to be loaded\n"
|
||||
" from an OpenSSL provider\n"
|
||||
" --signature=PATH PKCS#1 signature to embed in PKCS#7 signature\n"
|
||||
" --output=PATH Where to write the PKCS#7 signature\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -71,6 +79,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PRIVATE_KEY_SOURCE,
|
||||
ARG_CERTIFICATE,
|
||||
ARG_CERTIFICATE_SOURCE,
|
||||
ARG_SIGNATURE,
|
||||
ARG_OUTPUT,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -80,6 +90,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
|
||||
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
|
||||
{ "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
|
||||
{ "signature", required_argument, NULL, ARG_SIGNATURE },
|
||||
{ "output", required_argument, NULL, ARG_OUTPUT },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -130,6 +142,20 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_SIGNATURE:
|
||||
r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_signature);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case ARG_OUTPUT:
|
||||
r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_output);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -277,11 +303,124 @@ static int verb_public(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_pkcs7(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(X509_freep) X509 *certificate = NULL;
|
||||
_cleanup_free_ char *pkcs1 = NULL;
|
||||
size_t pkcs1_len = 0;
|
||||
int r;
|
||||
|
||||
if (!arg_certificate)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--certificate= must be specified");
|
||||
|
||||
if (!arg_signature)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--signature= must be specified");
|
||||
|
||||
if (!arg_output)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--output= 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);
|
||||
|
||||
r = read_full_file(arg_signature, &pkcs1, &pkcs1_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read PKCS#1 file %s: %m", arg_signature);
|
||||
if (pkcs1_len == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "PKCS#1 file %s is empty", arg_signature);
|
||||
|
||||
/* Create PKCS7_SIGNER_INFO using X509 pubkey/digest NIDs */
|
||||
|
||||
_cleanup_(PKCS7_SIGNER_INFO_freep) PKCS7_SIGNER_INFO *signer_info = PKCS7_SIGNER_INFO_new();
|
||||
if (!signer_info)
|
||||
return log_oom();
|
||||
|
||||
if (ASN1_INTEGER_set(signer_info->version, 1) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ASN1 integer: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (X509_NAME_set(&signer_info->issuer_and_serial->issuer, X509_get_issuer_name(certificate)) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set issuer name: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
ASN1_INTEGER_free(signer_info->issuer_and_serial->serial);
|
||||
signer_info->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get0_serialNumber(certificate));
|
||||
if (!signer_info->issuer_and_serial->serial)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set issuer serial: %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, /* secbits= */ NULL, /* flags= */ NULL) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get X.509 digest NID/PK: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (X509_ALGOR_set0(signer_info->digest_alg, OBJ_nid2obj(x509_mdnid), V_ASN1_NULL, /* pval= */ NULL) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set digest alg: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (X509_ALGOR_set0(signer_info->digest_enc_alg, OBJ_nid2obj(x509_pknid), V_ASN1_NULL, /* pval= */ NULL) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set digest enc alg: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
/* Create new PKCS7 using X509 certificate */
|
||||
|
||||
_cleanup_(PKCS7_freep) PKCS7 *pkcs7 = PKCS7_new();
|
||||
if (!pkcs7)
|
||||
return log_oom();
|
||||
|
||||
if (PKCS7_set_type(pkcs7, NID_pkcs7_signed) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 type: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (PKCS7_content_new(pkcs7, NID_pkcs7_data) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 content: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (PKCS7_set_detached(pkcs7, true) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 detached attribute: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (PKCS7_add_certificate(pkcs7, certificate) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 certificate: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
/* Add PKCS1 signature to PKCS7_SIGNER_INFO */
|
||||
|
||||
ASN1_STRING_set0(signer_info->enc_digest, TAKE_PTR(pkcs1), pkcs1_len);
|
||||
|
||||
/* Add PKCS7_SIGNER_INFO to PKCS7 */
|
||||
|
||||
if (PKCS7_add_signer(pkcs7, signer_info) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 signer info: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
TAKE_PTR(signer_info);
|
||||
|
||||
_cleanup_fclose_ FILE *output = fopen(arg_output, "we");
|
||||
if (!output)
|
||||
return log_error_errno(errno, "Could not open PKCS#7 output file %s: %m", arg_output);
|
||||
|
||||
if (!i2d_PKCS7_fp(output, pkcs7))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write PKCS#7 file: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 },
|
||||
{}
|
||||
};
|
||||
int r;
|
||||
|
@ -62,6 +62,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIGNUM*, BN_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BN_CTX*, BN_CTX_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ECDSA_SIG*, ECDSA_SIG_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7*, PKCS7_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7_SIGNER_INFO*, PKCS7_SIGNER_INFO_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free_all, NULL);
|
||||
|
@ -47,4 +47,11 @@ testcase_public() {
|
||||
(! /usr/lib/systemd/systemd-keyutil public)
|
||||
}
|
||||
|
||||
testcase_pkcs7() {
|
||||
echo -n "test" > /tmp/payload
|
||||
openssl dgst -sha256 -sign /tmp/test.key -out /tmp/payload.sig /tmp/payload
|
||||
/usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.sig pkcs7
|
||||
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -certfile /tmp/test.crt -nointern -noverify > /dev/null
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
Loading…
x
Reference in New Issue
Block a user