PEM_READ_BIO_PRIVATEKEY(3)

πŸ“¦ version 4.4.0, in alpine 3.24, arch rolling

πŸ¦‹ PEM routines β€” the manual page for PEM_READ_BIO_PRIVATEKEY(3) πŸ’…

ok so bestie, welcome to PEM_READ_BIO_PRIVATEKEY(3), section 3 of the library functions manual, and honestly? this page is a whole family reunion. it's not just one function, it's like eighty of them, all sharing one page, all named PEM_*, all living their best lives. it's giving "one giant group chat, everyone's typing at once" energy.

fair warning: this page is long. like, long. so it comes in parts and you're on part 1. stretch, hydrate, we're going in 🫢


✨ NAME ✨

ok so the roster. literally every one of these is in the NAME section of the original, so here's the full squad, no one left behind πŸ”₯

πŸ’– the PrivateKey girlies

  • PEM_read_bio_PrivateKey
  • PEM_read_PrivateKey
  • PEM_write_bio_PrivateKey
  • PEM_write_PrivateKey

πŸŽ€ the PKCS#8 encrypted-private-key crew

  • PEM_write_bio_PKCS8PrivateKey
  • PEM_write_PKCS8PrivateKey
  • PEM_write_bio_PKCS8PrivateKey_nid
  • PEM_write_PKCS8PrivateKey_nid

🌸 the raw PKCS#8 squad

  • PEM_read_bio_PKCS8
  • PEM_read_PKCS8
  • PEM_write_bio_PKCS8
  • PEM_write_PKCS8

🧚 PKCS#8 PRIV_KEY_INFO besties

  • PEM_read_bio_PKCS8_PRIV_KEY_INFO
  • PEM_read_PKCS8_PRIV_KEY_INFO
  • PEM_write_bio_PKCS8_PRIV_KEY_INFO
  • PEM_write_PKCS8_PRIV_KEY_INFO

πŸ‘‘ the generic PUBKEY icons

  • PEM_read_bio_PUBKEY
  • PEM_read_PUBKEY
  • PEM_write_bio_PUBKEY
  • PEM_write_PUBKEY

πŸ¦‹ RSA, the classic

  • PEM_read_bio_RSAPrivateKey
  • PEM_read_RSAPrivateKey
  • PEM_write_bio_RSAPrivateKey
  • PEM_write_RSAPrivateKey
  • PEM_read_bio_RSAPublicKey
  • PEM_read_RSAPublicKey
  • PEM_write_bio_RSAPublicKey
  • PEM_write_RSAPublicKey
  • PEM_read_bio_RSA_PUBKEY
  • PEM_read_RSA_PUBKEY
  • PEM_write_bio_RSA_PUBKEY
  • PEM_write_RSA_PUBKEY

🌈 DSA said "me too"

  • PEM_read_bio_DSAPrivateKey
  • PEM_read_DSAPrivateKey
  • PEM_write_bio_DSAPrivateKey
  • PEM_write_DSAPrivateKey
  • PEM_read_bio_DSA_PUBKEY
  • PEM_read_DSA_PUBKEY
  • PEM_write_bio_DSA_PUBKEY
  • PEM_write_DSA_PUBKEY
  • PEM_read_bio_DSAparams
  • PEM_read_DSAparams
  • PEM_write_bio_DSAparams
  • PEM_write_DSAparams

πŸ’€ Diffie–Hellman, the params only legend

  • PEM_read_bio_DHparams
  • PEM_read_DHparams
  • PEM_write_bio_DHparams
  • PEM_write_DHparams
notice there's no DHPrivateKey / DHPUBKEY up in here β€” that's not me being lazy, that's literally just what's in the NAME list. i don't make the rules, i just serve them πŸ’…

⭐ elliptic curve moment

  • PEM_read_bio_ECPKParameters
  • PEM_read_ECPKParameters
  • PEM_write_bio_ECPKParameters
  • PEM_write_ECPKParameters
  • PEM_read_bio_ECPrivateKey
  • PEM_read_ECPrivateKey
  • PEM_write_bio_ECPrivateKey
  • PEM_write_ECPrivateKey
  • PEM_read_bio_EC_PUBKEY
  • PEM_read_EC_PUBKEY
  • PEM_write_bio_EC_PUBKEY
  • PEM_write_EC_PUBKEY

