PROXY-CERTIFICATES(7ossl)

πŸ’– NAME πŸ’–

proxy-certificates β€” proxy certificates in OpenSSL, bestie. That's literally the whole page. This is the PROXY-CERTIFICATES(7ossl) manual, and we're gonna get you so comfy with these πŸ’…


✨ DESCRIPTION ✨

Okay so picture this: proxy certificates are defined in RFC 3820, and their whole job is extending rights to some other entity β€” usually a computer process, sometimes even the user themself 🫢. That way the entity can do operations on behalf of the owner of the EE (End Entity) certificate. Main character energy, but delegated.

For a proxy certificate to be valid, it's giving very specific requirements:

  • They are issued by an End Entity β€” either a normal EE certificate, or another proxy certificate.
  • They must not have the subjectAltName or issuerAltName extensions.
  • They must have the proxyCertInfo extension.
  • They must have the subject of their issuer, with one commonName added.

πŸ¦‹ Enabling proxy certificate verification πŸ¦‹

OpenSSL is not out here assuming anything, ngl. It expects applications that want to use proxy certificates to be specially aware of them and to say so out loud. You do that by setting an X509 verification flag:

X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);

or

X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_ALLOW_PROXY_CERTS);
Peep the "NOTES" section for a whole discussion on why this requirement exists β€” it's a whole thing πŸ’€

πŸŽ€ Creating proxy certificates πŸŽ€

Making proxy certificates is honestly a slay with the openssl-x509(1) command, you just need some extra extensions:

[ proxy ]
# A proxy certificate MUST NEVER be a CA certificate.
basicConstraints = CA:FALSE
# Usual authority key ID
authorityKeyIdentifier = keyid,issuer:always
# The extension which marks this certificate as a proxy
proxyCertInfo = critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB

You can also drop the proxy extension into its own separate section, if that's more your vibe:

proxyCertInfo = critical,@proxy_ext

[ proxy_ext ]
language = id-ppl-anyLanguage
pathlen = 0
policy = text:BC

The policy value has one very specific syntax: syntag:string, where the syntag decides what happens with the string. These are the syntags it recognises:

  • text β€” means the string is a byte sequence, no encoding at all, zero fuss:
policy=text:rΓ€ksmΓΆrgΓ₯s
  • hex β€” means the string is hexadecimal-encoded binary data, with colons between each byte (every second hex digit):
policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73
  • file β€” means the text of the policy should be pulled from a file, so the string is a filename. Obsessed with this one for policies that are more than a few lines, like XML or other markup πŸ‘‘
Heads up: the proxy policy value is literally what determines the rights granted to the process during the proxy certificate, and it's up to the application to interpret and combine these policies. That part is on you, bestie 😭

With a proxy extension set up, creating a proxy certificate is just two commands. Two!! πŸŽ€

openssl req -new -config proxy.cnf \
    -out proxy.req -keyout proxy.key \
    -subj "/DC=org/DC=openssl/DC=users/CN=proxy"

openssl x509 -req -CAcreateserial -in proxy.req -out proxy.crt \
    -CA user.crt -CAkey user.key -days 7 \
    -extfile proxy.cnf -extensions proxy

You can also create a proxy certificate using another proxy certificate as issuer β€” yes, it stacks, we love a chain πŸ”₯. Note that this example uses a different configuration section for the proxy extensions:

openssl req -new -config proxy.cnf \
    -out proxy2.req -keyout proxy2.key \
    -subj "/DC=org/DC=openssl/DC=users/CN=proxy/CN=proxy 2"

openssl x509 -req -CAcreateserial -in proxy2.req -out proxy2.crt \
    -CA proxy.crt -CAkey proxy.key -days 7 \
    -extfile proxy.cnf -extensions proxy_2

🌈 Using proxy certs in applications 🌈

To interpret proxy policies, your application would normally start with some default rights (maybe none at all β€” rough, I know), then compute the resulting rights by checking the rights against the chain of proxy certificates, user certificate and CA certificates.

The complicated part β€” the actual boss fight β€” is figuring out how to pass data between your application and the certificate validation procedure 😩

These are the ingredients you need for that processing:

  • a callback function that gets called for every certificate being validated. It's called several times for each certificate, so be careful and do the proxy policy interpretation at the right time. You also need to fill in the defaults when the EE certificate is checked.
  • a data structure that's shared between your application code and the callback.
  • a wrapper function that sets it all up.
  • an ex_data index function that creates an index into the generic ex_data store attached to an X509 validation context.

Here's skeleton code you can use as a starting point β€” take it, it's yours 🫢

#include <string.h>
#include <netdb.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>

#define total_rights 25

/*
 * In this example, I will use a view of granted rights as a bit
 * array, one bit for each possible right.
 */
typedef struct your_rights {
    unsigned char rights[(total_rights + 7) / 8];
} YOUR_RIGHTS;

/*
 * The following procedure will create an index for the ex_data
 * store in the X509 validation context the first time it's
 * called.  Subsequent calls will return the same index.
 */
static int get_proxy_auth_ex_data_idx(X509_STORE_CTX *ctx)
{
    static volatile int idx = -1;

    if (idx < 0) {
        X509_STORE_lock(X509_STORE_CTX_get0_store(ctx));
        if (idx < 0) {
            idx = X509_STORE_CTX_get_ex_new_index(0,
                                                  "for verify callback",
                                                  NULL,NULL,NULL);
        }
        X509_STORE_unlock(X509_STORE_CTX_get0_store(ctx));
    }
    return idx;
}

