Commit 02d0f65c9f for openssl.org

commit 02d0f65c9f8579cd1625a8d084ee8479093beb1b
Author: Todd Short <todd.short@me.com>
Date:   Fri Jul 17 13:35:56 2026 -0400

    CMS/PKCS7: use EVP_CIPHER_fetch() for SMIMECapabilities

    Fixes #25919

    Replace EVP_get_cipherbynid()/EVP_get_digestbynid() with
    EVP_CIPHER_fetch()/EVP_MD_fetch() when building SMIMECapabilities
    attributes, so that only algorithms available in the active providers
    are advertised. This prevents RC2, DES, and GOST from appearing by
    default when only the default provider is loaded.

    Add CMS_add_standard_smimecap_ex() as a provider-aware replacement
    for the newly-deprecated CMS_add_standard_smimecap().

    Adds missing documention and new unit-tests validating the new behavior.

    Signed-off-by: Todd Short <todd.short@me.com>

    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Reviewed-by: Jakub Zelenka <jakub.zelenka@openssl.foundation>
    MergeDate: Mon Aug  3 07:06:44 2026
    (Merged from https://github.com/openssl/openssl/pull/31990)

diff --git a/CHANGES.md b/CHANGES.md
index 9b1dbcd979..4e5b021968 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -31,6 +31,15 @@ OpenSSL Releases

 ### Changes between 4.0 and 4.1 [xx XXX xxxx]

+ * Added `CMS_add_standard_smimecap_ex()`, which populates an SMIMECapabilities
+   list using `EVP_CIPHER_fetch()` and `EVP_MD_fetch()` so that only algorithms
+   available in the active providers are advertised.  `PKCS7_sign_add_signer()`
+   was updated in the same way, so that legacy ciphers such as RC2 and DES are
+   no longer included in SMIMECapabilities by default when only the default
+   provider is loaded.
+
+   *Todd Short*
+
  * Added various optimizations for the Elbrus2000 architecture in the
    cryptographic and BN code.

diff --git a/crypto/cms/cms_sd.c b/crypto/cms/cms_sd.c
index e53a523207..20f60235aa 100644
--- a/crypto/cms/cms_sd.c
+++ b/crypto/cms/cms_sd.c
@@ -677,7 +677,8 @@ CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
         if (!(flags & CMS_NOSMIMECAP)) {
             STACK_OF(X509_ALGOR) *smcap = NULL;

-            i = CMS_add_standard_smimecap(&smcap);
+            i = CMS_add_standard_smimecap_ex(&smcap, ossl_cms_ctx_get0_libctx(ctx),
+                ossl_cms_ctx_get0_propq(ctx));
             if (i)
                 i = CMS_add_smimecap(si, smcap);
             sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
@@ -1584,34 +1585,47 @@ int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs,
 }

 /* Check to see if a cipher exists and if so add S/MIME capabilities */
-static int cms_add_cipher_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
+static int cms_add_cipher_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg,
+    OSSL_LIB_CTX *libctx, const char *propq)
 {
-    if (EVP_get_cipherbynid(nid))
+    EVP_CIPHER *cipher = EVP_CIPHER_fetch(libctx, OBJ_nid2sn(nid), propq);
+
+    if (cipher != NULL) {
+        EVP_CIPHER_free(cipher);
         return CMS_add_simple_smimecap(sk, nid, arg);
+    }
     return 1;
 }

-static int cms_add_digest_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
+static int cms_add_digest_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg,
+    OSSL_LIB_CTX *libctx, const char *propq)
 {
-    if (EVP_get_digestbynid(nid))
+    EVP_MD *md = EVP_MD_fetch(libctx, OBJ_nid2sn(nid), propq);
+
+    if (md != NULL) {
+        EVP_MD_free(md);
         return CMS_add_simple_smimecap(sk, nid, arg);
+    }
     return 1;
 }

