D2I_X509(3ossl)

πŸ“¦
version 15.1 Β· 3 versions
, in freebsd 15.1

πŸ’… d2i_X509(3ossl) β€” the OpenSSL one

okay so bestie, welcome to D2I_X509(3ossl), the OpenSSL manual page that starts by naming one function and then proceeds to name literally hundreds of them. The title is d2i_X509, but the page is basically a group chat where every ASN.1 type in OpenSSL gets added one after another. It's a lot. It's giving "the whole squad showed up". But it's also genuinely useful, so let's go 🫢


✨ NAME ✨

The whole gang converts objects from/to ASN.1/DER representation. That's the gig. That's the entire plot.

Here's the guest list, and no I'm not skipping anyone, that would be rude 😀

πŸŽ€ the d2i_ side (decode era)

the d2i_ crew takes DER bytes and turns them into real, usable objects. Behold:

d2i_ACCESS_DESCRIPTION, d2i_ADMISSIONS, d2i_ADMISSION_SYNTAX,
d2i_ASIdOrRange, d2i_ASIdentifierChoice, d2i_ASIdentifiers,
d2i_ASN1_BIT_STRING, d2i_ASN1_BMPSTRING, d2i_ASN1_ENUMERATED,
d2i_ASN1_GENERALIZEDTIME, d2i_ASN1_GENERALSTRING, d2i_ASN1_IA5STRING,
d2i_ASN1_INTEGER, d2i_ASN1_NULL, d2i_ASN1_OBJECT, d2i_ASN1_OCTET_STRING,
d2i_ASN1_PRINTABLE, d2i_ASN1_PRINTABLESTRING, d2i_ASN1_SEQUENCE_ANY,
d2i_ASN1_SET_ANY, d2i_ASN1_T61STRING, d2i_ASN1_TIME, d2i_ASN1_TYPE,
d2i_ASN1_UINTEGER, d2i_ASN1_UNIVERSALSTRING, d2i_ASN1_UTCTIME,
d2i_ASN1_UTF8STRING, d2i_ASN1_VISIBLESTRING, d2i_ASRange

look at those little string types go πŸ¦‹ they're all so specific and I respect it.

d2i_AUTHORITY_INFO_ACCESS, d2i_AUTHORITY_KEYID, d2i_BASIC_CONSTRAINTS,
d2i_CERTIFICATEPOLICIES, d2i_CMS_ContentInfo, d2i_CMS_ReceiptRequest,
d2i_CMS_bio, d2i_CRL_DIST_POINTS, d2i_DHxparams, d2i_DIRECTORYSTRING,
d2i_DISPLAYTEXT, d2i_DIST_POINT, d2i_DIST_POINT_NAME, d2i_DSA_SIG,
d2i_ECDSA_SIG, d2i_ECPKParameters, d2i_EDIPARTYNAME, d2i_ESS_CERT_ID,
d2i_ESS_CERT_ID_V2, d2i_ESS_ISSUER_SERIAL, d2i_ESS_SIGNING_CERT,
d2i_ESS_SIGNING_CERT_V2, d2i_EXTENDED_KEY_USAGE, d2i_GENERAL_NAME,
d2i_GENERAL_NAMES, d2i_IPAddressChoice, d2i_IPAddressFamily,
d2i_IPAddressOrRange, d2i_IPAddressRange, d2i_ISSUER_SIGN_TOOL,
d2i_ISSUING_DIST_POINT

the ESS_* family really said "we come in v1 and v2, and also v3 emotionally" πŸ’€

