Improve PKCS#7 verification with OpenSSL 3.5

Enhanced verification logic for PKCS#7 signedData structures by introducing a dedicated `verify_pkcs7_data()` function. This update addresses compatibility with older OpenSSL versions (< 3.0.5) and ensures correct handling of detached signed content using a BIO buffer.
The change enables support for PKCS#7 inner content (RFC 2315, section 7), as per OpenSSL PR#22575.
Refactored timestamp and authenticode verification functions to reduce duplication and properly manage X509_STORE and X509_CRL structures.
This commit is contained in:
olszomal 2025-04-30 09:11:28 +02:00 committed by Michał Trojnara
parent 475ea95ba3
commit 68e8845ef1

View File

@ -2216,7 +2216,6 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
*/ */
static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *timestamp, time_t time) static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *timestamp, time_t time)
{ {
X509_STORE *store;
STACK_OF(CMS_SignerInfo) *sinfos; STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *cmssi; CMS_SignerInfo *cmssi;
X509 *signer; X509 *signer;
@ -2224,8 +2223,8 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
STACK_OF(X509_CRL) *crls = NULL; STACK_OF(X509_CRL) *crls = NULL;
char *url = NULL; char *url = NULL;
int verok = 0; int verok = 0;
X509_STORE *store = X509_STORE_new();
store = X509_STORE_new();
if (!store) if (!store)
goto out; goto out;
if (x509_store_load_file(store, ctx->options->tsa_cafile)) { if (x509_store_load_file(store, ctx->options->tsa_cafile)) {
@ -2240,12 +2239,10 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
*/ */
if (!x509_store_set_time(store, time)) { if (!x509_store_set_time(store, time)) {
fprintf(stderr, "Failed to set store time\n"); fprintf(stderr, "Failed to set store time\n");
X509_STORE_free(store);
goto out; goto out;
} }
} else { } else {
printf("Use the \"-TSA-CAfile\" option to add the Time-Stamp Authority certificates bundle to verify the Timestamp Server.\n"); printf("Use the \"-TSA-CAfile\" option to add the Time-Stamp Authority certificates bundle to verify the Timestamp Server.\n");
X509_STORE_free(store);
goto out; goto out;
} }
@ -2255,14 +2252,12 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
STACK_OF(X509) *cms_certs; STACK_OF(X509) *cms_certs;
printf("CMS_verify error\n"); printf("CMS_verify error\n");
X509_STORE_free(store);
printf("\nFailed timestamp certificate chain retrieved from the signature:\n"); printf("\nFailed timestamp certificate chain retrieved from the signature:\n");
cms_certs = CMS_get1_certs(timestamp); cms_certs = CMS_get1_certs(timestamp);
print_certs_chain(cms_certs); print_certs_chain(cms_certs);
sk_X509_pop_free(cms_certs, X509_free); sk_X509_pop_free(cms_certs, X509_free);
goto out; goto out;
} }
X509_STORE_free(store);
sinfos = CMS_get0_SignerInfos(timestamp); sinfos = CMS_get0_SignerInfos(timestamp);
cmssi = sk_CMS_SignerInfo_value(sinfos, 0); cmssi = sk_CMS_SignerInfo_value(sinfos, 0);
@ -2299,7 +2294,6 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
int crlok = verify_crl(ctx->options->tsa_cafile, ctx->options->tsa_crlfile, int crlok = verify_crl(ctx->options->tsa_cafile, ctx->options->tsa_crlfile,
crls, signer, chain); crls, signer, chain);
sk_X509_pop_free(chain, X509_free); sk_X509_pop_free(chain, X509_free);
sk_X509_CRL_pop_free(crls, X509_CRL_free);
printf("Timestamp Server Signature CRL verification: %s\n", crlok ? "ok" : "failed"); printf("Timestamp Server Signature CRL verification: %s\n", crlok ? "ok" : "failed");
if (!crlok) if (!crlok)
goto out; goto out;
@ -2317,6 +2311,8 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
} }
verok = 1; /* OK */ verok = 1; /* OK */
out: out:
X509_STORE_free(store);
sk_X509_CRL_pop_free(crls, X509_CRL_free);
if (!verok) if (!verok)
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
return verok; return verok;
@ -2345,49 +2341,24 @@ static int PKCS7_type_is_other(PKCS7 *p7)
#endif /* OPENSSL_VERSION_NUMBER<0x30000000L */ #endif /* OPENSSL_VERSION_NUMBER<0x30000000L */
/* /*
* [in] ctx: structure holds input and output data
* [in] p7: PKCS#7 signature * [in] p7: PKCS#7 signature
* [in] time: signature verification time * [in] store: X509_STORE
* [in] signer: signer's X509 certificate
* [returns] 1 on error or 0 on success * [returns] 1 on error or 0 on success
*/ */
static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X509 *signer) static int verify_pkcs7_data(PKCS7 *p7, X509_STORE *store)
{ {
X509_STORE *store;
X509_CRL *crl = NULL;
STACK_OF(X509_CRL) *crls = NULL;
BIO *bio = NULL;
int verok = 0; int verok = 0;
char *url = NULL; #if OPENSSL_VERSION_NUMBER<0x30500000L
BIO *bio = NULL;
PKCS7 *contents = p7->d.sign->contents; PKCS7 *contents = p7->d.sign->contents;
store = X509_STORE_new(); /*
if (!store) * In the PKCS7_verify() function, the BIO *indata parameter refers to
goto out; * the signed data if the content is detached from p7.
* Otherwise, indata should be NULL, and then the signed data must be in p7.
if (!x509_store_load_file(store, ctx->options->cafile)) { * The OpenSSL error workaround is to put the inner content into BIO *indata parameter
fprintf(stderr, "Failed to add store lookup file\n"); * https://github.com/openssl/openssl/pull/22575
X509_STORE_free(store); */
goto out;
}
if (time != INVALID_TIME) {
printf("Signature verification time: ");
print_time_t(time);
if (!x509_store_set_time(store, time)) {
fprintf(stderr, "Failed to set signature time\n");
X509_STORE_free(store);
goto out;
}
} else if (ctx->options->time != INVALID_TIME) {
printf("Signature verification time: ");
print_time_t(ctx->options->time);
if (!x509_store_set_time(store, ctx->options->time)) {
fprintf(stderr, "Failed to set verifying time\n");
X509_STORE_free(store);
goto out;
}
}
/* verify a PKCS#7 signedData structure */
if (PKCS7_type_is_other(contents) && (contents->d.other != NULL) if (PKCS7_type_is_other(contents) && (contents->d.other != NULL)
&& (contents->d.other->value.sequence != NULL) && (contents->d.other->value.sequence != NULL)
&& (contents->d.other->value.sequence->length > 0)) { && (contents->d.other->value.sequence->length > 0)) {
@ -2402,7 +2373,7 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE) { if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE) {
fprintf(stderr, "Corrupted data content\n"); fprintf(stderr, "Corrupted data content\n");
X509_STORE_free(store); X509_STORE_free(store);
goto out; return 0; /* FAILED */
} }
bio = BIO_new_mem_buf(data, (int)len); bio = BIO_new_mem_buf(data, (int)len);
} else { } else {
@ -2413,26 +2384,61 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
} else { } else {
fprintf(stderr, "Corrupted data content\n"); fprintf(stderr, "Corrupted data content\n");
X509_STORE_free(store); X509_STORE_free(store);
return 0; /* FAILED */
}
verok = PKCS7_verify(p7, NULL, store, bio, NULL, 0);
BIO_free(bio);
#else /* OPENSSL_VERSION_NUMBER<0x30500000L */
verok = PKCS7_verify(p7, NULL, store, NULL, NULL, 0);
#endif /* OPENSSL_VERSION_NUMBER<0x30500000L */
return verok;
}
/*
* [in] ctx: structure holds input and output data
* [in] p7: PKCS#7 signature
* [in] time: signature verification time
* [in] signer: signer's X509 certificate
* [returns] 1 on error or 0 on success
*/
static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X509 *signer)
{
X509_CRL *crl = NULL;
STACK_OF(X509_CRL) *crls = NULL;
int verok = 0;
char *url = NULL;
X509_STORE *store = X509_STORE_new();
if (!store)
goto out;
if (!x509_store_load_file(store, ctx->options->cafile)) {
fprintf(stderr, "Failed to add store lookup file\n");
goto out; goto out;
} }
if (time != INVALID_TIME) {
printf("Signature verification time: ");
print_time_t(time);
if (!x509_store_set_time(store, time)) {
fprintf(stderr, "Failed to set signature time\n");
goto out;
}
} else if (ctx->options->time != INVALID_TIME) {
printf("Signature verification time: ");
print_time_t(ctx->options->time);
if (!x509_store_set_time(store, ctx->options->time)) {
fprintf(stderr, "Failed to set verifying time\n");
goto out;
}
}
/* verify a PKCS#7 signedData structure */
printf("Signing certificate chain verified using:\n"); printf("Signing certificate chain verified using:\n");
/* if (!verify_pkcs7_data(p7, store)) {
* In the PKCS7_verify() function, the BIO *indata parameter refers to
* the signed data if the content is detached from p7.
* Otherwise, indata should be NULL, and then the signed data must be in p7.
* The OpenSSL error workaround is to put the inner content into BIO *indata parameter
* https://github.com/openssl/openssl/pull/22575
*/
if (!PKCS7_verify(p7, NULL, store, bio, NULL, 0)) {
printf("PKCS7_verify error\n"); printf("PKCS7_verify error\n");
X509_STORE_free(store);
BIO_free(bio);
printf("Failed signing certificate chain retrieved from the signature:\n"); printf("Failed signing certificate chain retrieved from the signature:\n");
print_certs_chain(p7->d.sign->cert); print_certs_chain(p7->d.sign->cert);
goto out; goto out;
} }
X509_STORE_free(store);
BIO_free(bio);
/* verify a Certificate Revocation List */ /* verify a Certificate Revocation List */
if (!ctx->options->ignore_crl) { if (!ctx->options->ignore_crl) {
@ -2464,7 +2470,7 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
STACK_OF(X509) *chain = p7->d.sign->cert; STACK_OF(X509) *chain = p7->d.sign->cert;
int crlok = verify_crl(ctx->options->cafile, ctx->options->crlfile, int crlok = verify_crl(ctx->options->cafile, ctx->options->crlfile,
crls, signer, chain); crls, signer, chain);
sk_X509_CRL_pop_free(crls, X509_CRL_free);
printf("Signature CRL verification: %s\n", crlok ? "ok" : "failed"); printf("Signature CRL verification: %s\n", crlok ? "ok" : "failed");
if (!crlok) if (!crlok)
goto out; goto out;
@ -2477,6 +2483,8 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
verok = 1; /* OK */ verok = 1; /* OK */
out: out:
X509_STORE_free(store);
sk_X509_CRL_pop_free(crls, X509_CRL_free);
if (!verok) if (!verok)
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
return verok; return verok;