-int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap)
+int CMS_add_standard_smimecap_ex(STACK_OF(X509_ALGOR) **smcap,
+    OSSL_LIB_CTX *libctx, const char *propq)
 {
-    if (!cms_add_cipher_smcap(smcap, NID_aes_256_cbc, -1)
-        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1)
-        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1)
-        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_94, -1)
-        || !cms_add_cipher_smcap(smcap, NID_id_Gost28147_89, -1)
-        || !cms_add_cipher_smcap(smcap, NID_aes_192_cbc, -1)
-        || !cms_add_cipher_smcap(smcap, NID_aes_128_cbc, -1)
-        || !cms_add_cipher_smcap(smcap, NID_des_ede3_cbc, -1)
-        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 128)
-        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 64)
-        || !cms_add_cipher_smcap(smcap, NID_des_cbc, -1)
-        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 40))
+    if (!cms_add_cipher_smcap(smcap, NID_aes_256_cbc, -1, libctx, propq)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1, libctx, propq)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1, libctx, propq)
+        || !cms_add_digest_smcap(smcap, NID_id_GostR3411_94, -1, libctx, propq)
+        || !cms_add_cipher_smcap(smcap, NID_id_Gost28147_89, -1, libctx, propq)
+        || !cms_add_cipher_smcap(smcap, NID_aes_192_cbc, -1, libctx, propq)
+        || !cms_add_cipher_smcap(smcap, NID_aes_128_cbc, -1, libctx, propq)
+        || !cms_add_cipher_smcap(smcap, NID_des_ede3_cbc, -1, libctx, propq)
+        || !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 128, libctx, propq))
         return 0;
     return 1;
 }
+
+int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap)
+{
+    return CMS_add_standard_smimecap_ex(smcap, NULL, NULL);
+}
diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c
index 7ede4b6694..6bd33245d1 100644
--- a/crypto/pkcs7/pk7_smime.c
+++ b/crypto/pkcs7/pk7_smime.c
@@ -97,17 +97,27 @@ err:

 /* Check to see if a cipher exists and if so add S/MIME capabilities */

-static int add_cipher_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg)
+static int add_cipher_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg,
+    OSSL_LIB_CTX *libctx, const char *propq)
 {
-    if (EVP_get_cipherbynid(nid))
+    EVP_CIPHER *cipher = EVP_CIPHER_fetch(libctx, OBJ_nid2sn(nid), propq);
+
+    if (cipher != NULL) {
+        EVP_CIPHER_free(cipher);
         return PKCS7_simple_smimecap(sk, nid, arg);
+    }
     return 1;
 }

-static int add_digest_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg)
+static int add_digest_smcap(STACK_OF(X509_ALGOR) *sk, int nid, int arg,
+    OSSL_LIB_CTX *libctx, const char *propq)
 {
-    if (EVP_get_digestbynid(nid))
+    EVP_MD *md = EVP_MD_fetch(libctx, OBJ_nid2sn(nid), propq);
+
+    if (md != NULL) {
+        EVP_MD_free(md);
         return PKCS7_simple_smimecap(sk, nid, arg);
+    }
     return 1;
 }

@@ -117,6 +127,8 @@ PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *p7, X509 *signcert,
 {
     PKCS7_SIGNER_INFO *si = NULL;
     STACK_OF(X509_ALGOR) *smcap = NULL;
+    OSSL_LIB_CTX *libctx;
+    const char *propq;

     if (!X509_check_private_key(signcert, pkey)) {
         ERR_raise(ERR_LIB_PKCS7,
@@ -144,18 +156,17 @@ PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *p7, X509 *signcert,
                 ERR_raise(ERR_LIB_PKCS7, ERR_R_CRYPTO_LIB);
                 goto err;
             }
-            if (!add_cipher_smcap(smcap, NID_aes_256_cbc, -1)
-                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1)
-                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1)
-                || !add_digest_smcap(smcap, NID_id_GostR3411_94, -1)
-                || !add_cipher_smcap(smcap, NID_id_Gost28147_89, -1)
-                || !add_cipher_smcap(smcap, NID_aes_192_cbc, -1)
-                || !add_cipher_smcap(smcap, NID_aes_128_cbc, -1)
-                || !add_cipher_smcap(smcap, NID_des_ede3_cbc, -1)
-                || !add_cipher_smcap(smcap, NID_rc2_cbc, 128)
-                || !add_cipher_smcap(smcap, NID_rc2_cbc, 64)
-                || !add_cipher_smcap(smcap, NID_des_cbc, -1)
-                || !add_cipher_smcap(smcap, NID_rc2_cbc, 40)
+            libctx = ossl_pkcs7_ctx_get0_libctx(si->ctx);
+            propq = ossl_pkcs7_ctx_get0_propq(si->ctx);
+            if (!add_cipher_smcap(smcap, NID_aes_256_cbc, -1, libctx, propq)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_256, -1, libctx, propq)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_2012_512, -1, libctx, propq)
+                || !add_digest_smcap(smcap, NID_id_GostR3411_94, -1, libctx, propq)
+                || !add_cipher_smcap(smcap, NID_id_Gost28147_89, -1, libctx, propq)
+                || !add_cipher_smcap(smcap, NID_aes_192_cbc, -1, libctx, propq)
+                || !add_cipher_smcap(smcap, NID_aes_128_cbc, -1, libctx, propq)
+                || !add_cipher_smcap(smcap, NID_des_ede3_cbc, -1, libctx, propq)
+                || !add_cipher_smcap(smcap, NID_rc2_cbc, 128, libctx, propq)
                 || !PKCS7_add_attrib_smimecap(si, smcap))
                 goto err;
             sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