d2i_NAMING_AUTHORITY, d2i_NETSCAPE_CERT_SEQUENCE, d2i_NETSCAPE_SPKAC,
d2i_NETSCAPE_SPKI, d2i_NOTICEREF, d2i_OCSP_BASICRESP, d2i_OCSP_CERTID,
d2i_OCSP_CERTSTATUS, d2i_OCSP_CRLID, d2i_OCSP_ONEREQ, d2i_OCSP_REQINFO,
d2i_OCSP_REQUEST, d2i_OCSP_RESPBYTES, d2i_OCSP_RESPDATA, d2i_OCSP_RESPID,
d2i_OCSP_RESPONSE, d2i_OCSP_REVOKEDINFO, d2i_OCSP_SERVICELOC,
d2i_OCSP_SIGNATURE, d2i_OCSP_SINGLERESP, d2i_OSSL_AA_DIST_POINT,
d2i_OSSL_ALLOWED_ATTRIBUTES_CHOICE, d2i_OSSL_ALLOWED_ATTRIBUTES_ITEM,
d2i_OSSL_ALLOWED_ATTRIBUTES_SYNTAX, d2i_OSSL_ATAV,
d2i_OSSL_ATTRIBUTE_DESCRIPTOR, d2i_OSSL_ATTRIBUTE_MAPPING,
d2i_OSSL_ATTRIBUTE_MAPPINGS, d2i_OSSL_ATTRIBUTE_TYPE_MAPPING,
d2i_OSSL_ATTRIBUTE_VALUE_MAPPING, d2i_OSSL_ATTRIBUTES_SYNTAX,
d2i_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX, d2i_OSSL_BASIC_ATTR_CONSTRAINTS,
d2i_OSSL_CMP_ATAVS, d2i_OSSL_CMP_MSG, d2i_OSSL_CMP_PKIHEADER,
d2i_OSSL_CMP_PKISI, d2i_OSSL_CRMF_CERTID, d2i_OSSL_CRMF_CERTTEMPLATE,
d2i_OSSL_CRMF_ENCRYPTEDKEY, d2i_OSSL_CRMF_ENCRYPTEDVALUE,
d2i_OSSL_CRMF_MSG, d2i_OSSL_CRMF_MSGS, d2i_OSSL_CRMF_PBMPARAMETER,
d2i_OSSL_CRMF_PKIPUBLICATIONINFO, d2i_OSSL_CRMF_SINGLEPUBINFO

d2i_OSSL_... bestie there are SO many of you and I love that for you πŸ’œ

d2i_OSSL_DAY_TIME, d2i_OSSL_DAY_TIME_BAND, d2i_OSSL_HASH,
d2i_OSSL_IETF_ATTR_SYNTAX, d2i_OSSL_INFO_SYNTAX,
d2i_OSSL_INFO_SYNTAX_POINTER, d2i_OSSL_ISSUER_SERIAL,
d2i_OSSL_NAMED_DAY, d2i_OSSL_OBJECT_DIGEST_INFO,
d2i_OSSL_PRIVILEGE_POLICY_ID, d2i_OSSL_ROLE_SPEC_CERT_ID,
d2i_OSSL_ROLE_SPEC_CERT_ID_SYNTAX, d2i_OSSL_TARGET_CERT,
d2i_OSSL_TARGET, d2i_OSSL_TARGETING_INFORMATION, d2i_OSSL_TARGETS,
d2i_OSSL_TIME_PERIOD, d2i_OSSL_TIME_SPEC, d2i_OSSL_TIME_SPEC_ABSOLUTE,
d2i_OSSL_TIME_SPEC_DAY, d2i_OSSL_TIME_SPEC_MONTH,
d2i_OSSL_TIME_SPEC_TIME, d2i_OSSL_TIME_SPEC_WEEKS,
d2i_OSSL_TIME_SPEC_X_DAY_OF, d2i_OSSL_USER_NOTICE_SYNTAX, d2i_OTHERNAME

d2i_OSSL_TIME_SPEC_X_DAY_OF is such a main character name honestly πŸ‘‘

d2i_PBE2PARAM, d2i_PBEPARAM, d2i_PBKDF2PARAM, d2i_PBMAC1PARAM,
d2i_PKCS12, d2i_PKCS12_BAGS, d2i_PKCS12_MAC_DATA, d2i_PKCS12_SAFEBAG,
d2i_PKCS12_bio, d2i_PKCS12_fp, d2i_PKCS7, d2i_PKCS7_DIGEST,
d2i_PKCS7_ENCRYPT, d2i_PKCS7_ENC_CONTENT, d2i_PKCS7_ENVELOPE,
d2i_PKCS7_ISSUER_AND_SERIAL, d2i_PKCS7_RECIP_INFO, d2i_PKCS7_SIGNED,
d2i_PKCS7_SIGNER_INFO, d2i_PKCS7_SIGN_ENVELOPE, d2i_PKCS7_bio,
d2i_PKCS7_fp, d2i_PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_bio,
d2i_PKCS8_PRIV_KEY_INFO_fp, d2i_PKCS8_bio, d2i_PKCS8_fp,
d2i_PKEY_USAGE_PERIOD, d2i_POLICYINFO, d2i_POLICYQUALINFO,
d2i_PROFESSION_INFO, d2i_PROXY_CERT_INFO_EXTENSION, d2i_PROXY_POLICY,
d2i_RSA_OAEP_PARAMS, d2i_RSA_PSS_PARAMS, d2i_SCRYPT_PARAMS,
d2i_SCT_LIST, d2i_SXNET, d2i_SXNETID, d2i_TS_ACCURACY,
d2i_TS_MSG_IMPRINT, d2i_TS_MSG_IMPRINT_bio, d2i_TS_MSG_IMPRINT_fp,
d2i_TS_REQ, d2i_TS_REQ_bio, d2i_TS_REQ_fp, d2i_TS_RESP, d2i_TS_RESP_bio,
d2i_TS_RESP_fp, d2i_TS_STATUS_INFO, d2i_TS_TST_INFO,
d2i_TS_TST_INFO_bio, d2i_TS_TST_INFO_fp, d2i_USERNOTICE, d2i_X509,
d2i_X509_bio, d2i_X509_fp, d2i_X509_ACERT, d2i_X509_ACERT_bio,
d2i_X509_ACERT_fp, d2i_X509_ALGOR, d2i_X509_ALGORS, d2i_X509_ATTRIBUTE,
d2i_X509_CERT_AUX, d2i_X509_CINF, d2i_X509_CRL, d2i_X509_CRL_INFO,
d2i_X509_CRL_bio, d2i_X509_CRL_fp, d2i_X509_EXTENSION,
d2i_X509_EXTENSIONS, d2i_X509_NAME, d2i_X509_NAME_ENTRY,
d2i_X509_PUBKEY, d2i_X509_PUBKEY_bio, d2i_X509_PUBKEY_fp, d2i_X509_REQ,
d2i_X509_REQ_INFO, d2i_X509_REQ_bio, d2i_X509_REQ_fp, d2i_X509_REVOKED,
d2i_X509_SIG, d2i_X509_VAL

