1ebfedea0SLionel Sambuc HOWTO proxy certificates 2ebfedea0SLionel Sambuc 3ebfedea0SLionel Sambuc0. WARNING 4ebfedea0SLionel Sambuc 5*0a6a1f1dSLionel SambucNONE OF THE CODE PRESENTED HERE HAS BEEN CHECKED! The code is just examples to 6*0a6a1f1dSLionel Sambucshow you how things could be done. There might be typos or type conflicts, and 7*0a6a1f1dSLionel Sambucyou will have to resolve them. 8ebfedea0SLionel Sambuc 9ebfedea0SLionel Sambuc1. Introduction 10ebfedea0SLionel Sambuc 11*0a6a1f1dSLionel SambucProxy certificates are defined in RFC 3820. They are really usual certificates 12*0a6a1f1dSLionel Sambucwith the mandatory extension proxyCertInfo. 13ebfedea0SLionel Sambuc 14*0a6a1f1dSLionel SambucProxy certificates are issued by an End Entity (typically a user), either 15*0a6a1f1dSLionel Sambucdirectly with the EE certificate as issuing certificate, or by extension through 16*0a6a1f1dSLionel Sambucan already issued proxy certificate. Proxy certificates are used to extend 17*0a6a1f1dSLionel Sambucrights to some other entity (a computer process, typically, or sometimes to the 18*0a6a1f1dSLionel Sambucuser itself). This allows the entity to perform operations on behalf of the 19*0a6a1f1dSLionel Sambucowner of the EE certificate. 20ebfedea0SLionel Sambuc 21ebfedea0SLionel SambucSee http://www.ietf.org/rfc/rfc3820.txt for more information. 22ebfedea0SLionel Sambuc 23ebfedea0SLionel Sambuc 24ebfedea0SLionel Sambuc2. A warning about proxy certificates 25ebfedea0SLionel Sambuc 26*0a6a1f1dSLionel SambucNo one seems to have tested proxy certificates with security in mind. To this 27*0a6a1f1dSLionel Sambucdate, it seems that proxy certificates have only been used in a context highly 28*0a6a1f1dSLionel Sambucaware of them. 29ebfedea0SLionel Sambuc 30*0a6a1f1dSLionel SambucExisting applications might misbehave when trying to validate a chain of 31*0a6a1f1dSLionel Sambuccertificates which use a proxy certificate. They might incorrectly consider the 32*0a6a1f1dSLionel Sambucleaf to be the certificate to check for authorisation data, which is controlled 33*0a6a1f1dSLionel Sambucby the EE certificate owner. 34ebfedea0SLionel Sambuc 35*0a6a1f1dSLionel SambucsubjectAltName and issuerAltName are forbidden in proxy certificates, and this 36*0a6a1f1dSLionel Sambucis enforced in OpenSSL. The subject must be the same as the issuer, with one 37*0a6a1f1dSLionel SambuccommonName added on. 38*0a6a1f1dSLionel Sambuc 39*0a6a1f1dSLionel SambucPossible threats we can think of at this time include: 40ebfedea0SLionel Sambuc 41ebfedea0SLionel Sambuc - impersonation through commonName (think server certificates). 42*0a6a1f1dSLionel Sambuc - use of additional extensions, possibly non-standard ones used in certain 43*0a6a1f1dSLionel Sambuc environments, that would grant extra or different authorisation rights. 44ebfedea0SLionel Sambuc 45*0a6a1f1dSLionel SambucFor these reasons, OpenSSL requires that the use of proxy certificates be 46*0a6a1f1dSLionel Sambucexplicitly allowed. Currently, this can be done using the following methods: 47ebfedea0SLionel Sambuc 48*0a6a1f1dSLionel Sambuc - if the application directly calls X509_verify_cert(), it can first call: 49ebfedea0SLionel Sambuc 50ebfedea0SLionel Sambuc X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); 51ebfedea0SLionel Sambuc 52*0a6a1f1dSLionel Sambuc Where ctx is the pointer which then gets passed to X509_verify_cert(). 53ebfedea0SLionel Sambuc 54*0a6a1f1dSLionel Sambuc - proxy certificate validation can be enabled before starting the application 55*0a6a1f1dSLionel Sambuc by setting the environment variable OPENSSL_ALLOW_PROXY_CERTS. 56*0a6a1f1dSLionel Sambuc 57*0a6a1f1dSLionel SambucIn the future, it might be possible to enable proxy certificates by editing 58*0a6a1f1dSLionel Sambucopenssl.cnf. 59ebfedea0SLionel Sambuc 60ebfedea0SLionel Sambuc 61*0a6a1f1dSLionel Sambuc3. How to create proxy certificates 62ebfedea0SLionel Sambuc 63*0a6a1f1dSLionel SambucCreating proxy certificates is quite easy, by taking advantage of a lack of 64*0a6a1f1dSLionel Sambucchecks in the 'openssl x509' application (*ahem*). You must first create a 65*0a6a1f1dSLionel Sambucconfiguration section that contains a definition of the proxyCertInfo extension, 66*0a6a1f1dSLionel Sambucfor example: 67ebfedea0SLionel Sambuc 68ebfedea0SLionel Sambuc [ v3_proxy ] 69ebfedea0SLionel Sambuc # A proxy certificate MUST NEVER be a CA certificate. 70ebfedea0SLionel Sambuc basicConstraints=CA:FALSE 71ebfedea0SLionel Sambuc 72ebfedea0SLionel Sambuc # Usual authority key ID 73ebfedea0SLionel Sambuc authorityKeyIdentifier=keyid,issuer:always 74ebfedea0SLionel Sambuc 75*0a6a1f1dSLionel Sambuc # The extension which marks this certificate as a proxy 76ebfedea0SLionel Sambuc proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:1,policy:text:AB 77ebfedea0SLionel Sambuc 78*0a6a1f1dSLionel SambucIt's also possible to specify the proxy extension in a separate section: 79ebfedea0SLionel Sambuc 80ebfedea0SLionel Sambuc proxyCertInfo=critical,@proxy_ext 81ebfedea0SLionel Sambuc 82ebfedea0SLionel Sambuc [ proxy_ext ] 83ebfedea0SLionel Sambuc language=id-ppl-anyLanguage 84ebfedea0SLionel Sambuc pathlen=0 85ebfedea0SLionel Sambuc policy=text:BC 86ebfedea0SLionel Sambuc 87*0a6a1f1dSLionel SambucThe policy value has a specific syntax, {syntag}:{string}, where the syntag 88*0a6a1f1dSLionel Sambucdetermines what will be done with the string. The following syntags are 89*0a6a1f1dSLionel Sambucrecognised: 90ebfedea0SLionel Sambuc 91*0a6a1f1dSLionel Sambuc text indicates that the string is simply bytes, without any encoding: 92ebfedea0SLionel Sambuc 93ebfedea0SLionel Sambuc policy=text:räksmörgås 94ebfedea0SLionel Sambuc 95*0a6a1f1dSLionel Sambuc Previous versions of this design had a specific tag for UTF-8 text. 96*0a6a1f1dSLionel Sambuc However, since the bytes are copied as-is anyway, there is no need for 97*0a6a1f1dSLionel Sambuc such a specific tag. 98*0a6a1f1dSLionel Sambuc 99*0a6a1f1dSLionel Sambuc hex indicates the string is encoded in hex, with colons between each byte 100*0a6a1f1dSLionel Sambuc (every second hex digit): 101ebfedea0SLionel Sambuc 102ebfedea0SLionel Sambuc policy=hex:72:E4:6B:73:6D:F6:72:67:E5:73 103ebfedea0SLionel Sambuc 104*0a6a1f1dSLionel Sambuc Previous versions of this design had a tag to insert a complete DER 105*0a6a1f1dSLionel Sambuc blob. However, the only legal use for this would be to surround the 106*0a6a1f1dSLionel Sambuc bytes that would go with the hex: tag with whatever is needed to 107*0a6a1f1dSLionel Sambuc construct a correct OCTET STRING. The DER tag therefore felt 108*0a6a1f1dSLionel Sambuc superfluous, and was removed. 109ebfedea0SLionel Sambuc 110*0a6a1f1dSLionel Sambuc file indicates that the text of the policy should really be taken from a 111*0a6a1f1dSLionel Sambuc file. The string is then really a file name. This is useful for 112*0a6a1f1dSLionel Sambuc policies that are large (more than a few lines, e.g. XML documents). 113ebfedea0SLionel Sambuc 114ebfedea0SLionel SambucThe 'policy' setting can be split up in multiple lines like this: 115ebfedea0SLionel Sambuc 116ebfedea0SLionel Sambuc 0.policy=This is 117*0a6a1f1dSLionel Sambuc 1.policy= a multi- 118ebfedea0SLionel Sambuc 2.policy=line policy. 119ebfedea0SLionel Sambuc 120*0a6a1f1dSLionel SambucNOTE: the proxy policy value is the part which determines the rights granted to 121*0a6a1f1dSLionel Sambucthe process using the proxy certificate. The value is completely dependent on 122*0a6a1f1dSLionel Sambucthe application reading and interpreting it! 123ebfedea0SLionel Sambuc 124*0a6a1f1dSLionel SambucNow that you have created an extension section for your proxy certificate, you 125*0a6a1f1dSLionel Sambuccan easily create a proxy certificate by doing: 126ebfedea0SLionel Sambuc 127*0a6a1f1dSLionel Sambuc openssl req -new -config openssl.cnf -out proxy.req -keyout proxy.key 128*0a6a1f1dSLionel Sambuc openssl x509 -req -CAcreateserial -in proxy.req -days 7 -out proxy.crt \ 129*0a6a1f1dSLionel Sambuc -CA user.crt -CAkey user.key -extfile openssl.cnf -extensions v3_proxy 130ebfedea0SLionel Sambuc 131*0a6a1f1dSLionel SambucYou can also create a proxy certificate using another proxy certificate as 132*0a6a1f1dSLionel Sambucissuer (note: I'm using a different configuration section for it): 133ebfedea0SLionel Sambuc 134*0a6a1f1dSLionel Sambuc openssl req -new -config openssl.cnf -out proxy2.req -keyout proxy2.key 135*0a6a1f1dSLionel Sambuc openssl x509 -req -CAcreateserial -in proxy2.req -days 7 -out proxy2.crt \ 136*0a6a1f1dSLionel Sambuc -CA proxy.crt -CAkey proxy.key -extfile openssl.cnf -extensions v3_proxy2 137ebfedea0SLionel Sambuc 138ebfedea0SLionel Sambuc 139ebfedea0SLionel Sambuc4. How to have your application interpret the policy? 140ebfedea0SLionel Sambuc 141*0a6a1f1dSLionel SambucThe basic way to interpret proxy policies is to start with some default rights, 142*0a6a1f1dSLionel Sambucthen compute the resulting rights by checking the proxy certificate against 143*0a6a1f1dSLionel Sambucthe chain of proxy certificates, user certificate and CA certificates. You then 144*0a6a1f1dSLionel Sambucuse the final computed rights. Sounds easy, huh? It almost is. 145ebfedea0SLionel Sambuc 146*0a6a1f1dSLionel SambucThe slightly complicated part is figuring out how to pass data between your 147ebfedea0SLionel Sambucapplication and the certificate validation procedure. 148ebfedea0SLionel Sambuc 149ebfedea0SLionel SambucYou need the following ingredients: 150ebfedea0SLionel Sambuc 151*0a6a1f1dSLionel Sambuc - a callback function that will be called for every certificate being 152*0a6a1f1dSLionel Sambuc validated. The callback be called several times for each certificate, 153*0a6a1f1dSLionel Sambuc so you must be careful to do the proxy policy interpretation at the right 154*0a6a1f1dSLionel Sambuc time. You also need to fill in the defaults when the EE certificate is 155*0a6a1f1dSLionel Sambuc checked. 156ebfedea0SLionel Sambuc 157*0a6a1f1dSLionel Sambuc - a data structure that is shared between your application code and the 158*0a6a1f1dSLionel Sambuc callback. 159ebfedea0SLionel Sambuc 160ebfedea0SLionel Sambuc - a wrapper function that sets it all up. 161ebfedea0SLionel Sambuc 162*0a6a1f1dSLionel Sambuc - an ex_data index function that creates an index into the generic ex_data 163*0a6a1f1dSLionel Sambuc store that is attached to an X509 validation context. 164ebfedea0SLionel Sambuc 165*0a6a1f1dSLionel SambucHere is some skeleton code you can fill in: 166ebfedea0SLionel Sambuc 167ebfedea0SLionel Sambuc /* In this example, I will use a view of granted rights as a bit 168ebfedea0SLionel Sambuc array, one bit for each possible right. */ 169ebfedea0SLionel Sambuc typedef struct your_rights { 170ebfedea0SLionel Sambuc unsigned char rights[total_rights / 8]; 171ebfedea0SLionel Sambuc } YOUR_RIGHTS; 172ebfedea0SLionel Sambuc 173ebfedea0SLionel Sambuc /* The following procedure will create an index for the ex_data 174ebfedea0SLionel Sambuc store in the X509 validation context the first time it's called. 175ebfedea0SLionel Sambuc Subsequent calls will return the same index. */ 176ebfedea0SLionel Sambuc static int get_proxy_auth_ex_data_idx(void) 177ebfedea0SLionel Sambuc { 178ebfedea0SLionel Sambuc static volatile int idx = -1; 179ebfedea0SLionel Sambuc if (idx < 0) 180ebfedea0SLionel Sambuc { 181ebfedea0SLionel Sambuc CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE); 182ebfedea0SLionel Sambuc if (idx < 0) 183ebfedea0SLionel Sambuc { 184ebfedea0SLionel Sambuc idx = X509_STORE_CTX_get_ex_new_index(0, 185ebfedea0SLionel Sambuc "for verify callback", 186ebfedea0SLionel Sambuc NULL,NULL,NULL); 187ebfedea0SLionel Sambuc } 188ebfedea0SLionel Sambuc CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE); 189ebfedea0SLionel Sambuc } 190ebfedea0SLionel Sambuc return idx; 191ebfedea0SLionel Sambuc } 192ebfedea0SLionel Sambuc 193ebfedea0SLionel Sambuc /* Callback to be given to the X509 validation procedure. */ 194ebfedea0SLionel Sambuc static int verify_callback(int ok, X509_STORE_CTX *ctx) 195ebfedea0SLionel Sambuc { 196ebfedea0SLionel Sambuc if (ok == 1) /* It's REALLY important you keep the proxy policy 197*0a6a1f1dSLionel Sambuc check within this section. It's important to know 198ebfedea0SLionel Sambuc that when ok is 1, the certificates are checked 199ebfedea0SLionel Sambuc from top to bottom. You get the CA root first, 200ebfedea0SLionel Sambuc followed by the possible chain of intermediate 201ebfedea0SLionel Sambuc CAs, followed by the EE certificate, followed by 202ebfedea0SLionel Sambuc the possible proxy certificates. */ 203ebfedea0SLionel Sambuc { 204ebfedea0SLionel Sambuc X509 *xs = ctx->current_cert; 205ebfedea0SLionel Sambuc 206ebfedea0SLionel Sambuc if (xs->ex_flags & EXFLAG_PROXY) 207ebfedea0SLionel Sambuc { 208ebfedea0SLionel Sambuc YOUR_RIGHTS *rights = 209ebfedea0SLionel Sambuc (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, 210ebfedea0SLionel Sambuc get_proxy_auth_ex_data_idx()); 211ebfedea0SLionel Sambuc PROXY_CERT_INFO_EXTENSION *pci = 212ebfedea0SLionel Sambuc X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL); 213ebfedea0SLionel Sambuc 214ebfedea0SLionel Sambuc switch (OBJ_obj2nid(pci->proxyPolicy->policyLanguage)) 215ebfedea0SLionel Sambuc { 216ebfedea0SLionel Sambuc case NID_Independent: 217ebfedea0SLionel Sambuc /* Do whatever you need to grant explicit rights to 218ebfedea0SLionel Sambuc this particular proxy certificate, usually by 219ebfedea0SLionel Sambuc pulling them from some database. If there are none 220ebfedea0SLionel Sambuc to be found, clear all rights (making this and any 221ebfedea0SLionel Sambuc subsequent proxy certificate void of any rights). 222ebfedea0SLionel Sambuc */ 223ebfedea0SLionel Sambuc memset(rights->rights, 0, sizeof(rights->rights)); 224ebfedea0SLionel Sambuc break; 225ebfedea0SLionel Sambuc case NID_id_ppl_inheritAll: 226ebfedea0SLionel Sambuc /* This is basically a NOP, we simply let the current 227ebfedea0SLionel Sambuc rights stand as they are. */ 228ebfedea0SLionel Sambuc break; 229ebfedea0SLionel Sambuc default: 230ebfedea0SLionel Sambuc /* This is usually the most complex section of code. 231ebfedea0SLionel Sambuc You really do whatever you want as long as you 232ebfedea0SLionel Sambuc follow RFC 3820. In the example we use here, the 233ebfedea0SLionel Sambuc simplest thing to do is to build another, temporary 234ebfedea0SLionel Sambuc bit array and fill it with the rights granted by 235ebfedea0SLionel Sambuc the current proxy certificate, then use it as a 236ebfedea0SLionel Sambuc mask on the accumulated rights bit array, and 237*0a6a1f1dSLionel Sambuc voilà, you now have a new accumulated rights bit 238ebfedea0SLionel Sambuc array. */ 239ebfedea0SLionel Sambuc { 240ebfedea0SLionel Sambuc int i; 241ebfedea0SLionel Sambuc YOUR_RIGHTS tmp_rights; 242ebfedea0SLionel Sambuc memset(tmp_rights.rights, 0, sizeof(tmp_rights.rights)); 243ebfedea0SLionel Sambuc 244ebfedea0SLionel Sambuc /* process_rights() is supposed to be a procedure 245ebfedea0SLionel Sambuc that takes a string and it's length, interprets 246ebfedea0SLionel Sambuc it and sets the bits in the YOUR_RIGHTS pointed 247ebfedea0SLionel Sambuc at by the third argument. */ 248ebfedea0SLionel Sambuc process_rights((char *) pci->proxyPolicy->policy->data, 249ebfedea0SLionel Sambuc pci->proxyPolicy->policy->length, 250ebfedea0SLionel Sambuc &tmp_rights); 251ebfedea0SLionel Sambuc 252ebfedea0SLionel Sambuc for(i = 0; i < total_rights / 8; i++) 253ebfedea0SLionel Sambuc rights->rights[i] &= tmp_rights.rights[i]; 254ebfedea0SLionel Sambuc } 255ebfedea0SLionel Sambuc break; 256ebfedea0SLionel Sambuc } 257ebfedea0SLionel Sambuc PROXY_CERT_INFO_EXTENSION_free(pci); 258ebfedea0SLionel Sambuc } 259ebfedea0SLionel Sambuc else if (!(xs->ex_flags & EXFLAG_CA)) 260ebfedea0SLionel Sambuc { 261ebfedea0SLionel Sambuc /* We have a EE certificate, let's use it to set default! 262ebfedea0SLionel Sambuc */ 263ebfedea0SLionel Sambuc YOUR_RIGHTS *rights = 264ebfedea0SLionel Sambuc (YOUR_RIGHTS *)X509_STORE_CTX_get_ex_data(ctx, 265ebfedea0SLionel Sambuc get_proxy_auth_ex_data_idx()); 266ebfedea0SLionel Sambuc 267ebfedea0SLionel Sambuc /* The following procedure finds out what rights the owner 268ebfedea0SLionel Sambuc of the current certificate has, and sets them in the 269ebfedea0SLionel Sambuc YOUR_RIGHTS structure pointed at by the second 270ebfedea0SLionel Sambuc argument. */ 271ebfedea0SLionel Sambuc set_default_rights(xs, rights); 272ebfedea0SLionel Sambuc } 273ebfedea0SLionel Sambuc } 274ebfedea0SLionel Sambuc return ok; 275ebfedea0SLionel Sambuc } 276ebfedea0SLionel Sambuc 277ebfedea0SLionel Sambuc static int my_X509_verify_cert(X509_STORE_CTX *ctx, 278ebfedea0SLionel Sambuc YOUR_RIGHTS *needed_rights) 279ebfedea0SLionel Sambuc { 280ebfedea0SLionel Sambuc int i; 281ebfedea0SLionel Sambuc int (*save_verify_cb)(int ok,X509_STORE_CTX *ctx) = ctx->verify_cb; 282ebfedea0SLionel Sambuc YOUR_RIGHTS rights; 283ebfedea0SLionel Sambuc 284ebfedea0SLionel Sambuc X509_STORE_CTX_set_verify_cb(ctx, verify_callback); 285ebfedea0SLionel Sambuc X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(), &rights); 286ebfedea0SLionel Sambuc X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS); 287ebfedea0SLionel Sambuc ok = X509_verify_cert(ctx); 288ebfedea0SLionel Sambuc 289ebfedea0SLionel Sambuc if (ok == 1) 290ebfedea0SLionel Sambuc { 291ebfedea0SLionel Sambuc ok = check_needed_rights(rights, needed_rights); 292ebfedea0SLionel Sambuc } 293ebfedea0SLionel Sambuc 294ebfedea0SLionel Sambuc X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb); 295ebfedea0SLionel Sambuc 296ebfedea0SLionel Sambuc return ok; 297ebfedea0SLionel Sambuc } 298ebfedea0SLionel Sambuc 299ebfedea0SLionel SambucIf you use SSL or TLS, you can easily set up a callback to have the 300ebfedea0SLionel Sambuccertificates checked properly, using the code above: 301ebfedea0SLionel Sambuc 302ebfedea0SLionel Sambuc SSL_CTX_set_cert_verify_callback(s_ctx, my_X509_verify_cert, &needed_rights); 303ebfedea0SLionel Sambuc 304ebfedea0SLionel Sambuc 305ebfedea0SLionel Sambuc-- 306ebfedea0SLionel SambucRichard Levitte 307