diff --git a/doc/build.info b/doc/build.info
index c36ff802c7..1ed8458bc6 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -827,6 +827,10 @@ DEPEND[html/man3/CMS_add1_signer.html]=man3/CMS_add1_signer.pod
 GENERATE[html/man3/CMS_add1_signer.html]=man3/CMS_add1_signer.pod
 DEPEND[man/man3/CMS_add1_signer.3]=man3/CMS_add1_signer.pod
 GENERATE[man/man3/CMS_add1_signer.3]=man3/CMS_add1_signer.pod
+DEPEND[html/man3/CMS_add_standard_smimecap_ex.html]=man3/CMS_add_standard_smimecap_ex.pod
+GENERATE[html/man3/CMS_add_standard_smimecap_ex.html]=man3/CMS_add_standard_smimecap_ex.pod
+DEPEND[man/man3/CMS_add_standard_smimecap_ex.3]=man3/CMS_add_standard_smimecap_ex.pod
+GENERATE[man/man3/CMS_add_standard_smimecap_ex.3]=man3/CMS_add_standard_smimecap_ex.pod
 DEPEND[html/man3/CMS_compress.html]=man3/CMS_compress.pod
 GENERATE[html/man3/CMS_compress.html]=man3/CMS_compress.pod
 DEPEND[man/man3/CMS_compress.3]=man3/CMS_compress.pod
@@ -3266,6 +3270,7 @@ html/man3/CMS_EnvelopedData_create.html \
 html/man3/CMS_add0_cert.html \
 html/man3/CMS_add1_recipient_cert.html \
 html/man3/CMS_add1_signer.html \
+html/man3/CMS_add_standard_smimecap_ex.html \
 html/man3/CMS_compress.html \
 html/man3/CMS_data_create.html \
 html/man3/CMS_decrypt.html \
@@ -3943,6 +3948,7 @@ man/man3/CMS_EnvelopedData_create.3 \
 man/man3/CMS_add0_cert.3 \
 man/man3/CMS_add1_recipient_cert.3 \
 man/man3/CMS_add1_signer.3 \
+man/man3/CMS_add_standard_smimecap_ex.3 \
 man/man3/CMS_compress.3 \
 man/man3/CMS_data_create.3 \
 man/man3/CMS_decrypt.3 \
diff --git a/doc/man3/CMS_add1_signer.pod b/doc/man3/CMS_add1_signer.pod
index 58b8bcc51d..a24abce583 100644
--- a/doc/man3/CMS_add1_signer.pod
+++ b/doc/man3/CMS_add1_signer.pod
@@ -76,10 +76,10 @@ and serial number. If B<CMS_USE_KEYID> is set it will use the subject key
 identifier value instead. An error occurs if the signing certificate does not
 have a subject key identifier extension.

-If present the SMIMECapabilities attribute indicates support for the following
-algorithms in preference order: 256 bit AES, Gost R3411-94, Gost 28147-89, 192
-bit AES, 128 bit AES, triple DES, 128 bit RC2, 64 bit RC2, DES and 40 bit RC2.
-If any of these algorithms is not available then it will not be included.
+If present, the SMIMECapabilities attribute indicates support for only the
+algorithms that are available in the providers associated with the
+B<CMS_ContentInfo> structure. See L<CMS_add_standard_smimecap_ex(3)> for the
+full candidate list and further details.

 Note that, in the case signedAttributes are not used, for some hash-less signing
 schemes the given hash B<md> will be ignored and a hash required by the signing
@@ -109,7 +109,7 @@ L<CMS_final(3)>,

 =head1 COPYRIGHT