and there it is, d2i_X509 itself, the namesake, showing up fashionably late to its own page πŸ™ˆ

🌈 the i2d_ side (encode era)

the i2d_ crew does the reverse direction of the trip. Same energy, opposite journey:

i2d_ACCESS_DESCRIPTION, i2d_ADMISSIONS, i2d_ADMISSION_SYNTAX,
i2d_ASIdOrRange, i2d_ASIdentifierChoice, i2d_ASIdentifiers,
i2d_ASN1_BIT_STRING, i2d_ASN1_BMPSTRING, i2d_ASN1_ENUMERATED,
i2d_ASN1_GENERALIZEDTIME, i2d_ASN1_GENERALSTRING, i2d_ASN1_IA5STRING,
i2d_ASN1_INTEGER, i2d_ASN1_NULL, i2d_ASN1_OBJECT, i2d_ASN1_OCTET_STRING,
i2d_ASN1_PRINTABLE, i2d_ASN1_PRINTABLESTRING, i2d_ASN1_SEQUENCE_ANY,
i2d_ASN1_SET_ANY, i2d_ASN1_T61STRING, i2d_ASN1_TIME, i2d_ASN1_TYPE,
i2d_ASN1_UNIVERSALSTRING, i2d_ASN1_UTCTIME, i2d_ASN1_UTF8STRING,
i2d_ASN1_VISIBLESTRING, i2d_ASN1_bio_stream, i2d_ASRange
πŸ‘€ careful bestie, the two lists are not a perfect mirror. d2i_ASN1_UINTEGER lives on the decode side only, and i2d_ASN1_bio_stream lives on the encode side only. They're not typos, that's just how the page lists them. Read the list, don't assume the pattern.
i2d_AUTHORITY_INFO_ACCESS, i2d_AUTHORITY_KEYID, i2d_BASIC_CONSTRAINTS,
i2d_CERTIFICATEPOLICIES, i2d_CMS_ContentInfo, i2d_CMS_ReceiptRequest,
i2d_CMS_bio, i2d_CRL_DIST_POINTS, i2d_DHxparams, i2d_DIRECTORYSTRING,
i2d_DISPLAYTEXT, i2d_DIST_POINT, i2d_DIST_POINT_NAME, i2d_DSA_SIG,
i2d_ECDSA_SIG, i2d_ECPKParameters, i2d_EDIPARTYNAME, i2d_ESS_CERT_ID,
i2d_ESS_CERT_ID_V2, i2d_ESS_ISSUER_SERIAL, i2d_ESS_SIGNING_CERT,
i2d_ESS_SIGNING_CERT_V2, i2d_EXTENDED_KEY_USAGE, i2d_GENERAL_NAME,
i2d_GENERAL_NAMES, i2d_IPAddressChoice, i2d_IPAddressFamily,
i2d_IPAddressOrRange, i2d_IPAddressRange, i2d_ISSUER_SIGN_TOOL,
i2d_ISSUING_DIST_POINT
i2d_NAMING_AUTHORITY, i2d_NETSCAPE_CERT_SEQUENCE, i2d_NETSCAPE_SPKAC,
i2d_NETSCAPE_SPKI, i2d_NOTICEREF, i2d_OCSP_BASICRESP, i2d_OCSP_CERTID,
i2d_OCSP_CERTSTATUS, i2d_OCSP_CRLID, i2d_OCSP_ONEREQ, i2d_OCSP_REQINFO,
i2d_OCSP_REQUEST, i2d_OCSP_RESPBYTES, i2d_OCSP_RESPDATA, i2d_OCSP_RESPID,
i2d_OCSP_RESPONSE, i2d_OCSP_REVOKEDINFO, i2d_OCSP_SERVICELOC,
i2d_OCSP_SIGNATURE, i2d_OCSP_SINGLERESP, i2d_OSSL_AA_DIST_POINT,
i2d_OSSL_ALLOWED_ATTRIBUTES_CHOICE, i2d_OSSL_ALLOWED_ATTRIBUTES_ITEM,
i2d_OSSL_ALLOWED_ATTRIBUTES_SYNTAX, i2d_OSSL_ATAV,
i2d_OSSL_ATTRIBUTE_DESCRIPTOR, i2d_OSSL_ATTRIBUTE_MAPPING,
i2d_OSSL_ATTRIBUTE_MAPPINGS, i2d_OSSL_ATTRIBUTE_TYPE_MAPPING,
i2d_OSSL_ATTRIBUTE_VALUE_MAPPING, i2d_OSSL_ATTRIBUTES_SYNTAX,
i2d_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX, i2d_OSSL_BASIC_ATTR_CONSTRAINTS,
i2d_OSSL_CMP_ATAVS, i2d_OSSL_CMP_MSG, i2d_OSSL_CMP_PKIHEADER,
i2d_OSSL_CMP_PKISI, i2d_OSSL_CRMF_CERTID, i2d_OSSL_CRMF_CERTTEMPLATE,
i2d_OSSL_CRMF_ENCRYPTEDKEY, i2d_OSSL_CRMF_ENCRYPTEDVALUE,
i2d_OSSL_CRMF_MSG, i2d_OSSL_CRMF_MSGS, i2d_OSSL_CRMF_PBMPARAMETER,
i2d_OSSL_CRMF_PKIPUBLICATIONINFO, i2d_OSSL_CRMF_SINGLEPUBINFO
i2d_OSSL_HASH, i2d_OSSL_DAY_TIME, i2d_OSSL_DAY_TIME_BAND,
i2d_OSSL_IETF_ATTR_SYNTAX, i2d_OSSL_INFO_SYNTAX,
i2d_OSSL_INFO_SYNTAX_POINTER, i2d_OSSL_ISSUER_SERIAL,
i2d_OSSL_NAMED_DAY, i2d_OSSL_OBJECT_DIGEST_INFO,
i2d_OSSL_PRIVILEGE_POLICY_ID, i2d_OSSL_ROLE_SPEC_CERT_ID,
i2d_OSSL_ROLE_SPEC_CERT_ID_SYNTAX, i2d_OSSL_TARGET_CERT,
i2d_OSSL_TARGET, i2d_OSSL_TARGETING_INFORMATION, i2d_OSSL_TARGETS,
i2d_OSSL_TIME_PERIOD, i2d_OSSL_TIME_SPEC, i2d_OSSL_TIME_SPEC_ABSOLUTE,
i2d_OSSL_TIME_SPEC_DAY, i2d_OSSL_TIME_SPEC_MONTH,
i2d_OSSL_TIME_SPEC_TIME, i2d_OSSL_TIME_SPEC_WEEKS,
i2d_OSSL_TIME_SPEC_X_DAY_OF, i2d_OSSL_USER_NOTICE_SYNTAX, i2d_OTHERNAME

