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