CMS_DECRYPT(3ossl)

πŸ’– CMS_DECRYPT πŸ’–

okay bestie, so this is the OpenSSL manual page for CMS_decrypt and its little squad of helper functions β€” CMS_decrypt_set1_pkey_and_peer, CMS_decrypt_set1_pkey, and CMS_decrypt_set1_password. we're in 3ossl territory here, so buckle up πŸŽ€


✨ NAME ✨

CMS_decrypt, CMS_decrypt_set1_pkey_and_peer, CMS_decrypt_set1_pkey, CMS_decrypt_set1_password β€” these are the girls who decrypt content from a CMS envelopedData structure πŸ’…


🌸 SYNOPSIS 🌸

#include <openssl/cms.h>

int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pkey, X509 *cert,
                BIO *dcont, BIO *out, unsigned int flags);
int CMS_decrypt_set1_pkey_and_peer(CMS_ContentInfo *cms,
                EVP_PKEY *pk, X509 *cert, X509 *peer);
int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert);
int CMS_decrypt_set1_password(CMS_ContentInfo *cms,
                              unsigned char *pass, ossl_ssize_t passlen);

πŸ¦‹ DESCRIPTION πŸ¦‹

CMS_decrypt() pulls the decrypted content out of a CMS EnvelopedData or AuthEnvelopedData structure. it does the deed by calling CMS_decrypt_set1_pkey() to decrypt the content with the recipient private key pkey β€” but only if pkey isn't NULL. in that case, you're recommended (like, strongly, iykyk) to also hand over the associated certificate in cert β€” the NOTES below spill all the tea on why. out is the BIO the content gets written to, and flags is your optional little set of flags. if pkey is NULL, the function assumes decryption already happened (say, via CMS_decrypt_set1_pkey() or CMS_decrypt_set1_password()) and it just serves you the content β€” unless cert, dcont, and out are NULL too. and the dcont parameter? that's for the rare girlboss scenario where the encrypted content is detached. normally you just set it to NULL and move on.

CMS_decrypt_set1_pkey_and_peer() decrypts the CMS_ContentInfo structure cms using the private key pkey, the corresponding certificate cert (recommended, but may be NULL), and the (optional) originator certificate peer. on success it also records the decryption key pkey inside cms, and then you should follow it up with "CMS_decrypt(cms, NULL, NULL, dcont, out, flags)". heads up: this call deallocates any decryption key stored in cms. 🫢

CMS_decrypt_set1_pkey() is literally the same as CMS_decrypt_set1_pkey_and_peer() except peer is NULL. periodt.

CMS_decrypt_set1_password() decrypts the CMS_ContentInfo structure cms using the secret pass of length passlen. on success it also records the decryption key used inside cms, and then β€” same drill β€” you should follow it with "CMS_decrypt(cms, NULL, NULL, dcont, out, flags)". this call deallocates any decryption key stored in cms too. πŸ’€


πŸ‘‘ NOTES πŸ‘‘

so even though the recipient's certificate isn't technically needed to decrypt the data, it is needed to locate the right recipient among possibly several in the CMS structure. it's giving "you don't need her but she'll find your girl for you."

if cert is set to NULL, all possible recipients get tried. sounds fine, right? bestie, it's problematic. to thwart the MMA attack (that's Bleichenbacher's attack on PKCS #1 v1.5 RSA padding), every recipient gets tried whether it succeeds or not. and if none of them succeed, a random symmetric key is used to decrypt the content β€” which will typically output garbage and may (but is not guaranteed to) ultimately return a padding error only. why bother? because if CMS_decrypt() just returned an error whenever all the recipient encrypted keys failed to decrypt, an attacker could weaponise that in a timing attack. timing attacks are not the vibe. πŸ™ˆ

if the special flag CMS_DEBUG_DECRYPT is set, all that behaviour above gets modified, and an error is returned if no recipient encrypted key can be decrypted without generating a random content encryption key. > Applications should use this flag with extreme caution, especially in automated gateways, because it can leave them wide open to attack. see EVP_PKEY_decrypt(3) for more details, bestie.

you can determine the correct recipient key by other means (like looking them up in a database πŸ—‚οΈ) and set them in the CMS structure in advance using the CMS utility functions such as CMS_set1_pkey(), or use CMS_decrypt_set1_password() if the recipient has a symmetric key. in those cases, both cert and pkey should be set to NULL.

to process KEKRecipientInfo types, CMS_set1_key() or CMS_RecipientInfo_set0_key() and CMS_RecipientInfo_decrypt() should be called before CMS_decrypt(), with cert and pkey set to NULL.

the following flags can be passed in the flags parameter:

  • if the CMS_TEXT flag is set, MIME headers for type "text/plain" are deleted from the content. if the content is not of type "text/plain", then an error is returned. no notes. ✨

πŸ’« RETURN VALUES πŸ’«

CMS_decrypt(), CMS_decrypt_set1_pkey_and_peer(), CMS_decrypt_set1_pkey(), and CMS_decrypt_set1_password() return either 1 for success or 0 for failure. the error can be obtained from ERR_get_error(3). slay when it's 1, sad girl hours when it's 0. 😭


πŸ’€ BUGS πŸ’€

the set1_ part of these function names is misleading and should better read: with_. like, the naming committee fumbled, ngl.

the lack of single pass processing and the need to hold all data in memory as mentioned in CMS_verify() also applies to CMS_decrypt(). so yeah, memory girl, that's you now.


🌈 SEE ALSO 🌈

ERR_get_error(3), CMS_encrypt(3), EVP_PKEY_decrypt(3)


⭐ HISTORY ⭐

CMS_decrypt_set1_pkey_and_peer() and CMS_decrypt_set1_password() were added in OpenSSL 3.0. the glow-up era. πŸ’…


Copyright 2008-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 <https://www.openssl.org/source/license.html>.


okay that's the whole page bestie, no cap 🫢 this was OpenSSL version 3.5.9, dated 2026-09-29 β€” literally iconic behavior from the OpenSSL girlies. go decrypt something and stay safe out there, mwah πŸ’–βœ¨

πŸ“¦ where this page comes from