(and yes, i2d_OSSL_HASH really is listed before i2d_OSSL_DAY_TIME up there while d2i_OSSL_HASH comes after d2i_OSSL_DAY_TIME_BAND on the other list. The page do be like that sometimes πŸ’€ don't @ me, I just work here.)

i2d_PBE2PARAM, i2d_PBEPARAM, i2d_PBKDF2PARAM, i2d_PBMAC1PARAM,
i2d_PKCS12, i2d_PKCS12_BAGS, i2d_PKCS12_MAC_DATA, i2d_PKCS12_SAFEBAG,
i2d_PKCS12_bio, i2d_PKCS12_fp, i2d_PKCS7, i2d_PKCS7_DIGEST,
i2d_PKCS7_ENCRYPT, i2d_PKCS7_ENC_CONTENT, i2d_PKCS7_ENVELOPE,
i2d_PKCS7_ISSUER_AND_SERIAL, i2d_PKCS7_NDEF, i2d_PKCS7_RECIP_INFO,
i2d_PKCS7_SIGNED, i2d_PKCS7_SIGNER_INFO, i2d_PKCS7_SIGN_ENVELOPE,
i2d_PKCS7_bio, i2d_PKCS7_fp, i2d_PKCS8PrivateKeyInfo_bio,
i2d_PKCS8PrivateKeyInfo_fp, i2d_PKCS8_PRIV_KEY_INFO,
i2d_PKCS8_PRIV_KEY_INFO_bio, i2d_PKCS8_PRIV_KEY_INFO_fp, i2d_PKCS8_bio,
i2d_PKCS8_fp, i2d_PKEY_USAGE_PERIOD, i2d_POLICYINFO, i2d_POLICYQUALINFO,
i2d_PROFESSION_INFO, i2d_PROXY_CERT_INFO_EXTENSION, i2d_PROXY_POLICY,
i2d_RSA_OAEP_PARAMS, i2d_RSA_PSS_PARAMS, i2d_SCRYPT_PARAMS,
i2d_SCT_LIST, i2d_SXNET, i2d_SXNETID, i2d_TS_ACCURACY,
i2d_TS_MSG_IMPRINT, i2d_TS_MSG_IMPRINT_bio, i2d_TS_MSG_IMPRINT_fp,
i2d_TS_REQ, i2d_TS_REQ_bio, i2d_TS_REQ_fp, i2d_TS_RESP, i2d_TS_RESP_bio,
i2d_TS_RESP_fp, i2d_TS_STATUS_INFO, i2d_TS_TST_INFO,
i2d_TS_TST_INFO_bio, i2d_TS_TST_INFO_fp, i2d_USERNOTICE, i2d_X509,
i2d_X509_bio, i2d_X509_fp, i2d_X509_ACERT, i2d_X509_ACERT_bio,
i2d_X509_ACERT_fp, i2d_X509_ALGOR, i2d_X509_ALGORS, i2d_X509_ATTRIBUTE,
i2d_X509_CERT_AUX, i2d_X509_CINF, i2d_X509_CRL, i2d_X509_CRL_INFO,
i2d_X509_CRL_bio, i2d_X509_CRL_fp, i2d_X509_EXTENSION,
i2d_X509_EXTENSIONS, i2d_X509_NAME, i2d_X509_NAME_ENTRY,
i2d_X509_PUBKEY, i2d_X509_PUBKEY_bio, i2d_X509_PUBKEY_fp, i2d_X509_REQ,
i2d_X509_REQ_INFO, i2d_X509_REQ_bio, i2d_X509_REQ_fp, i2d_X509_REVOKED,
i2d_X509_SIG, i2d_X509_VAL

no cap, I scrolled for a while. But every single one of them is on the list, and that's the point 🫑

Also spotted only on the encode side, in case you go looking and can't find their twins: i2d_PKCS7_NDEF, i2d_PKCS8PrivateKeyInfo_bio, and i2d_PKCS8PrivateKeyInfo_fp. They're fine, they're just solo acts πŸ’…


πŸ¦‹ SYNOPSIS πŸ¦‹

okay so the shorthand: TYPE is whatever type you're working with, and the functions come in three flavours β€” the raw pointer version, a <italic>_bio</italic> version, and a <italic>_fp</italic> version. The whole shape of it is:

TYPE *d2i_TYPE(TYPE **a, const unsigned char **ppin, long length);
TYPE *d2i_TYPE_bio(BIO *bp, TYPE **a);
TYPE *d2i_TYPE_fp(FILE *fp, TYPE **a);

int i2d_TYPE(const TYPE *a, unsigned char **ppout);
int i2d_TYPE(TYPE *a, unsigned char **ppout);
int i2d_TYPE_fp(FILE *fp, const TYPE *a);
int i2d_TYPE_fp(FILE *fp, TYPE *a);
int i2d_TYPE_bio(BIO *bp, const TYPE *a);
int i2d_TYPE_bio(BIO *bp, TYPE *a);

the way that i2d_ gets listed twice for each shape β€” once with const TYPE *a and once with plain TYPE *a β€” is not me glitching, that's literally the page. Both of them are real. Const bestie and non-const bestie both get a seat 🌸

and that's the setup, bestie β€” the rest of the page is still coming, so don't wander off πŸ’–

πŸ’– DESCRIPTION πŸ’–

okay so first thing, bestie: in this whole page, TYPE is just a placeholder for any of the OpenSSL datatypes, like X509_CRL. And the parameters ppin and ppout? In the headers they're usually both just called pp, or sometimes in and out. iykyk.

These functions convert OpenSSL objects to and from their ASN.1/DER encoding. The tea here: the C structures can have pointers to sub-objects inside them, but DER is a serialized encoding β€” a glow-up into bytes, basically. It's what you actually send over the network, write to a file, and so on. πŸ¦‹

d2i_TYPE() tries to decode len bytes at *ppin. If it slays, you get back a pointer to the TYPE structure and *ppin gets incremented to the byte right after the parsed data. If a is not NULL, a pointer to the returned structure is also written to *a. If something went wrong, NULL comes back. The caller retains ownership of the returned object and needs to free it when it's no longer needed β€” like X509_free() for X509 objects, or DSA_SIG_free() for DSA_SIG objects. 🫢

On a successful return, if *a is not NULL then it's assumed that *a contains a valid TYPE structure, and an attempt is made to reuse it. For TYPE structures where it matters, you can set up a library context on the decoded structure this way (peep the EXAMPLES section). But using the "reuse" capability for other purposes is strongly discouraged β€” see BUGS below, and the discussion in the RETURN VALUES section. 🚩

d2i_TYPE_bio() is similar to d2i_TYPE() except it attempts to parse data from BIO bp.

d2i_TYPE_fp() is similar to d2i_TYPE() except it attempts to parse data from FILE pointer fp.

i2d_TYPE() encodes the structure pointed to by a into DER format. If ppout is not NULL, it writes the DER encoded data to the buffer at *ppout, and increments it to point after the data just written. If the return value is negative an error occurred; otherwise it returns the length of the encoded data. ✨

If *ppout is NULL, memory will be allocated for a buffer and the encoded data written to it. In this case *ppout is not incremented and it points to the start of the data just written.

i2d_TYPE_bio() is similar to i2d_TYPE() except it writes the encoding of the structure a to BIO bp and it returns 1 for success and 0 for failure.

i2d_TYPE_fp() is similar to i2d_TYPE() except it writes the encoding of the structure a to FILE pointer fp and it returns 1 for success and 0 for failure.

⚠️ These routines do not encrypt private keys and therefore offer no security. Use PEM_write_PrivateKey(3) or similar for writing to files. seriously. don't do it. πŸ’€

✨ NOTES ✨

The letters i and d in i2d_TYPE() stand for "internal" (that is, an internal C structure) and "DER" respectively. So i2d_TYPE() converts from internal to DER. cute, right? πŸŽ€

The functions can also understand BER forms.

The actual TYPE structure passed to i2d_TYPE() must be a valid, populated TYPE structure β€” it cannot simply be fed with an empty structure such as that returned by TYPE_new(). no empty vibes allowed, she needs content. πŸ™ˆ

The encoded data is in binary form and may contain embedded zeros. Therefore, any FILE pointers or BIOs should be opened in binary mode. Functions such as strlen() will not return the correct length of the encoded structure. (she's giving "binary, not text" energy, respect it.)

The ways that *ppin and *ppout are incremented after the operation can trap the unwary. See the WARNINGS section for some common errors. The reason for this auto-increment behaviour is to reflect a typical usage of ASN1 functions: after one structure is encoded or decoded, another will be processed after it. πŸ”₯

The following points about the data types might be useful:

ASN1_OBJECT

Represents an ASN1 OBJECT IDENTIFIER.

DHparams

Represents a PKCS#3 DH parameters structure.

DHxparams

Represents an ANSI X9.42 DH parameters structure.

ECDSA_SIG

Represents an ECDSA signature.

X509_ALGOR

Represents an AlgorithmIdentifier structure as used in IETF RFC 6960 and elsewhere.

X509_NAME

Represents a Name type as used for subject and issuer names in IETF RFC 6960 and elsewhere.

X509_REQ

Represents a PKCS#10 certificate request.

X509_SIG

Represents the DigestInfo structure defined in PKCS#1 and PKCS#7.


🌸 RETURN VALUES 🌸

d2i_TYPE(), d2i_TYPE_bio() and d2i_TYPE_fp() return a valid TYPE structure or NULL if an error occurs. If the "reuse" capability has been used with a valid structure being passed in via a, then the object is freed in the event of error and *a is set to NULL. so like, she takes the L for you. 😭

i2d_TYPE() returns the number of bytes successfully encoded or a negative value if an error occurs.

i2d_TYPE_bio() and i2d_TYPE_fp(), as well as i2d_ASN1_bio_stream(), return 1 for success and 0 if an error occurs.

On error, these functions may record the error in the OpenSSL error queue. That error queue can be inspected with the ERR family of functions, such as ERR_print_errors(3) and ERR_peek_last_error_all(3). go check the receipts, bestie. 🧾


πŸ¦‹ EXAMPLES πŸ¦‹

Allocate and encode the DER encoding of an X509 structure:

int len;
unsigned char *buf;

buf = NULL;
len = i2d_X509(x, &buf);
if (len < 0)
    /* error */

Attempt to decode a buffer:

X509 *x;
unsigned char *buf;
const unsigned char *p;
int len;

/* Set up buf and len to point to the input buffer. */
p = buf;
x = d2i_X509(NULL, &p, len);
if (x == NULL)
    /* error */

Alternative technique:

X509 *x;
unsigned char *buf;
const unsigned char *p;
int len;

/* Set up buf and len to point to the input buffer. */
p = buf;
x = NULL;

if (d2i_X509(&x, &p, len) == NULL)
    /* error */

Setting up a library context and property query:

X509 *x;
unsigned char *buf;
const unsigned char *p;
int len;
OSSL_LIB_CTX *libctx = ....;
const char *propq = ....;

/* Set up buf and len to point to the input buffer. */
p = buf;
x = X509_new_ex(libctx, propq);

if (d2i_X509(&x, &p, len) == NULL)
    /* error, x was freed and NULL assigned to it (see RETURN VALUES) */

πŸ’… WARNINGS πŸ’…

Using a temporary variable is mandatory. A common mistake is to attempt to use a buffer directly as follows:

int len;
unsigned char *buf;

len = i2d_X509(x, NULL);
buf = OPENSSL_malloc(len);
...
i2d_X509(x, &buf);
...
OPENSSL_free(buf);

This code will result in buf apparently containing garbage because it was incremented after the call to point after the data just written. Also buf will no longer contain the pointer allocated by OPENSSL_malloc() and the subsequent call to OPENSSL_free() is likely to crash. πŸ’€ not the segfault jump scare.

Another trap to avoid is misuse of the a argument to d2i_TYPE():

X509 *x;

if (d2i_X509(&x, &p, len) == NULL)
    /* error */

This will probably crash somewhere in d2i_X509(). The reason for this is that the variable x is uninitialized and an attempt will be made to interpret its (invalid) value as an X509 structure, typically causing a segmentation violation. If x is set to NULL first then this will not happen. set her to NULL, bestie. it's the bare minimum. πŸ™


πŸ“ BUGS πŸ“

In some versions of OpenSSL the "reuse" behaviour of d2i_TYPE() when *a is valid is broken and some parts of the reused structure may persist if they are not present in the new one. Additionally, in versions of OpenSSL prior to 1.1.0, when the "reuse" behaviour is used and an error occurs the behaviour is inconsistent. Some functions behaved as described here, while some did not free *a on error and did not set *a to NULL.

As a result of the above issues the "reuse" behaviour is strongly discouraged. πŸ‘‘ (iconic of them to say it twice.)

i2d_TYPE() will not return an error in many versions of OpenSSL β€” if mandatory fields are not initialized due to a programming error, then the encoded structure may contain invalid data or omit the fields entirely and will not be parsed by d2i_TYPE(). This may be fixed in future so code should not assume that i2d_TYPE() will always succeed. don't trust her blindly, ngl. πŸ”₯

Any function which encodes a structure (i2d_TYPE(), i2d_TYPE_bio() or i2d_TYPE_fp()) may return a stale encoding if the structure has been modified after deserialization or previous serialization. This is because some objects cache the encoding for efficiency reasons. (caching: slay for speed, cursed for freshness. 😭)


🌈 SEE ALSO 🌈

ERR_print_errors(3), ERR_peek_last_error_all(3)


⭐ HISTORY ⭐

d2i_OSSL_ATTRIBUTES_SYNTAX(), d2i_OSSL_BASIC_ATTR_CONSTRAINTS(), d2i_OSSL_CMP_ATAVS(), d2i_OSSL_IETF_ATTR_SYNTAX(), d2i_OSSL_TARGET(), d2i_OSSL_TARGETING_INFORMATION(), d2i_OSSL_TARGETS(), d2i_OSSL_USER_NOTICE_SYNTAX(), d2i_PBMAC1PARAM(), d2i_X509_ACERT(), d2i_X509_ACERT_bio(), d2i_X509_ACERT_fp(), i2d_OSSL_ATTRIBUTES_SYNTAX(), i2d_OSSL_BASIC_ATTR_CONSTRAINTS(), i2d_OSSL_CMP_ATAVS(), i2d_OSSL_IETF_ATTR_SYNTAX(), i2d_OSSL_TARGET(), i2d_OSSL_TARGETING_INFORMATION(), i2d_OSSL_TARGETS(), i2d_OSSL_USER_NOTICE_SYNTAX(), i2d_PBMAC1PARAM(), i2d_X509_ACERT(), i2d_X509_ACERT_bio(), i2d_X509_ACERT_fp() were added in OpenSSL 3.4.

d2i_OSSL_AA_DIST_POINT(), d2i_OSSL_ALLOWED_ATTRIBUTES_CHOICE(), d2i_OSSL_ALLOWED_ATTRIBUTES_ITEM(), d2i_OSSL_ALLOWED_ATTRIBUTES_SYNTAX(), d2i_OSSL_ATAV(), d2i_OSSL_ATTRIBUTE_DESCRIPTOR(), d2i_OSSL_ATTRIBUTE_MAPPING(), d2i_OSSL_ATTRIBUTE_MAPPINGS(), d2i_OSSL_ATTRIBUTE_TYPE_MAPPING(), d2i_OSSL_ATTRIBUTE_VALUE_MAPPING(), d2i_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX(), d2i_OSSL_HASH(), d2i_OSSL_INFO_SYNTAX(), d2i_OSSL_INFO_SYNTAX_POINTER(), d2i_OSSL_PRIVILEGE_POLICY_ID(), d2i_OSSL_ROLE_SPEC_CERT_ID(), d2i_OSSL_ROLE_SPEC_CERT_ID_SYNTAX(), i2d_OSSL_AA_DIST_POINT(), i2d_OSSL_ALLOWED_ATTRIBUTES_CHOICE(), i2d_OSSL_ALLOWED_ATTRIBUTES_ITEM(), i2d_OSSL_ALLOWED_ATTRIBUTES_SYNTAX(), i2d_OSSL_ATAV(), i2d_OSSL_ATTRIBUTE_DESCRIPTOR(), i2d_OSSL_ATTRIBUTE_MAPPING(), i2d_OSSL_ATTRIBUTE_MAPPINGS(), i2d_OSSL_ATTRIBUTE_TYPE_MAPPING(), i2d_OSSL_ATTRIBUTE_VALUE_MAPPING(), i2d_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX(), i2d_OSSL_HASH(), i2d_OSSL_INFO_SYNTAX(), i2d_OSSL_INFO_SYNTAX_POINTER(), i2d_OSSL_PRIVILEGE_POLICY_ID(), i2d_OSSL_ROLE_SPEC_CERT_ID(), i2d_OSSL_ROLE_SPEC_CERT_ID_SYNTAX(), d2i_OSSL_CRMF_ENCRYPTEDKEY(), i2d_OSSL_CRMF_ENCRYPTEDKEY(), d2i_OSSL_DAY_TIME(), d2i_OSSL_DAY_TIME_BAND(), d2i_OSSL_NAMED_DAY(), d2i_OSSL_TIME_PERIOD(), d2i_OSSL_TIME_SPEC(), d2i_OSSL_TIME_SPEC_ABSOLUTE(), d2i_OSSL_TIME_SPEC_DAY(), d2i_OSSL_TIME_SPEC_MONTH(), d2i_OSSL_TIME_SPEC_TIME(), d2i_OSSL_TIME_SPEC_WEEKS(), d2i_OSSL_TIME_SPEC_X_DAY_OF(), i2d_OSSL_DAY_TIME(), i2d_OSSL_DAY_TIME_BAND(), i2d_OSSL_NAMED_DAY(), i2d_OSSL_TIME_PERIOD(), i2d_OSSL_TIME_SPEC(), i2d_OSSL_TIME_SPEC_ABSOLUTE(), i2d_OSSL_TIME_SPEC_DAY(), i2d_OSSL_TIME_SPEC_MONTH(), i2d_OSSL_TIME_SPEC_TIME(), i2d_OSSL_TIME_SPEC_WEEKS(), i2d_OSSL_TIME_SPEC_X_DAY_OF() were added in OpenSSL 3.5.


okay so this is the legal bit, and yes we're reading it, because respecting open source licenses is literally the most main character energy there is πŸŽ€

Copyright 1998-2025 The OpenSSL Project Authors. All Rights Reserved.

no cap, all rights reserved by the OpenSSL Project Authors, 1998 through 2025 🫢

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. Like β€” you can use it, but only if you actually follow the rules in the License, iykyk πŸ™ˆ

You can obtain a copy of the License in the file LICENSE in the source distribution, or at https://www.openssl.org/source/license.html πŸ’…

fr fr bestie, read the License before you go shipping anything. "I didn't know" is not the flex you think it is πŸ’€

and that's the whole page, you made it all the way to the end, I'm so proud of you πŸ₯Ήβœ¨ This was D2I_X509(3ossl) from OpenSSL 3.5.6, dated 2026-04-07 β€” go forth and decode some certificates, you absolute icon πŸ‘‘πŸ’œ

πŸ“¦ where this page comes from