{CMS,PKCS7}_verify(): use 'certs' parameter ('-certfile' option) also for chain building

Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18916)
This commit is contained in:
Dr. David von Oheimb 2023-10-04 21:32:00 +02:00 committed by Tomas Mraz
parent cc31db1eb6
commit 29bbe7d008
11 changed files with 107 additions and 32 deletions

View file

@ -175,7 +175,10 @@ const OPTIONS cms_options[] = {
OPT_SECTION("Signing"), OPT_SECTION("Signing"),
{"md", OPT_MD, 's', "Digest algorithm to use"}, {"md", OPT_MD, 's', "Digest algorithm to use"},
{"signer", OPT_SIGNER, 's', "Signer certificate input file"}, {"signer", OPT_SIGNER, 's', "Signer certificate input file"},
{"certfile", OPT_CERTFILE, '<', "Other certificates file"}, {"certfile", OPT_CERTFILE, '<',
"Extra signer and intermediate CA certificates to include when signing"},
{OPT_MORE_STR, 0, 0,
"or to use as preferred signer certs and for chain building when verifying"},
{"cades", OPT_CADES, '-', {"cades", OPT_CADES, '-',
"Include signingCertificate attribute (CAdES-BES)"}, "Include signingCertificate attribute (CAdES-BES)"},
{"nodetach", OPT_NODETACH, '-', "Use opaque signing"}, {"nodetach", OPT_NODETACH, '-', "Use opaque signing"},

View file

@ -96,7 +96,10 @@ const OPTIONS smime_options[] = {
{"nosigs", OPT_NOSIGS, '-', "Don't verify message signature"}, {"nosigs", OPT_NOSIGS, '-', "Don't verify message signature"},
{"noverify", OPT_NOVERIFY, '-', "Don't verify signers certificate"}, {"noverify", OPT_NOVERIFY, '-', "Don't verify signers certificate"},
{"certfile", OPT_CERTFILE, '<', "Other certificates file"}, {"certfile", OPT_CERTFILE, '<',
"Extra signer and intermediate CA certificates to include when signing"},
{OPT_MORE_STR, 0, 0,
"or to use as preferred signer certs and for chain building when verifying"},
{"recip", OPT_RECIP, '<', "Recipient certificate file for decryption"}, {"recip", OPT_RECIP, '<', "Recipient certificate file for decryption"},
OPT_SECTION("Email"), OPT_SECTION("Email"),

View file

@ -15,6 +15,7 @@
#include <openssl/cms.h> #include <openssl/cms.h>
#include "cms_local.h" #include "cms_local.h"
#include "crypto/asn1.h" #include "crypto/asn1.h"
#include "crypto/x509.h"
static BIO *cms_get_text_bio(BIO *out, unsigned int flags) static BIO *cms_get_text_bio(BIO *out, unsigned int flags)
{ {
@ -308,7 +309,7 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
{ {
CMS_SignerInfo *si; CMS_SignerInfo *si;
STACK_OF(CMS_SignerInfo) *sinfos; STACK_OF(CMS_SignerInfo) *sinfos;
STACK_OF(X509) *cms_certs = NULL; STACK_OF(X509) *untrusted = NULL;
STACK_OF(X509_CRL) *crls = NULL; STACK_OF(X509_CRL) *crls = NULL;
STACK_OF(X509) **si_chains = NULL; STACK_OF(X509) **si_chains = NULL;
X509 *signer; X509 *signer;
@ -360,13 +361,21 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
if (si_chains == NULL) if (si_chains == NULL)
goto err; goto err;
} }
cms_certs = CMS_get1_certs(cms); if ((untrusted = CMS_get1_certs(cms)) == NULL)
if (!(flags & CMS_NOCRL)) goto err;
crls = CMS_get1_crls(cms); if (sk_X509_num(certs) > 0
&& !ossl_x509_add_certs_new(&untrusted, certs,
X509_ADD_FLAG_UP_REF |
X509_ADD_FLAG_NO_DUP))
goto err;
if ((flags & CMS_NOCRL) == 0
&& (crls = CMS_get1_crls(cms)) == NULL)
goto err;
for (i = 0; i < scount; i++) { for (i = 0; i < scount; i++) {
si = sk_CMS_SignerInfo_value(sinfos, i); si = sk_CMS_SignerInfo_value(sinfos, i);
if (!cms_signerinfo_verify_cert(si, store, cms_certs, crls, if (!cms_signerinfo_verify_cert(si, store, untrusted, crls,
si_chains ? &si_chains[i] : NULL, si_chains ? &si_chains[i] : NULL,
ctx)) ctx))
goto err; goto err;
@ -482,7 +491,7 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
OSSL_STACK_OF_X509_free(si_chains[i]); OSSL_STACK_OF_X509_free(si_chains[i]);
OPENSSL_free(si_chains); OPENSSL_free(si_chains);
} }
OSSL_STACK_OF_X509_free(cms_certs); sk_X509_pop_free(untrusted, X509_free);
sk_X509_CRL_pop_free(crls, X509_CRL_free); sk_X509_CRL_pop_free(crls, X509_CRL_free);
return ret; return ret;

View file

@ -413,7 +413,7 @@ PKCS7_SIGNER_INFO *PKCS7_add_signature(PKCS7 *p7, X509 *x509, EVP_PKEY *pkey,
return NULL; return NULL;
} }
static STACK_OF(X509) *pkcs7_get_signer_certs(const PKCS7 *p7) STACK_OF(X509) *pkcs7_get0_certificates(const PKCS7 *p7)
{ {
if (p7->d.ptr == NULL) if (p7->d.ptr == NULL)
return NULL; return NULL;
@ -454,7 +454,7 @@ void ossl_pkcs7_resolve_libctx(PKCS7 *p7)
rinfos = pkcs7_get_recipient_info(p7); rinfos = pkcs7_get_recipient_info(p7);
sinfos = PKCS7_get_signer_info(p7); sinfos = PKCS7_get_signer_info(p7);
certs = pkcs7_get_signer_certs(p7); certs = pkcs7_get0_certificates(p7);
for (i = 0; i < sk_X509_num(certs); i++) for (i = 0; i < sk_X509_num(certs); i++)
ossl_x509_set0_libctx(sk_X509_value(certs, i), libctx, propq); ossl_x509_set0_libctx(sk_X509_value(certs, i), libctx, propq);

View file

@ -9,6 +9,7 @@
#include "crypto/pkcs7.h" #include "crypto/pkcs7.h"
STACK_OF(X509) *pkcs7_get0_certificates(const PKCS7 *p7);
const PKCS7_CTX *ossl_pkcs7_get0_ctx(const PKCS7 *p7); const PKCS7_CTX *ossl_pkcs7_get0_ctx(const PKCS7 *p7);
OSSL_LIB_CTX *ossl_pkcs7_ctx_get0_libctx(const PKCS7_CTX *ctx); OSSL_LIB_CTX *ossl_pkcs7_ctx_get0_libctx(const PKCS7_CTX *ctx);
const char *ossl_pkcs7_ctx_get0_propq(const PKCS7_CTX *ctx); const char *ossl_pkcs7_ctx_get0_propq(const PKCS7_CTX *ctx);

View file

@ -11,6 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include "internal/cryptlib.h" #include "internal/cryptlib.h"
#include "crypto/x509.h"
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h> #include <openssl/x509v3.h>
#include "pk7_local.h" #include "pk7_local.h"
@ -215,6 +216,8 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
BIO *indata, BIO *out, int flags) BIO *indata, BIO *out, int flags)
{ {
STACK_OF(X509) *signers; STACK_OF(X509) *signers;
STACK_OF(X509) *included_certs;
STACK_OF(X509) *untrusted = NULL;
X509 *signer; X509 *signer;
STACK_OF(PKCS7_SIGNER_INFO) *sinfos; STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
PKCS7_SIGNER_INFO *si; PKCS7_SIGNER_INFO *si;
@ -272,21 +275,24 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
ossl_pkcs7_ctx_get0_propq(p7_ctx)); ossl_pkcs7_ctx_get0_propq(p7_ctx));
if (cert_ctx == NULL) if (cert_ctx == NULL)
goto err; goto err;
if (!(flags & PKCS7_NOVERIFY)) if ((flags & PKCS7_NOVERIFY) == 0) {
if (!ossl_x509_add_certs_new(&untrusted, certs, X509_ADD_FLAG_NO_DUP))
goto err;
included_certs = pkcs7_get0_certificates(p7);
if ((flags & PKCS7_NOCHAIN) == 0
&& !ossl_x509_add_certs_new(&untrusted, included_certs,
X509_ADD_FLAG_NO_DUP))
goto err;
for (k = 0; k < sk_X509_num(signers); k++) { for (k = 0; k < sk_X509_num(signers); k++) {
signer = sk_X509_value(signers, k); signer = sk_X509_value(signers, k);
if (!(flags & PKCS7_NOCHAIN)) { if (!X509_STORE_CTX_init(cert_ctx, store, signer, untrusted)) {
if (!X509_STORE_CTX_init(cert_ctx, store, signer,
p7->d.sign->cert)) {
ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB);
goto err;
}
if (!X509_STORE_CTX_set_default(cert_ctx, "smime_sign"))
goto err;
} else if (!X509_STORE_CTX_init(cert_ctx, store, signer, NULL)) {
ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB); ERR_raise(ERR_LIB_PKCS7, ERR_R_X509_LIB);
goto err; goto err;
} }
if ((flags & PKCS7_NOCHAIN) == 0
&& !X509_STORE_CTX_set_default(cert_ctx, "smime_sign"))
goto err;
if (!(flags & PKCS7_NOCRL)) if (!(flags & PKCS7_NOCRL))
X509_STORE_CTX_set0_crls(cert_ctx, p7->d.sign->crl); X509_STORE_CTX_set0_crls(cert_ctx, p7->d.sign->crl);
i = X509_verify_cert(cert_ctx); i = X509_verify_cert(cert_ctx);
@ -299,6 +305,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
} }
/* Check for revocation status here */ /* Check for revocation status here */
} }
}
if ((p7bio = PKCS7_dataInit(p7, indata)) == NULL) if ((p7bio = PKCS7_dataInit(p7, indata)) == NULL)
goto err; goto err;
@ -353,13 +360,14 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
BIO_pop(p7bio); BIO_pop(p7bio);
BIO_free_all(p7bio); BIO_free_all(p7bio);
sk_X509_free(signers); sk_X509_free(signers);
sk_X509_free(untrusted);
return ret; return ret;
} }
STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs,
int flags) int flags)
{ {
STACK_OF(X509) *signers; STACK_OF(X509) *signers, *included_certs;
STACK_OF(PKCS7_SIGNER_INFO) *sinfos; STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
PKCS7_SIGNER_INFO *si; PKCS7_SIGNER_INFO *si;
PKCS7_ISSUER_AND_SERIAL *ias; PKCS7_ISSUER_AND_SERIAL *ias;
@ -375,6 +383,7 @@ STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs,
ERR_raise(ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE);
return NULL; return NULL;
} }
included_certs = pkcs7_get0_certificates(p7);
/* Collect all the signers together */ /* Collect all the signers together */
@ -395,14 +404,11 @@ STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs,
ias = si->issuer_and_serial; ias = si->issuer_and_serial;
signer = NULL; signer = NULL;
/* If any certificates passed they take priority */ /* If any certificates passed they take priority */
if (certs != NULL) signer = X509_find_by_issuer_and_serial(certs,
signer = X509_find_by_issuer_and_serial(certs, ias->issuer, ias->serial);
if (signer == NULL && (flags & PKCS7_NOINTERN) == 0)
signer = X509_find_by_issuer_and_serial(included_certs,
ias->issuer, ias->serial); ias->issuer, ias->serial);
if (signer == NULL && !(flags & PKCS7_NOINTERN)
&& p7->d.sign->cert)
signer =
X509_find_by_issuer_and_serial(p7->d.sign->cert,
ias->issuer, ias->serial);
if (signer == NULL) { if (signer == NULL) {
ERR_raise(ERR_LIB_PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND); ERR_raise(ERR_LIB_PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND);
sk_X509_free(signers); sk_X509_free(signers);

View file

@ -453,8 +453,9 @@ used multiple times if more than one signer is required.
=item B<-certfile> I<file> =item B<-certfile> I<file>
Allows additional certificates to be specified. When signing these will Allows additional certificates to be specified. When signing these will
be included with the message. When verifying these will be searched for be included with the message. When verifying, these will be searched for
the signers certificates. signer certificates and will be used for chain building.
The input can be in PEM, DER, or PKCS#12 format. The input can be in PEM, DER, or PKCS#12 format.
=item B<-cades> =item B<-cades>

View file

@ -234,8 +234,9 @@ option is present B<CRLF> is used instead.
=item B<-certfile> I<file> =item B<-certfile> I<file>
Allows additional certificates to be specified. When signing these will Allows additional certificates to be specified. When signing these will
be included with the message. When verifying these will be searched for be included with the message. When verifying, these will be searched for
the signers certificates. signer certificates and will be used for chain building.
The input can be in PEM, DER, or PKCS#12 format. The input can be in PEM, DER, or PKCS#12 format.
=item B<-signer> I<file> =item B<-signer> I<file>

View file

@ -26,6 +26,8 @@ B<CMS SignedData> structure contained in a structure of type B<CMS_ContentInfo>.
I<cms> points to the B<CMS_ContentInfo> structure to verify. I<cms> points to the B<CMS_ContentInfo> structure to verify.
The optional I<certs> parameter refers to a set of certificates The optional I<certs> parameter refers to a set of certificates
in which to search for signing certificates. in which to search for signing certificates.
It is also used
as a source of untrusted intermediate CA certificates for chain building.
I<cms> may contain extra untrusted CA certificates that may be used for I<cms> may contain extra untrusted CA certificates that may be used for
chain building as well as CRLs that may be used for certificate validation. chain building as well as CRLs that may be used for certificate validation.
I<store> may be NULL or point to I<store> may be NULL or point to

View file

@ -19,6 +19,8 @@ PKCS7_verify() is very similar to L<CMS_verify(3)>.
It verifies a PKCS#7 signedData structure given in I<p7>. It verifies a PKCS#7 signedData structure given in I<p7>.
The optional I<certs> parameter refers to a set of certificates The optional I<certs> parameter refers to a set of certificates
in which to search for signer's certificates. in which to search for signer's certificates.
It is also used
as a source of untrusted intermediate CA certificates for chain building.
I<p7> may contain extra untrusted CA certificates that may be used for I<p7> may contain extra untrusted CA certificates that may be used for
chain building as well as CRLs that may be used for certificate validation. chain building as well as CRLs that may be used for certificate validation.
I<store> may be NULL or point to I<store> may be NULL or point to

View file

@ -1044,6 +1044,53 @@ subtest "CMS signed digest, S/MIME format" => sub {
"Verify CMS signed digest, S/MIME format"); "Verify CMS signed digest, S/MIME format");
}; };
sub path_tests {
our $app = shift;
our @path = qw(test certs);
our $key = srctop_file(@path, "ee-key.pem");
our $ee = srctop_file(@path, "ee-cert.pem");
our $ca = srctop_file(@path, "ca-cert.pem");
our $root = srctop_file(@path, "root-cert.pem");
our $sig_file = "signature.p7s";
sub sign {
my $inter = shift;
my @inter = $inter ? ("-certfile", $inter) : ();
my $msg = shift;
ok(run(app(["openssl", $app, @prov, "-sign", "-in", $smcont,
"-inkey", $key, "-signer", $ee, @inter,
"-out", $sig_file],
"accept $app sign with EE $msg".
" intermediate CA certificates")));
}
sub verify {
my $inter = shift;
my @inter = $inter ? ("-certfile", $inter) : ();
my $msg = shift;
my $res = shift;
ok($res == run(app(["openssl", $app, @prov, "-verify", "-in", $sig_file,
"-purpose", "sslserver", "-CAfile", $root, @inter,
"-content", $smcont],
"accept $app verify with EE ".
"$msg intermediate CA certificates")));
}
sign($ca, "and");
verify(0, "with included", 1);
sign(0, "without");
verify(0, "without", 0);
verify($ca, "with added", 1);
};
subtest "CMS sign+verify cert path tests" => sub {
plan tests => 5;
path_tests("cms");
};
subtest "PKCS7 sign+verify cert path tests" => sub {
plan tests => 5;
path_tests("smime");
};
subtest "CMS code signing test" => sub { subtest "CMS code signing test" => sub {
plan tests => 7; plan tests => 7;
my $sig_file = "signature.p7s"; my $sig_file = "signature.p7s";