EVP_PKEY_DECRYPT(3SSL)

✨ EVP_PKEY_DECRYPT ✨

okay bestie, gather round πŸ’– this is the 3SSL manual page for the decryption squad: EVP_PKEY_decrypt_init, EVP_PKEY_decrypt_init_ex, and EVP_PKEY_decrypt. it's literally "decrypt using a public key algorithm" and it's giving main character energy, no cap. πŸ”


🌸 NAME 🌸

EVP_PKEY_decrypt_init, EVP_PKEY_decrypt_init_ex, EVP_PKEY_decrypt β€” decrypt using a public key algorithm


πŸŽ€ SYNOPSIS πŸŽ€

#include <openssl/evp.h>

int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_decrypt_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,
                     unsigned char *out, size_t *outlen,
                     const unsigned char *in, size_t inlen);

πŸ’… DESCRIPTION πŸ’…

so here's the tea. πŸ«–

  • EVP_PKEY_decrypt_init() β€” this one sets up the public key algorithm context ctx for a decryption operation. but heads up: a key must already be associated with ctx. normally you'd do that by creating it with EVP_PKEY_CTX_new(3) or EVP_PKEY_CTX_new_from_pkey(3). no key, no party. 🚫
  • EVP_PKEY_decrypt_init_ex() β€” same vibe as EVP_PKEY_decrypt_init() but it additionally sets the algorithm-specific params. it's the init function but with extra sparkle. ✨
  • EVP_PKEY_decrypt() β€” this is the one that actually does the public key decryption using ctx. the data you wanna decrypt goes in in and inlen. if out is NULL, then the minimum required size of the output buffer gets written to the *outlen parameter (a lil recon mission, we love her πŸ‘€).

if out is not NULL, then before the call the *outlen parameter must contain the length of the out buffer. if the call succeeds, the decrypted data gets written to out and the amount of decrypted data written goes to *outlen. otherwise? an error is returned. 😭


πŸ“ NOTES πŸ“

after you call EVP_PKEY_decrypt_init(), you can do algorithm-specific control operations to set any appropriate parameters for the operation. and guess what β€” those operations can also be included in the EVP_PKEY_decrypt_init_ex() call. efficiency queen. πŸ‘‘

also: EVP_PKEY_decrypt() can be called more than once on the same context if you're doing several operations with the same parameters. reuse, recycle, slay. πŸ”


⭐ RETURN VALUES ⭐

EVP_PKEY_decrypt_init(), EVP_PKEY_decrypt_init_ex() and EVP_PKEY_decrypt() return 1 for success and 0 or a negative value for failure.

and specifically: a return value of -2 means the operation is not supported by the public key algorithm. πŸ’€


🚨 WARNINGS 🚨

okay this part is serious, bestie, listen up. πŸ‘‚

In OpenSSL versions before 3.2.0, when used in PKCS#1 v1.5 padding, both the return value from EVP_PKEY_decrypt() and the outlen provided information useful in mounting a Bleichenbacher attack against the used private key. They had to be processed in a side-channel free way. 😱

Since version 3.2.0, the EVP_PKEY_decrypt() method when used with PKCS#1 v1.5 padding as implemented in the default provider implements the implicit rejection mechanism (see OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION in provider-asym_cipher(7)).

what does that mean fr fr? it means it doesn't return an error when it detects an error in padding β€” instead it returns a pseudo-randomly generated message, removing the need for side-channel secure code from applications using OpenSSL. iconic behaviour honestly. πŸŽ€

⚠️ but WAIT β€” if OpenSSL is configured to use a provider that doesn't implement implicit rejection, your code still needs to handle the returned values using side-channel free code. do not skip this.

Side-channel free handling of the error stack can be performed using either:

either way, stay safe out there. 🫢


🌈 EXAMPLES 🌈

decrypt data using OAEP (for RSA keys):

#include <openssl/evp.h>
#include <openssl/rsa.h>

EVP_PKEY_CTX *ctx;
unsigned char *out, *in;
size_t outlen, inlen;
EVP_PKEY *key;

/*
 * NB: assumes key, in, inlen are already set up
 * and that key is an RSA private key
 */
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, key, NULL);
if (!ctx)
    /* Error occurred */
if (EVP_PKEY_decrypt_init(ctx) <= 0)
    /* Error */
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
    /* Error */

/* Determine buffer length */
if (EVP_PKEY_decrypt(ctx, NULL, &outlen, in, inlen) <= 0)
    /* Error */

out = OPENSSL_malloc(outlen);

if (!out)
    /* malloc failure */

if (EVP_PKEY_decrypt(ctx, out, &outlen, in, inlen) <= 0)
    /* Error */

/* Decrypted data is outlen bytes written to buffer out */

see how she does the two-pass thing with outlen first? that's the buffer-length recon step. so clean. 🧼


πŸ‘€ SEE ALSO πŸ‘€

EVP_PKEY_CTX_new(3), EVP_PKEY_encrypt(3), EVP_PKEY_sign(3), EVP_PKEY_verify(3), EVP_PKEY_verify_recover(3), EVP_PKEY_derive(3)

go read those too, they're the whole friend group. πŸ«‚


πŸ¦‹ HISTORY πŸ¦‹

these functions were added in OpenSSL 1.0.0. old money, honestly. πŸ’œ


Copyright 2006-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>.


and that's the whole page, bestie!! πŸŽ‰ this was 4.0.3, dated 2026-09-29, from the EVP_PKEY_DECRYPT(3SSL) manual. go forth and decrypt responsibly, mwah πŸ’–πŸ’…βœ¨

πŸ“¦ where this page comes from