-Copyright 2014-2025 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2014-2026 The OpenSSL Project Authors. All Rights Reserved.

 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
diff --git a/doc/man3/CMS_add_standard_smimecap_ex.pod b/doc/man3/CMS_add_standard_smimecap_ex.pod
new file mode 100644
index 0000000000..226d81635d
--- /dev/null
+++ b/doc/man3/CMS_add_standard_smimecap_ex.pod
@@ -0,0 +1,85 @@
+=pod
+
+=head1 NAME
+
+CMS_add_standard_smimecap_ex, CMS_add_standard_smimecap,
+CMS_add_smimecap, CMS_add_simple_smimecap
+- CMS SMIMECapabilities attribute utilities
+
+=head1 SYNOPSIS
+
+ #include <openssl/cms.h>
+
+ int CMS_add_standard_smimecap_ex(STACK_OF(X509_ALGOR) **smcap,
+                                  OSSL_LIB_CTX *libctx, const char *propq);
+ int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap);
+
+ int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs);
+ int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs,
+                             int algnid, int keysize);
+
+=head1 DESCRIPTION
+
+CMS_add_standard_smimecap_ex() populates B<*smcap> with the set of
+algorithms that should be advertised in an SMIMECapabilities signed
+attribute.  Only algorithms that are available in the providers
+associated with B<libctx> and the property query string B<propq> are
+included, so the list accurately reflects what the caller can actually
+use.  If B<libctx> is NULL the default library context is used.  If
+B<*smcap> is NULL a new B<STACK_OF(X509_ALGOR)> is allocated; otherwise
+entries are appended to the existing stack.
+
+The candidate algorithms, listed in preference order, are: AES-256-CBC,
+GOST R 34.11-2012 (256-bit), GOST R 34.11-2012 (512-bit), GOST R
+34.11-94, GOST 28147-89, AES-192-CBC, AES-128-CBC, DES-EDE3-CBC,
+RC2-CBC (128-bit key), RC2-CBC (64-bit key), DES-CBC, and RC2-CBC
+(40-bit key).  Algorithms not available in the active providers
+(for example, RC2 and DES when only the default provider is loaded)
+are silently omitted.
+
+CMS_add_standard_smimecap() is a wrapper that calls
+CMS_add_standard_smimecap_ex() with a NULL library context and NULL
+property query string.
+
+CMS_add_smimecap() adds a pre-built stack of algorithm identifiers
+B<algs> as the SMIMECapabilities signed attribute on the
+B<CMS_SignerInfo> B<si>.
+
+CMS_add_simple_smimecap() appends a single algorithm entry to B<*algs>.
+B<algnid> is the NID of the algorithm and B<keysize> is the key size in
+bits, or -1 if the algorithm does not use a variable key size.
+
+=head1 NOTES
+
+Applications that need accurate capability advertisements should use
+CMS_add_standard_smimecap_ex(), passing the same B<libctx> and B<propq>
+used for the rest of the CMS operation.  This ensures that only
+algorithms genuinely available to the application are listed.
+
+CMS_add1_signer() calls CMS_add_standard_smimecap_ex() internally using
+the library context associated with the CMS_ContentInfo structure, so
+applications that use CMS_add1_signer() without B<CMS_NOSMIMECAP> do not
+need to call these functions directly.
+
+=head1 RETURN VALUES
+
+All functions return 1 for success or 0 for failure.
+
+=head1 SEE ALSO
+
+L<CMS_add1_signer(3)>, L<CMS_sign(3)>, L<OSSL_LIB_CTX(3)>
+
+=head1 HISTORY
+
+CMS_add_standard_smimecap_ex() was added in OpenSSL 4.1.
+
+=head1 COPYRIGHT
+
+Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/openssl/cms.h.in b/include/openssl/cms.h.in
index 20af42015c..1184e12626 100644
--- a/include/openssl/cms.h.in
+++ b/include/openssl/cms.h.in
@@ -312,6 +312,8 @@ BIO *CMS_SignedData_verify(CMS_SignedData *sd, BIO *detached_data,
 int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs);
 int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs,
     int algnid, int keysize);