πŸŽ€ X.509 certificates n stuff

  • PEM_read_bio_X509
  • PEM_read_X509
  • PEM_write_bio_X509
  • PEM_write_X509
  • PEM_read_bio_X509_AUX
  • PEM_read_X509_AUX
  • PEM_write_bio_X509_AUX
  • PEM_write_X509_AUX

πŸ’œ certificate signing requests (yes, both flavors)

  • PEM_read_bio_X509_REQ
  • PEM_read_X509_REQ
  • PEM_write_bio_X509_REQ
  • PEM_write_X509_REQ
  • PEM_write_bio_X509_REQ_NEW
  • PEM_write_X509_REQ_NEW

πŸ“ certificate revocation lists

  • PEM_read_bio_X509_CRL
  • PEM_read_X509_CRL
  • PEM_write_bio_X509_CRL
  • PEM_write_X509_CRL

🌸 PKCS#7 + CMS, the fancy packaging

  • PEM_read_bio_PKCS7
  • PEM_read_PKCS7
  • PEM_write_bio_PKCS7
  • PEM_write_PKCS7
  • PEM_read_CMS
  • PEM_read_bio_CMS
  • PEM_write_CMS
  • PEM_write_bio_CMS

and the entire point of this whole page, in three words: PEM routines ✨ that's the descriptor. that's what all eighty-something of these little icons collectively are.


🌟 SYNOPSIS 🌟

ok so the vibe here is ridiculously consistent: every single read function takes a stream / a double pointer / a pem_password_cb *cb / a void *u, and every write function takes a stream / the object (plus encryption bits if it's a private key). once you clock the pattern, the whole page is just one pattern with a hundred wigs on.

🫢 library + header

library β€œlibcrypto” #include <openssl/pem.h>

πŸ”‘ PrivateKey (the generic EVP_PKEY ones)

EVP_PKEY *
PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
    void *u);

EVP_PKEY *
PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
    void *u);

int
PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

πŸŽ€ PKCS#8 private key, encrypted flavors

int
PEM_write_bio_PKCS8PrivateKey(BIO *bp, EVP_PKEY *x,
    const EVP_CIPHER *enc, char *kstr, int klen, pem_password_cb *cb,
    void *u);

int
PEM_write_PKCS8PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
    char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid,
    char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid,
    char *kstr, int klen, pem_password_cb *cb, void *u);

🌸 raw PKCS#8 (X509_SIG)

X509_SIG *
PEM_read_bio_PKCS8(BIO *bp, X509_SIG **x, pem_password_cb *cb, void *u);

X509_SIG *
PEM_read_PKCS8(FILE *fp, X509_SIG **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_PKCS8(BIO *bp, X509_SIG *x);

int
PEM_write_PKCS8(FILE *fp, X509_SIG *x);

🧚 PKCS8_PRIV_KEY_INFO

PKCS8_PRIV_KEY_INFO *
PEM_read_bio_PKCS8_PRIV_KEY_INFO(BIO *bp, PKCS8_PRIV_KEY_INFO **x,
    pem_password_cb *cb, void *u);

PKCS8_PRIV_KEY_INFO *
PEM_read_PKCS8_PRIV_KEY_INFO(FILE *fp, PKCS8_PRIV_KEY_INFO **x,
    pem_password_cb *cb, void *u);

int
PEM_write_bio_PKCS8_PRIV_KEY_INFO(BIO *bp, PKCS8_PRIV_KEY_INFO *x);

int
PEM_write_PKCS8_PRIV_KEY_INFO(FILE *fp, PKCS8_PRIV_KEY_INFO *x);

πŸ‘‘ generic PUBKEY

EVP_PKEY *
PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
    void *u);

EVP_PKEY *
PEM_read_PUBKEY(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x);

int
PEM_write_PUBKEY(FILE *fp, EVP_PKEY *x);

πŸ¦‹ RSA (all four flavors, bestie)

RSA *
PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb,
    void *u);

