2

I'm using OpenSSL in Windows. I want OpenSSL's certificate checking to validate certificates against their CRLs. In the OpenSSL documentation, it says:

If CRLs checking is enable CRLs are expected to be available in the corresponding X509_STORE structure. No attempt is made to download CRLs from the CRL distribution points extension.

I populate OpenSSL's certificates store with certificates the Windows Certificate Store. I'd like to do the same with the CRLs that exist in the Windows Certificate Store.

Is there a way to do that? Is it possible that OpenSSL has added CRL downloading to its checks but it is not yet documented?

nitzanms
  • 1,786
  • 12
  • 35

2 Answers2

2

Turns out it's a lot easier than I thought - you use CertEnumCRLsInStore to get the CRLs for the relevant store, and then use d2i_X509_CRL to encode the certificates for OpenSSL. The whole thing is remarkably similar to this: https://stackoverflow.com/a/40046425/1132699

Community
  • 1
  • 1
nitzanms
  • 1,786
  • 12
  • 35
  • It enumerates only **explicitly installed** CRLs. Is there a way to get CRLs from Windows Certificate Store's cache? – Ilyan May 07 '20 at 13:06
1

I wrote the following code to enable getting CRLs from cache on demand:

#define length_of(x) (sizeof(x) / sizeof(x[0]))
#define length_of_null_terminated_string(x) (length_of(x) - 1)

static X509_CRL * GetCRLByUrl(
    const char * url)
{
    PCCRL_CONTEXT pCrlContext = nullptr;
    BOOL res = ::CryptRetrieveObjectByUrlA(url, CONTEXT_OID_CRL, 0, 0, (LPVOID*)&pCrlContext, nullptr, nullptr, nullptr, nullptr);
    if (!res) {
        DWORD dwErr = ::GetLastError();
        return nullptr;
    }

    const unsigned char * pbCrlEncoded = pCrlContext->pbCrlEncoded;
    X509_CRL * x509_crl = d2i_X509_CRL(NULL, &pbCrlEncoded, pCrlContext->cbCrlEncoded);
    ::CertFreeCRLContext(pCrlContext);
    if (!x509_crl) {
        return nullptr;
    }

    return x509_crl;

    LOG_SCOPE_LEAVE;
}

static STACK_OF(X509_CRL) * LookupCRLsInWindowsStore(
    X509_STORE_CTX * ctx,
    X509_NAME * nm)
{
    STACK_OF(X509_CRL) * crls = sk_X509_CRL_new_null();
    if (!crls) {
        return nullptr;
    }

    X509 * x509 = X509_STORE_CTX_get_current_cert(ctx);

    static int nids[] = { NID_crl_distribution_points, NID_freshest_crl };
    for (int * it = std::begin(nids), * itEnd = std::end(nids); it != itEnd; ++it) {
        int nid = *it;
        STACK_OF(DIST_POINT) * crldp = (STACK_OF(DIST_POINT) *)X509_get_ext_d2i(x509, nid, NULL, NULL);
        for (int i = 0, iEnd = sk_DIST_POINT_num(crldp); i < iEnd; ++i) {
            DIST_POINT * dp = sk_DIST_POINT_value(crldp, i);
            if (!dp->distpoint) {
                continue;
            }
            if (dp->distpoint->type != 0) {
                continue;
            }

            GENERAL_NAMES * gens = dp->distpoint->name.fullname;
            for (int j = 0, jEnd = sk_GENERAL_NAME_num(gens); j < jEnd; ++j) {
                GENERAL_NAME * gen = sk_GENERAL_NAME_value(gens, i);
                int gtype;
                ASN1_STRING * uri = (ASN1_STRING *)GENERAL_NAME_get0_value(gen, &gtype);
                if (gtype != GEN_URI) {
                    continue;
                }
                const char * url = (const char *)ASN1_STRING_data(uri);
                if (ASN1_STRING_length(uri) < length_of_null_terminated_string("http://") || strncmp(url, "http://", length_of_null_terminated_string("http://")) != 0) {
                    continue;
                }
                X509_CRL * x509_crl = GetCRLByUrl(url);
                if (x509_crl) {
                    sk_X509_CRL_push(crls, x509_crl);
                    break;
                }
            }
        }
        sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
    }

    return crls;
}

X509_STORE * x509_store = ...;
X509_STORE_set_lookup_crls_cb(x509_store, LookupCRLsInWindowsStore);
Ilyan
  • 175
  • 6
  • I presume you derived your code from openssl's s_client app... In contrast to your `LookupCRLsInWindowsStore()` routine their callback `crls_http_cb()` always fetches the delta CRL (`NID_freshest_crl` distribution points) if the`NID_crl_distribution_points` distribution points are present. – klaus triendl Feb 16 '21 at 12:06