+int CMS_add_standard_smimecap_ex(STACK_OF(X509_ALGOR) **smcap,
+    OSSL_LIB_CTX *libctx, const char *propq);
 int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap);

 int CMS_signed_get_attr_count(const CMS_SignerInfo *si);
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
index 977095b3f8..d1851b4121 100644
--- a/test/cmsapitest.c
+++ b/test/cmsapitest.c
@@ -277,6 +277,44 @@ static int test_encrypt_decrypt_aes_256_gcm(void)
     return test_encrypt_decrypt(EVP_aes_256_gcm());
 }

+static int smimecap_has_nid(STACK_OF(X509_ALGOR) *smcap, int nid)
+{
+    int i;
+
+    for (i = 0; i < sk_X509_ALGOR_num(smcap); i++) {
+        X509_ALGOR *alg = sk_X509_ALGOR_value(smcap, i);
+        if (OBJ_obj2nid(alg->algorithm) == nid)
+            return 1;
+    }
+    return 0;
+}
+
+static int test_CMS_add_standard_smimecap_ex(void)
+{
+    STACK_OF(X509_ALGOR) *smcap = NULL;
+    int ret = 0;
+
+    if (!TEST_true(CMS_add_standard_smimecap_ex(&smcap, NULL, NULL)))
+        goto end;
+
+    /* AES ciphers must be present with the default provider */
+    if (!TEST_true(smimecap_has_nid(smcap, NID_aes_256_cbc))
+        || !TEST_true(smimecap_has_nid(smcap, NID_aes_192_cbc))
+        || !TEST_true(smimecap_has_nid(smcap, NID_aes_128_cbc)))
+        goto end;
+
+    /* RC2, DES, and GOST must NOT be present with just the default provider */
+    if (!TEST_false(smimecap_has_nid(smcap, NID_rc2_cbc))
+        || !TEST_false(smimecap_has_nid(smcap, NID_des_cbc))
+        || !TEST_false(smimecap_has_nid(smcap, NID_id_Gost28147_89)))
+        goto end;
+
+    ret = 1;
+end:
+    sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
+    return ret;
+}
+
 static int test_CMS_add1_cert(void)
 {
     CMS_ContentInfo *cms = NULL;
@@ -831,6 +869,7 @@ int setup_tests(void)
     ADD_TEST(test_non_aead_on_auth_envelope_enc);
     ADD_TEST(test_non_aead_on_auth_envelope_dec);
     ADD_TEST(test_short_mac_on_auth_envelope_data);
+    ADD_TEST(test_CMS_add_standard_smimecap_ex);
     ADD_TEST(test_CMS_add1_cert);
     ADD_TEST(test_d2i_CMS_bio_NULL);
     ADD_TEST(test_CMS_set1_key_mem_leak);
diff --git a/test/pkcs7_test.c b/test/pkcs7_test.c
index 2fe7064c01..b48cbd847a 100644
--- a/test/pkcs7_test.c
+++ b/test/pkcs7_test.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -8,6 +8,7 @@
  */

 #include <string.h>
+#include <openssl/objects.h>
 #include <openssl/pkcs7.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
@@ -15,6 +16,57 @@
 #include "internal/nelem.h"
 #include "testutil.h"

+static X509 *smimecap_cert = NULL;
+static EVP_PKEY *smimecap_privkey = NULL;
+
+static int smimecap_has_nid(STACK_OF(X509_ALGOR) *smcap, int nid)
+{
+    int i;
+
+    for (i = 0; i < sk_X509_ALGOR_num(smcap); i++) {
+        X509_ALGOR *alg = sk_X509_ALGOR_value(smcap, i);
+        if (OBJ_obj2nid(alg->algorithm) == nid)
+            return 1;
+    }
+    return 0;
+}
+
+static int test_pkcs7_smimecap(void)
+{
+    PKCS7 *p7 = NULL;
+    PKCS7_SIGNER_INFO *si = NULL;
+    STACK_OF(X509_ALGOR) *smcap = NULL;
+    int ret = 0;
+
+    if (!TEST_ptr(p7 = PKCS7_new())
+        || !TEST_true(PKCS7_set_type(p7, NID_pkcs7_signed))
+        || !TEST_true(PKCS7_content_new(p7, NID_pkcs7_data))
+        || !TEST_ptr(si = PKCS7_sign_add_signer(p7, smimecap_cert,
+                         smimecap_privkey, NULL, 0)))
+        goto end;
+
+    if (!TEST_ptr(smcap = PKCS7_get_smimecap(si)))
+        goto end;
+
+    /* AES ciphers must be present with the default provider */
+    if (!TEST_true(smimecap_has_nid(smcap, NID_aes_256_cbc))
+        || !TEST_true(smimecap_has_nid(smcap, NID_aes_192_cbc))
+        || !TEST_true(smimecap_has_nid(smcap, NID_aes_128_cbc)))
+        goto end;
+
+    /* RC2, DES, and GOST must NOT be present with just the default provider */
+    if (!TEST_false(smimecap_has_nid(smcap, NID_rc2_cbc))
+        || !TEST_false(smimecap_has_nid(smcap, NID_des_cbc))
+        || !TEST_false(smimecap_has_nid(smcap, NID_id_Gost28147_89)))
+        goto end;
+
+    ret = 1;
+end:
+    sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
+    PKCS7_free(p7);
+    return ret;
+}
+
 static int pkcs7_issuer_and_serial_negative_idx_test(void)
 {
     PKCS7 *p7 = NULL;
@@ -462,6 +514,27 @@ static int pkcs7_stream_enveloped_signed_no_content_test(void)

 int setup_tests(void)
 {
+    const char *certin, *privkeyin;
+    BIO *bio = NULL;
+
+    if (!test_skip_common_options()) {
+        TEST_error("Error parsing test options\n");
+        return 0;
+    }
+
+    certin = test_get_argument(0);
+    privkeyin = test_get_argument(1);
+    if (certin != NULL && privkeyin != NULL) {
+        if (TEST_ptr(bio = BIO_new_file(certin, "r"))) {
+            PEM_read_bio_X509(bio, &smimecap_cert, NULL, NULL);
+            BIO_free(bio);
+        }
+        if (TEST_ptr(bio = BIO_new_file(privkeyin, "r"))) {
+            PEM_read_bio_PrivateKey(bio, &smimecap_privkey, NULL, NULL);
+            BIO_free(bio);
+        }
+    }
+
     ADD_TEST(pkcs7_issuer_and_serial_negative_idx_test);
 #ifndef OPENSSL_NO_EC
     ADD_TEST(pkcs7_verify_test);
@@ -469,5 +542,13 @@ int setup_tests(void)
 #endif /* OPENSSL_NO_EC */
     ADD_TEST(pkcs7_stream_enveloped_no_content_test);
     ADD_TEST(pkcs7_stream_enveloped_signed_no_content_test);
+    if (smimecap_cert != NULL && smimecap_privkey != NULL)
+        ADD_TEST(test_pkcs7_smimecap);
     return 1;
 }
+
+void cleanup_tests(void)
+{
+    X509_free(smimecap_cert);
+    EVP_PKEY_free(smimecap_privkey);
+}
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index bd7ed2cc1e..c3d75e6e29 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -58,7 +58,8 @@ $no_rc2 = 1 if disabled("legacy");

 plan tests => 41;

-ok(run(test(["pkcs7_test"])), "test pkcs7");
+ok(run(test(["pkcs7_test", srctop_file("test", "certs", "servercert.pem"),
+             srctop_file("test", "certs", "serverkey.pem")])), "test pkcs7");

 unless ($no_fips) {
     my $provconf = srctop_file("test", "fips-and-base.cnf");
diff --git a/util/libcrypto.num b/util/libcrypto.num
index c7b7777cc4..0acb162dde 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5727,3 +5727,4 @@ EVP_KDF_CTX_get1_kdf                    ?	4_1_0	EXIST::FUNCTION:
 ASN1_STRING_set_data                    ?	4_1_0	EXIST::FUNCTION:
 ASN1_STRING_set_string                  ?	4_1_0	EXIST::FUNCTION:
 ASN1_STRING_length_ex                   ?	4_1_0	EXIST::FUNCTION:
+CMS_add_standard_smimecap_ex            ?	4_1_0	EXIST::FUNCTION:CMS
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 5e8126d12c..b49861044d 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -285,9 +285,6 @@ CMS_SignerInfo_get0_pkey_ctx(3)
 CMS_add0_CertificateChoices(3)
 CMS_add0_RevocationInfoChoice(3)
 CMS_add0_recipient_password(3)
-CMS_add_simple_smimecap(3)
-CMS_add_smimecap(3)
-CMS_add_standard_smimecap(3)
 CMS_data(3)
 CMS_dataInit(3)
 CMS_decrypt_set1_key(3)