/* Callback to be given to the X509 validation procedure.  */
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
    if (ok == 1) {
        /*
         * It's REALLY important you keep the proxy policy check
         * within this section.  It's important to know that when
         * ok is 1, the certificates are checked from top to
         * bottom.  You get the CA root first, followed by the
         * possible chain of intermediate CAs, followed by the EE
         * certificate, followed by the possible proxy
         * certificates.
         */
        X509 *xs = X509_STORE_CTX_get_current_cert(ctx);

        if (X509_get_extension_flags(xs) & EXFLAG_PROXY) {
            YOUR_RIGHTS *rights =
                (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
                    get_proxy_auth_ex_data_idx(ctx));
            PROXY_CERT_INFO_EXTENSION *pci =
                X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL);

            switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage)) {
            case NID_Independent:
                /*
                 * Do whatever you need to grant explicit rights
                 * to this particular proxy certificate, usually
                 * by pulling them from some database.  If there
                 * are none to be found, clear all rights (making
                 * this and any subsequent proxy certificate void
                 * of any rights).
                 */
                memset(rights->rights, 0, sizeof(rights->rights));
                break;
            case NID_id_ppl_inheritAll:
                /*
                 * This is basically a NOP, we simply let the
                 * current rights stand as they are.
                 */
                break;
            default:
                /*
                 * This is usually the most complex section of
                 * code.  You really do whatever you want as long
                 * as you follow RFC 3820.  In the example we use
                 * here, the simplest thing to do is to build
                 * another, temporary bit array and fill it with
                 * the rights granted by the current proxy
                 * certificate, then use it as a mask on the
                 * accumulated rights bit array, and voilΓ , you
                 * now have a new accumulated rights bit array.
                 */
                {
                    int i;
                    YOUR_RIGHTS tmp_rights;
                    memset(tmp_rights.rights, 0,
                           sizeof(tmp_rights.rights));

                    /*
                     * process_rights() is supposed to be a
                     * procedure that takes a string and its
                     * length, interprets it and sets the bits
                     * in the YOUR_RIGHTS pointed at by the
                     * third argument.
                     */
                    process_rights((char *) pci->proxyPolicy->policy->data,
                                   pci->proxyPolicy->policy->length,
                                   &tmp_rights);

                    for(i = 0; i < total_rights / 8; i++)
                        rights->rights[i] &= tmp_rights.rights[i];
                }
                break;
            }
            PROXY_CERT_INFO_EXTENSION_free(pci);
        } else if (!(X509_get_extension_flags(xs) & EXFLAG_CA)) {
            /* We have an EE certificate, let's use it to set default! */
            YOUR_RIGHTS *rights =
                (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx,
                    get_proxy_auth_ex_data_idx(ctx));

            /*
             * The following procedure finds out what rights the
             * owner of the current certificate has, and sets them
             * in the YOUR_RIGHTS structure pointed at by the
             * second argument.
             */
            set_default_rights(xs, rights);
        }
    }
    return ok;
}

static int my_X509_verify_cert(X509_STORE_CTX *ctx,
                               YOUR_RIGHTS *needed_rights)
{
    int ok;
    int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) =
        X509_STORE_CTX_get_verify_cb(ctx);
    YOUR_RIGHTS rights;
               X509_STORE_CTX_set_verify_cb(ctx, verify_callback);
               X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(ctx),
                                          &rights);
               X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
               ok = X509_verify_cert(ctx);

               if (ok == 1) {
                   ok = check_needed_rights(rights, needed_rights);
               }

               X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb);

               return ok;
           }

okay so that's the tail end of the function β€” you see how it flips the verify callback back to save_verify_cb at the end? bestie that's the put it back how you found it moment, we love a tidy queen πŸ’…

And if you're living in SSL/TLS land (which, honestly, iconic), you can just wire up a callback so the certs get checked properly, using the code above:

           SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert,
                                            &needed_rights);

🌸 NOTES 🌸

Real talk, bestie: to this date, it seems that proxy certificates have only been used in environments that are aware of them, and no one seems to have investigated how they can be used or misused outside of such an environment. no cap, that's the actual state of things πŸ™ˆ

For that reason, OpenSSL requires that applications aware of proxy certificates must also make that explicit. like, you gotta say you know her before she'll show up to the party, periodt ✨

subjectAltName and issuerAltName are forbidden in proxy certificates, and this is enforced in OpenSSL. The subject must be the same as the issuer, with one commonName added on. that's the whole vibe β€” same-same but with one extra name on top πŸŽ€

πŸ’€ Fr fr, don't try to sneak a subjectAltName in there. OpenSSL will catch it and it will not be cute.

πŸ¦‹ SEE ALSO πŸ¦‹

go read RFC 3820 if you wanna be a proxy cert main character, iykyk πŸ“šπŸ’–


Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


and that's the whole page, bestie!! πŸ’œ this bad girl is from 3.5.9, dated 2026-09-29, and it goes by PROXY-CERTIFICATES(7ossl) β€” go forth and certify responsibly, muah πŸ˜˜πŸ’…πŸ«Ά

πŸ“¦ where this page comes from