RSA *
PEM_read_RSAPrivateKey(FILE *fp, RSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_RSAPrivateKey(FILE *fp, RSA *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

RSA *
PEM_read_bio_RSAPublicKey(BIO *bp, RSA **x, pem_password_cb *cb,
    void *u);

RSA *
PEM_read_RSAPublicKey(FILE *fp, RSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_RSAPublicKey(BIO *bp, RSA *x);

int
PEM_write_RSAPublicKey(FILE *fp, RSA *x);

RSA *
PEM_read_bio_RSA_PUBKEY(BIO *bp, RSA **x, pem_password_cb *cb, void *u);

RSA *
PEM_read_RSA_PUBKEY(FILE *fp, RSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_RSA_PUBKEY(BIO *bp, RSA *x);

int
PEM_write_RSA_PUBKEY(FILE *fp, RSA *x);

🌈 DSA

DSA *
PEM_read_bio_DSAPrivateKey(BIO *bp, DSA **x, pem_password_cb *cb,
    void *u);

DSA *
PEM_read_DSAPrivateKey(FILE *fp, DSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_DSAPrivateKey(BIO *bp, DSA *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_DSAPrivateKey(FILE *fp, DSA *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

DSA *
PEM_read_bio_DSA_PUBKEY(BIO *bp, DSA **x, pem_password_cb *cb, void *u);

DSA *
PEM_read_DSA_PUBKEY(FILE *fp, DSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_DSA_PUBKEY(BIO *bp, DSA *x);

int
PEM_write_DSA_PUBKEY(FILE *fp, DSA *x);

DSA *
PEM_read_bio_DSAparams(BIO *bp, DSA **x, pem_password_cb *cb, void *u);

DSA *
PEM_read_DSAparams(FILE *fp, DSA **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_DSAparams(BIO *bp, DSA *x);

int
PEM_write_DSAparams(FILE *fp, DSA *x);

πŸ’€ DH params (and only params, we been knew)

DH *
PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u);

DH *
PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_DHparams(BIO *bp, DH *x);

int
PEM_write_DHparams(FILE *fp, DH *x);

⭐ EC πŸ₯°

EC_GROUP *
PEM_read_bio_ECPKParameters(BIO *bp, EC_GROUP **x, pem_password_cb *cb,
    void *u);

EC_GROUP *
PEM_read_ECPKParameters(FILE *fp, EC_GROUP **x, pem_password_cb *cb,
    void *u);

int
PEM_write_bio_ECPKParameters(BIO *bp, const EC_GROUP *x);

int
PEM_write_ECPKParameters(FILE *fp, const EC_GROUP *x);

EC_KEY *
PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb,
    void *u);

EC_KEY *
PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
    void *u);

int
PEM_write_bio_ECPrivateKey(BIO *bp, EC_KEY *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

int
PEM_write_ECPrivateKey(FILE *fp, EC_KEY *x, const EVP_CIPHER *enc,
    unsigned char *kstr, int klen, pem_password_cb *cb, void *u);

EC_KEY *
PEM_read_bio_EC_PUBKEY(BIO *bp, EC_KEY **x, pem_password_cb *cb,
    void *u);

EC_KEY *
PEM_read_EC_PUBKEY(FILE *fp, EC_KEY **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_EC_PUBKEY(BIO *bp, EC_KEY *x);

int
PEM_write_EC_PUBKEY(FILE *fp, EC_KEY *x);
side note i am obsessed with the fact that PEM_read_bio_ECPrivateKey calls its param key and PEM_read_ECPrivateKey calls it eckey. iconic inconsistency. not changing a single character of it πŸ’…

πŸŽ€ X.509 certs + _AUX

X509 *
PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u);

X509 *
PEM_read_X509(FILE *fp, X509 **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_X509(BIO *bp, X509 *x);

int
PEM_write_X509(FILE *fp, X509 *x);

X509 *
PEM_read_bio_X509_AUX(BIO *bp, X509 **x, pem_password_cb *cb, void *u);

X509 *
PEM_read_X509_AUX(FILE *fp, X509 **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_X509_AUX(BIO *bp, X509 *x);

int
PEM_write_X509_AUX(FILE *fp, X509 *x);

πŸ’œ CSRs + the mysterious _NEW writers

X509_REQ *
PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x, pem_password_cb *cb,
    void *u);

X509_REQ *
PEM_read_X509_REQ(FILE *fp, X509_REQ **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_X509_REQ(BIO *bp, X509_REQ *x);

int
PEM_write_X509_REQ(FILE *fp, X509_REQ *x);

int
PEM_write_bio_X509_REQ_NEW(BIO *bp, X509_REQ *x);

int
PEM_write_X509_REQ_NEW(FILE *fp, X509_REQ *x);

πŸ“ CRLs (cert revocation, not crumbs)

X509_CRL *
PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x, pem_password_cb *cb,
    void *u);

X509_CRL *
PEM_read_X509_CRL(FILE *fp, X509_CRL **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_X509_CRL(BIO *bp, X509_CRL *x);

int
PEM_write_X509_CRL(FILE *fp, X509_CRL *x);

🌸 PKCS#7

PKCS7 *
PEM_read_bio_PKCS7(BIO *bp, PKCS7 **x, pem_password_cb *cb, void *u);

PKCS7 *
PEM_read_PKCS7(FILE *fp, PKCS7 **x, pem_password_cb *cb, void *u);

int
PEM_write_bio_PKCS7(BIO *bp, PKCS7 *x);

int
PEM_write_PKCS7(FILE *fp, PKCS7 *x);

🧚 CMS β€” note the different header, bestie!!

#include <openssl/cms.h>

CMS_ContentInfo *
PEM_read_CMS(FILE *fp, CMS_ContentInfo **x, pem_password_cb *cb,
    void *u);

CMS_ContentInfo *
PEM_read_bio_CMS(BIO *bp, CMS_ContentInfo **x, pem_password_cb *cb,
    void *u);

int
PEM_write_CMS(FILE *fp, const CMS_ContentInfo *x);

int
PEM_write_bio_CMS(BIO *bp, const CMS_ContentInfo *x);

ok so that's the setup: the entire name drop and every single prototype, untouched. keep reading for the actual DESCRIPTION energy, because that's where the lore lives πŸ‘€

πŸ’– DESCRIPTION πŸ’–

Okay so bestie, here's the whole vibe: the PEM functions read or write structures in PEM format. And what even is PEM format, you ask? It's literally just base64-encoded data surrounded by header lines β€” that's it, that's the whole thing. See PEM_read(3) for more details.

For more details about the meaning of arguments, check the β€œPEM function arguments” section below. πŸ‘‡

Every single operation has four functions associated with it. For brevity, the term β€œTYPE functions” is gonna be used to collectively refer to PEM_read_bio_TYPE(), PEM_read_TYPE(), PEM_write_bio_TYPE(), and PEM_write_TYPE(). And if no set of specific functions exists for a given type, PEM_ASN1_read(3) can be used instead. Slay.


🧚 PEM function arguments 🧚

The PEM functions have a lot of common arguments, so let's go through them:

  • bp β€” the BIO to read from or write to.
  • fp β€” the FILE pointer to read from or write to.

Okay so the PEM read functions all take a pointer-to-pointer argument x and return a pointer of the same type. Here's the breakdown, because this is the part everyone gets confused about:

  • If x is NULL, then the parameter is ignored.
  • If x is not NULL but *x is NULL, then the structure returned will be written to *x.
  • If neither x nor *x are NULL, then an attempt is made to reuse the structure at *x, but bestie, please see the β€œBUGS” and β€œEXAMPLES” sections first. 😭

Irrespective of the value of x, a pointer to the structure is always returned, or NULL if an error occurred.

The PEM functions which write private keys take an enc parameter, which specifies the encryption algorithm to use. Encryption is done at the PEM level. If this parameter is set to NULL, then the private key is written in unencrypted form. 🫣

The optional arguments u and cb are a passphrase used for encrypting a PEM structure or a callback to obtain the passphrase; see pem_password_cb(3) for details.

For the PEM write routines, if the kstr parameter is not NULL, then klen bytes at kstr are used as the passphrase and cb is ignored. Bye cb, you're not needed. πŸ’…


πŸ” PEM encryption format πŸ”

Ngl, these old PrivateKey routines use a non-standard technique for encryption. Just so you know.

The private key (or other data) takes the following form:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,3F17F5316E2BAC89

...base64 encoded data...
-----END RSA PRIVATE KEY-----

The line beginning with β€œDEK-Info” contains two comma separated pieces of information: the encryption algorithm name as used by EVP_get_cipherbyname(3) and an 8-byte salt encoded as a set of hexadecimal digits. Cute, right?

After this is the base64-encoded encrypted data.

The encryption key is determined using EVP_BytesToKey(3), using the salt and an iteration count of 1. And heads up β€” the IV used is the value of the salt and not the IV returned by EVP_BytesToKey(3). That's a real gotcha, so don't mix those up, iykyk. πŸ™ˆ


🌈 RETURN VALUES 🌈

  • The read routines return either a pointer to the structure read or NULL if an error occurred.
  • The write routines return 1 for success or 0 for failure.

Periodt. That's the whole return value situation.


✨ EXAMPLES ✨

Okay so the PEM routines take several arguments, but in almost all applications most of them are set to 0 or NULL. So you'll see that pattern a lot below.

Read a certificate in PEM format from a BIO:

X509 *x;
x = PEM_read_bio_X509(bp, NULL, 0, NULL);
if (x == NULL) {
        /* Error */
}

Alternative method:

X509 *x = NULL;
if (!PEM_read_bio_X509(bp, &x, 0, NULL)) {
        /* Error */
}

Write a certificate to a BIO:

if (!PEM_write_bio_X509(bp, x)) {
        /* Error */
}

Write an unencrypted private key to a FILE:

if (!PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL)) {
        /* Error */
}

Write a private key (using traditional format) to a BIO using triple DES encryption; the pass phrase is prompted for:

if (!PEM_write_bio_PrivateKey(bp, key, EVP_des_ede3_cbc(),
    NULL, 0, 0, NULL)) {
        /* Error */
}

Write a private key (using PKCS#8 format) to a BIO using triple DES encryption, using the pass phrase "hello":

if (!PEM_write_bio_PKCS8PrivateKey(bp, key, EVP_des_ede3_cbc(),
    NULL, 0, 0, "hello")) {
        /* Error */
}

Read a private key from a BIO using the pass phrase "hello":

key = PEM_read_bio_PrivateKey(bp, NULL, 0, "hello");
if (key == NULL) {
        /* Error */
}

Read a private key from a BIO using a pass phrase callback:

key = PEM_read_bio_PrivateKey(bp, NULL, pass_cb, "My Private Key");
if (key == NULL) {
        /* Error */
}

Skeleton pass phrase callback β€” a whole icon, honestly:

int
pass_cb(char *buf, int size, int rwflag, void *u)
{
        char    *tmp;
        size_t   len;

        /* We'd probably do something else if 'rwflag' is 1 */
        printf("Enter pass phrase for \"%s\"\n", u);

        /*
         * Instead of the following line, get the passphrase
         * from the user in some way.
         */
        tmp = "hello";
        if (tmp == NULL) /* An error occurred. */
                return -1;

        len = strlen(tmp);
        if (len == 0) /* Treat an empty passphrase as an error, too. */
                return -1;

        /* if too long, truncate */
        if (len > size)
                len = size;
        memcpy(buf, tmp, len);
        return len;
}

πŸ‘€ SEE ALSO πŸ‘€

Go read these, bestie, they're all linked and relevant:

BIO_new(3), DSA_new(3), PEM_ASN1_read(3), PEM_bytes_read_bio(3), PEM_read(3), PEM_read_SSL_SESSION(3), PEM_write_bio_CMS_stream(3), PEM_write_bio_PKCS7_stream(3), PEM_X509_INFO_read_bio(3), RSA_new(3), X509_CRL_new(3), X509_REQ_new(3), X509_SIG_new(3)

🌈 HISTORY

okay bestie, storytime πŸ“– β€” this page has lore, and I mean a lot of it. Buckle up.

PEM_read_X509() and PEM_write_X509() are the OGs β€” they showed up in SSLeay 0.4 or earlier πŸ¦‹

Then PEM_read_X509_REQ(), PEM_write_X509_REQ(), PEM_read_X509_CRL(), and PEM_write_X509_CRL() first appeared in SSLeay 0.4.4.

Next batch, SSLeay 0.5.1 energy: PEM_read_RSAPrivateKey(), PEM_write_RSAPrivateKey(), PEM_read_DHparams(), PEM_write_DHparams(), PEM_read_PKCS7(), and PEM_write_PKCS7().

Then SSLeay 0.6.0 dropped a whole squad πŸ‘‘:

  • PEM_read_bio_PrivateKey()
  • PEM_read_PrivateKey()
  • PEM_read_bio_RSAPrivateKey()
  • PEM_write_bio_RSAPrivateKey()
  • PEM_read_bio_DSAPrivateKey()
  • PEM_read_DSAPrivateKey()
  • PEM_write_bio_DSAPrivateKey()
  • PEM_write_DSAPrivateKey()
  • PEM_read_bio_DHparams()
  • PEM_write_bio_DHparams()
  • PEM_read_bio_X509()
  • PEM_write_bio_X509()
  • PEM_read_bio_X509_REQ()
  • PEM_write_bio_X509_REQ()
  • PEM_read_bio_X509_CRL()
  • PEM_write_bio_X509_CRL()
  • PEM_read_bio_PKCS7()
  • PEM_write_bio_PKCS7()

SSLeay 0.8.0 gave us PEM_write_bio_PrivateKey(), PEM_write_PrivateKey(), PEM_read_bio_DSAparams(), PEM_read_DSAparams(), PEM_write_bio_DSAparams(), and PEM_write_DSAparams() ✨

And SSLeay 0.8.1 brought PEM_read_bio_RSAPublicKey(), PEM_read_RSAPublicKey(), PEM_write_bio_RSAPublicKey(), and PEM_write_RSAPublicKey().

All these functions have been available since OpenBSD 2.4 πŸ’–


Next era, and it's a long one β€” all of these first appeared in OpenSSL 0.9.5 and have been available since OpenBSD 2.7 🌸

PEM_write_bio_PKCS8PrivateKey(), PEM_write_PKCS8PrivateKey(), PEM_read_bio_PKCS8(), PEM_read_PKCS8(), PEM_write_bio_PKCS8(), PEM_write_PKCS8(), PEM_read_bio_PKCS8_PRIV_KEY_INFO(), PEM_read_PKCS8_PRIV_KEY_INFO(), PEM_write_bio_PKCS8_PRIV_KEY_INFO(), PEM_write_PKCS8_PRIV_KEY_INFO(),

PEM_write_bio_PKCS8PrivateKey_nid(), PEM_write_PKCS8PrivateKey_nid(), PEM_read_bio_PUBKEY(), PEM_read_PUBKEY(), PEM_write_bio_PUBKEY(), PEM_write_PUBKEY(), PEM_read_bio_RSA_PUBKEY(), PEM_read_RSA_PUBKEY(), PEM_write_bio_RSA_PUBKEY(), PEM_write_RSA_PUBKEY(), PEM_read_bio_DSA_PUBKEY(), PEM_read_DSA_PUBKEY(), PEM_write_bio_DSA_PUBKEY(), PEM_write_DSA_PUBKEY(), PEM_write_bio_X509_REQ_NEW(), PEM_write_X509_REQ_NEW(), PEM_read_bio_X509_AUX(), PEM_read_X509_AUX(), PEM_write_bio_X509_AUX(), and PEM_write_X509_AUX().


ECC girlies rise 🧚 β€” PEM_read_bio_ECPKParameters(), PEM_read_ECPKParameters(), PEM_write_bio_ECPKParameters(), PEM_write_ECPKParameters(), PEM_read_bio_ECPrivateKey(), PEM_read_ECPrivateKey(), PEM_write_bio_ECPrivateKey(), PEM_write_ECPrivateKey(), PEM_read_bio_EC_PUBKEY(), PEM_read_EC_PUBKEY(), PEM_write_bio_EC_PUBKEY(), and PEM_write_EC_PUBKEY() first appeared in OpenSSL 0.9.8 and have been available since OpenBSD 4.5 πŸ’œ


And the most recent glow-up: PEM_read_CMS(), PEM_read_bio_CMS(), PEM_write_CMS(), and PEM_write_bio_CMS() first appeared in OpenSSL 0.9.8h and have been available since OpenBSD 6.7 ⭐

πŸ’€ CAVEATS

lowkey the #1 way people mess this up 🫠 β€” trying to use the PEM routines like this:

      X509 *x;
      PEM_read_bio_X509(bp, &x, 0, NULL);

This is a bug because an attempt will be made to reuse the data at x, which is an uninitialised pointer. Not it. πŸ™ˆ

These functions make no assumption regarding the pass phrase received from the password callback. It will simply be treated as a byte sequence. That's it, that's the whole vibe β€” bytes are bytes, no judgment πŸ’…

πŸ› BUGS

ngl there's a known situation here 🫢 β€” the PEM read routines in some versions of OpenSSL will not correctly reuse an existing structure. Therefore:

      PEM_read_bio_X509(bp, &x, 0, NULL);

where x already contains a valid certificate, may not work, whereas

      X509_free(x);
      x = PEM_read_bio_X509(bp, NULL, 0, NULL);

is guaranteed to work. So like β€” free first, then read. Trust. ✨


and that's the whole page bestie, you ate it πŸ’…βœ¨

This one ships with the Debian flavour, dated July 16, 2025, and it's PEM_READ_BIO_PRIVATEKEY(3) β€” go off, you absolute icon πŸ‘‘πŸ¦‹πŸ’–

πŸ“¦ where this page comes from