LordTitiKaka LordTitiKaka - 21 days ago 10
C Question

How can I verify certificate has "CA=true" basic constraint?

I'm new to openssl API

when I use the openssl APP with this command :

openssl x509 -in mycert.crt -text -noout


form it's output my interest is :

...
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE <-- this section I want to validate in my code
...


I want to validate the CA part using OpenSSL c API but can't figure how.
I've drilled down the APP src code but got lost when I've arrived to

openssl/crypto/ct/ct_prn:102

BIO_printf(out, "\n%*sExtensions: ", indent + 4, "");
if (sct->ext_len == 0)
BIO_printf(out, "none");
else
BIO_hex_string(out, indent + 16, 16, sct->ext, sct->ext_len);


my question:
what is the proper way to iterate over the extensions , find tthe basic constrains section and validate that my certificate is CA:TRUE

bonus question:
what is your method searching the openssl API for methods

jww jww
Answer

bonus question: what is your method searching the openssl API for methods

Sadly, there's no easy method at times. I'll walk you through the steps I use to answer unique questions like this.

If you know what to look for, sometimes you can use a symbol browser like ctags. I don't use it however; instead, I usually grep the sources.

If you have ever heard OpenSSL is "self documenting", this is the epitome of it.


my question: what is the proper way to iterate over the extensions , find tthe basic constrains section and validate that my certificate is CA:TRUE

After a while, you will get a feel for the interesting stuff. From the grep below, I know its the NID_basic_constraints.

openssl-1.1.0b$ grep -iR basic * | grep constraint
crypto/objects/obj_dat.h:    0x55,0x1D,0x13,                                /* [  505] OBJ_basic_constraints */
crypto/objects/obj_dat.h:    {"basicConstraints", "X509v3 Basic Constraints", NID_basic_constraints, 3, &so[505]},
crypto/objects/obj_dat.h:      87,    /* OBJ_basic_constraints            2 5 29 19 */
crypto/objects/obj_mac.num:basic_constraints        87
crypto/objects/objects.txt:!Cname basic-constraints
crypto/x509v3/v3_bcons.c:    NID_basic_constraints, 0,
crypto/x509v3/v3_purp.c:        NID_basic_constraints,  /* 87 */
crypto/x509v3/v3_purp.c:    /* Handle basic constraints */
crypto/x509v3/v3_purp.c:    if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL)))
...

From above, I know a few things. First, v3_purp.c is the file of interest. Second, X509_get_ext_d2i(x, ...) means x is a X509*. Third, NID_basic_constraints is the item of interest.

And here's the head of the function with the bits of interest. BASIC_CONSTRAINTS and both bs->ca look interesting.

static void x509v3_cache_extensions(X509 *x)
{
    BASIC_CONSTRAINTS *bs;
    PROXY_CERT_INFO_EXTENSION *pci;
    ASN1_BIT_STRING *usage;
    ASN1_BIT_STRING *ns;
    EXTENDED_KEY_USAGE *extusage;
    X509_EXTENSION *ex;

    int i;
    if (x->ex_flags & EXFLAG_SET)
        return;
    X509_digest(x, EVP_sha1(), x->sha1_hash, NULL);
    /* V1 should mean no extensions ... */
    if (!X509_get_version(x))
        x->ex_flags |= EXFLAG_V1;
    /* Handle basic constraints */
    if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, NULL, NULL))) {
        if (bs->ca)
            x->ex_flags |= EXFLAG_CA;
        if (bs->pathlen) {
            if ((bs->pathlen->type == V_ASN1_NEG_INTEGER)
                || !bs->ca) {
                x->ex_flags |= EXFLAG_INVALID;
                x->ex_pathlen = 0;
            } else
                x->ex_pathlen = ASN1_INTEGER_get(bs->pathlen);
        } else
            x->ex_pathlen = -1;
        BASIC_CONSTRAINTS_free(bs);
        x->ex_flags |= EXFLAG_BCONS;
    }
    ...
}

Next, checkout BASIC_CONSTRAINTS. You want the _st because that's how OpenSSL names their structs.

$ grep -IR BASIC_CONSTRAINTS * | grep _st
include/openssl/x509v3.h:typedef struct BASIC_CONSTRAINTS_st {

And a quick peek at BASIC_CONSTRAINTS_st:

typedef struct BASIC_CONSTRAINTS_st {
    int ca;
    ASN1_INTEGER *pathlen;
} BASIC_CONSTRAINTS;

The final question may be, how do you get a hold of an X509 *x? Well, you get that from:

  • SSL_get_peer_certificate (from an SSL connection)
  • PEM_read_X509 (convert from PEM encoding)
  • d2i_X509 (convert from ASN.1/DER encoding)
  • etc...

If you already have an X509* x, it looks like you only need to test for x->ex_flags & EXFLAG_V1.

Lather, rinse, repeat for EXFLAG_V1 and X509_st:

$ grep -IR EXFLAG_V1 * | grep define
crypto/x509v3/v3_purp.c:#define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
include/openssl/x509v3.h:# define EXFLAG_V1               0x40

$ grep -IR X509 * | grep _st | grep typedef
crypto/x509v3/pcy_int.h:typedef struct X509_POLICY_DATA_st X509_POLICY_DATA;
include/openssl/ossl_typ.h:typedef struct x509_st X509;
include/openssl/ossl_typ.h:typedef struct X509_algor_st X509_ALGOR;
include/openssl/ossl_typ.h:typedef struct X509_crl_st X509_CRL;
...