LIBRESSL-X509_NAME_GET_INDEX_BY_NID(3)

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

πŸ’… X509_NAME_GET_INDEX_BY_NID(3)

okay so bestie, this is the X509_NAME_GET_INDEX_BY_NID(3) page from the Library Functions Manual β€” it's giving "how do I actually dig through an X509_NAME" energy, and honestly? iconic. let's get into it πŸŽ€


✨ NAME ✨

X509_NAME_get_index_by_NID, X509_NAME_get_index_by_OBJ, X509_NAME_entry_count, X509_NAME_get_entry, X509_NAME_get_text_by_NID, X509_NAME_get_text_by_OBJ β€” these are the X509_NAME lookup and enumeration functions. yes, all six of them, one big friend group 🫢


🌸 SYNOPSIS 🌸

library "libcrypto" and you gotta include this:

#include <openssl/x509.h>

then the whole squad:

int
X509_NAME_get_index_by_NID(const X509_NAME *name, int nid, int lastpos);

int
X509_NAME_get_index_by_OBJ(const X509_NAME *name,
    const ASN1_OBJECT *obj, int lastpos);

int
X509_NAME_entry_count(const X509_NAME *name);

X509_NAME_ENTRY *
X509_NAME_get_entry(const X509_NAME *name, int loc);

int
X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len);

int
X509_NAME_get_text_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
    char *buf, int len);

πŸ¦‹ DESCRIPTION πŸ¦‹

these functions let you examine an X509_NAME structure. and what even is that? it's the same as the ASN.1 Name type defined in RFC 2459 (and like, elsewhere too) β€” the one used for certificate subject and issuer names. so yeah, main character of every cert ever πŸ’…

X509_NAME_get_index_by_NID() and X509_NAME_get_index_by_OBJ() retrieve the next index matching nid or obj after lastpos. pro tip: lastpos should initially be set to -1. that's the starting line, gorl 🏁

X509_NAME_get_entry() retrieves the X509_NAME_ENTRY from name corresponding to index loc. acceptable values for loc run from 0 to X509_NAME_entry_count(name) - 1. so like, zero-indexed, obviously.

X509_NAME_get_text_by_NID() and X509_NAME_get_text_by_OBJ() retrieve the bytes encoded as UTF-8 from the first entry in name which matches nid or obj.

  • if buf is NULL, nothing is written, but the return value is calculated as usual (iykyk, it's a size-check vibe)
  • if buf is not NULL, no more than len bytes will be written, and the text written to buf will be NUL terminated ✨

and here's the part where you gotta pay attention 😭 β€” if len is not large enough to hold the NUL byte terminated UTF-8 encoding of the text, or if the UTF-8 encoding of the text would contain a NUL byte, no data will be written and the call will return failure. no cap, it just bails.

all the relevant NID_* and OBJ_* codes can be found in the <openssl/objects.h> header file. go look, they're all right there πŸ“

🚨 apps that could pass invalid NIDs to X509_NAME_get_index_by_NID() should check for the return value of -2. alternatively, the NID validity can be determined first by checking that OBJ_nid2obj(nid) is not NULL. literally just vibe check your nid first.

πŸ”₯ RETURN VALUES πŸ”₯

  • X509_NAME_get_index_by_NID() β€” returns the index of the next matching entry, -1 if not found, or -2 if the nid does not correspond to a valid OID. that -2 is the "your nid is fake" signal πŸ’€
  • X509_NAME_get_index_by_OBJ() β€” returns the index of the next matching entry or -1 if not found.
  • X509_NAME_entry_count() β€” returns the total number of entries in name.
  • X509_NAME_get_entry() β€” returns an internal pointer which must not be freed by the caller, or NULL if the index is invalid. do not free it, bestie, it's not yours πŸ™ˆ
  • X509_NAME_get_text_by_NID() and X509_NAME_get_text_by_OBJ() β€” return the length of the output UTF-8 string written, not counting the terminating NUL, or -1 in the case of an error or no match being found.

and in some cases of failure of X509_NAME_get_index_by_NID() and X509_NAME_get_text_by_NID(), the reason can be determined with ERR_get_error(3). detective mode πŸ”


🌈 EXAMPLES 🌈

process all entries β€” the classic full-lore run-through:

int i;
X509_NAME_ENTRY *e;

for (i = 0; i < X509_NAME_entry_count(nm); i++) {
        e = X509_NAME_get_entry(nm, i);
        /* Do something with e */
}

process all commonName entries β€” the "only the ones I care about" arc:

int lastpos = -1;
X509_NAME_ENTRY *e;

for (;;) {
        lastpos = X509_NAME_get_index_by_NID(nm, NID_commonName, lastpos);
        if (lastpos == -1)
                break;
        e = X509_NAME_get_entry(nm, lastpos);
        /* Do something with e */
}

see how lastpos starts at -1? told you πŸ’œ


🧚 SEE ALSO 🧚

d2i_X509_NAME(3), X509_NAME_ENTRY_get_object(3), X509_NAME_new(3)


⭐ HISTORY ⭐

these functions first appeared in SSLeay 0.8.0 and have been available since OpenBSD 2.4. vintage. lowkey legendary πŸ‘‘


πŸ’€ CAVEATS πŸ’€

okay so real talk:

X509_NAME_get_text_by_NID() and X509_NAME_get_text_by_OBJ() are legacy functions which have various limitations which make them of minimal use in practice. not me calling them flops but like... they kinda are 😬

  • they can only find the first matching entry
  • and they will copy the contents of the field verbatim

which can be highly confusing if the target is a multicharacter string type like a BMPString or a UTF8String. like, you get raw bytes, not always what you expected. rude.

for a more general solution, use X509_NAME_get_index_by_NID() or X509_NAME_get_index_by_OBJ(), followed by X509_NAME_get_entry() on any matching indices, and then the various X509_NAME_ENTRY utility functions on the result. that's the upgrade path, bestie β€” take it 🌸


kisses, this page is from Debian, dated June 8, 2025 β€” and it's the X509_NAME_GET_INDEX_BY_NID(3) girlies' official group chat. go read your certs properly, periodt πŸ’…βœ¨

πŸ“¦ where this page comes from