]>
Commit | Line | Data |
---|---|---|
552cc11b | 1 | /* |
d73f453c | 2 | * Copyright (C) 2008-2009 Martin Willi |
552cc11b MW |
3 | * Copyright (C) 2007 Andreas Steffen |
4 | * Hochschule fuer Technik Rapperswil | |
5 | * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
552cc11b MW |
16 | */ |
17 | ||
18 | #include "x509_ocsp_request.h" | |
19 | ||
20 | #include <library.h> | |
21 | #include <asn1/oid.h> | |
22 | #include <asn1/asn1.h> | |
23 | #include <utils/identification.h> | |
12642a68 | 24 | #include <collections/linked_list.h> |
f05b4272 | 25 | #include <utils/debug.h> |
552cc11b | 26 | #include <credentials/certificates/x509.h> |
e24aaddd | 27 | #include <credentials/keys/private_key.h> |
552cc11b MW |
28 | |
29 | #define NONCE_LEN 16 | |
30 | ||
31 | typedef struct private_x509_ocsp_request_t private_x509_ocsp_request_t; | |
32 | ||
33 | /** | |
34 | * private data of x509_ocsp_request | |
35 | */ | |
36 | struct private_x509_ocsp_request_t { | |
37 | ||
38 | /** | |
39 | * public functions | |
40 | */ | |
41 | x509_ocsp_request_t public; | |
7daf5226 | 42 | |
552cc11b MW |
43 | /** |
44 | * CA the candidates belong to | |
45 | */ | |
46 | x509_t *ca; | |
7daf5226 | 47 | |
552cc11b MW |
48 | /** |
49 | * Requestor name, subject of cert used if not set | |
50 | */ | |
51 | identification_t *requestor; | |
52 | ||
53 | /** | |
54 | * Requestor certificate, included in request | |
55 | */ | |
56 | certificate_t *cert; | |
7daf5226 | 57 | |
552cc11b MW |
58 | /** |
59 | * Requestor private key to sign request | |
60 | */ | |
61 | private_key_t *key; | |
7daf5226 | 62 | |
552cc11b MW |
63 | /** |
64 | * list of certificates to check, x509_t | |
65 | */ | |
66 | linked_list_t *candidates; | |
7daf5226 | 67 | |
552cc11b MW |
68 | /** |
69 | * nonce used in request | |
70 | */ | |
71 | chunk_t nonce; | |
7daf5226 | 72 | |
552cc11b MW |
73 | /** |
74 | * encoded OCSP request | |
75 | */ | |
76 | chunk_t encoding; | |
7daf5226 | 77 | |
552cc11b MW |
78 | /** |
79 | * reference count | |
80 | */ | |
81 | refcount_t ref; | |
82 | }; | |
83 | ||
3b878dae | 84 | static const chunk_t ASN1_nonce_oid = chunk_from_chars( |
552cc11b MW |
85 | 0x06, 0x09, |
86 | 0x2B, 0x06, | |
87 | 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 | |
3b878dae MW |
88 | ); |
89 | static const chunk_t ASN1_response_oid = chunk_from_chars( | |
552cc11b MW |
90 | 0x06, 0x09, |
91 | 0x2B, 0x06, | |
92 | 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 | |
3b878dae MW |
93 | ); |
94 | static const chunk_t ASN1_response_content = chunk_from_chars( | |
552cc11b MW |
95 | 0x04, 0x0D, |
96 | 0x30, 0x0B, | |
97 | 0x06, 0x09, | |
98 | 0x2B, 0x06, | |
99 | 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 | |
3b878dae | 100 | ); |
552cc11b MW |
101 | |
102 | /** | |
103 | * build requestorName | |
104 | */ | |
105 | static chunk_t build_requestorName(private_x509_ocsp_request_t *this) | |
106 | { | |
107 | if (this->requestor || this->cert) | |
108 | { /* use requestor name, fallback to his cert subject */ | |
109 | if (!this->requestor) | |
110 | { | |
111 | this->requestor = this->cert->get_subject(this->cert); | |
112 | this->requestor = this->requestor->clone(this->requestor); | |
113 | } | |
114 | return asn1_wrap(ASN1_CONTEXT_C_1, "m", | |
115 | asn1_simple_object(ASN1_CONTEXT_C_4, | |
116 | this->requestor->get_encoding(this->requestor))); | |
7daf5226 | 117 | |
552cc11b MW |
118 | } |
119 | return chunk_empty; | |
120 | } | |
121 | ||
122 | /** | |
123 | * build Request, not using singleRequestExtensions | |
124 | */ | |
125 | static chunk_t build_Request(private_x509_ocsp_request_t *this, | |
126 | chunk_t issuerNameHash, chunk_t issuerKeyHash, | |
127 | chunk_t serialNumber) | |
128 | { | |
129 | return asn1_wrap(ASN1_SEQUENCE, "m", | |
eb73685d | 130 | asn1_wrap(ASN1_SEQUENCE, "mmmm", |
552cc11b MW |
131 | asn1_algorithmIdentifier(OID_SHA1), |
132 | asn1_simple_object(ASN1_OCTET_STRING, issuerNameHash), | |
133 | asn1_simple_object(ASN1_OCTET_STRING, issuerKeyHash), | |
134 | asn1_simple_object(ASN1_INTEGER, serialNumber))); | |
135 | } | |
136 | ||
137 | /** | |
138 | * build requestList | |
139 | */ | |
140 | static chunk_t build_requestList(private_x509_ocsp_request_t *this) | |
141 | { | |
142 | chunk_t issuerNameHash, issuerKeyHash; | |
143 | identification_t *issuer; | |
144 | x509_t *x509; | |
145 | certificate_t *cert; | |
146 | chunk_t list = chunk_empty; | |
147 | public_key_t *public; | |
7daf5226 | 148 | |
552cc11b MW |
149 | cert = (certificate_t*)this->ca; |
150 | public = cert->get_public_key(cert); | |
151 | if (public) | |
152 | { | |
153 | hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); | |
154 | if (hasher) | |
155 | { | |
da9724e6 | 156 | if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, |
6b6ece63 | 157 | &issuerKeyHash)) |
552cc11b MW |
158 | { |
159 | enumerator_t *enumerator; | |
7daf5226 | 160 | |
552cc11b | 161 | issuer = cert->get_subject(cert); |
87dd205b MW |
162 | if (hasher->allocate_hash(hasher, issuer->get_encoding(issuer), |
163 | &issuerNameHash)) | |
552cc11b | 164 | { |
87dd205b MW |
165 | enumerator = this->candidates->create_enumerator( |
166 | this->candidates); | |
167 | while (enumerator->enumerate(enumerator, &x509)) | |
168 | { | |
169 | chunk_t request, serialNumber; | |
170 | ||
171 | serialNumber = x509->get_serial(x509); | |
172 | request = build_Request(this, issuerNameHash, | |
173 | issuerKeyHash, serialNumber); | |
174 | list = chunk_cat("mm", list, request); | |
175 | } | |
176 | enumerator->destroy(enumerator); | |
177 | chunk_free(&issuerNameHash); | |
552cc11b | 178 | } |
87dd205b | 179 | hasher->destroy(hasher); |
552cc11b MW |
180 | } |
181 | } | |
182 | else | |
183 | { | |
8b0e0910 | 184 | DBG1(DBG_LIB, "creating OCSP request failed, SHA1 not supported"); |
552cc11b MW |
185 | } |
186 | public->destroy(public); | |
187 | } | |
188 | else | |
189 | { | |
8b0e0910 TB |
190 | DBG1(DBG_LIB, "creating OCSP request failed, CA certificate has " |
191 | "no public key"); | |
552cc11b MW |
192 | } |
193 | return asn1_wrap(ASN1_SEQUENCE, "m", list); | |
194 | } | |
195 | ||
196 | /** | |
197 | * build nonce extension | |
198 | */ | |
199 | static chunk_t build_nonce(private_x509_ocsp_request_t *this) | |
200 | { | |
6a365f07 | 201 | rng_t *rng; |
7daf5226 | 202 | |
6a365f07 | 203 | rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); |
ae56e1eb | 204 | if (!rng || !rng->allocate_bytes(rng, NONCE_LEN, &this->nonce)) |
6a365f07 | 205 | { |
ae56e1eb TB |
206 | DBG1(DBG_LIB, "creating OCSP request nonce failed, no RNG found"); |
207 | DESTROY_IF(rng); | |
208 | return chunk_empty; | |
6a365f07 | 209 | } |
ae56e1eb TB |
210 | rng->destroy(rng); |
211 | return asn1_wrap(ASN1_SEQUENCE, "cm", ASN1_nonce_oid, | |
212 | asn1_simple_object(ASN1_OCTET_STRING, this->nonce)); | |
552cc11b MW |
213 | } |
214 | ||
215 | /** | |
216 | * build acceptableResponses extension | |
217 | */ | |
218 | static chunk_t build_acceptableResponses(private_x509_ocsp_request_t *this) | |
219 | { | |
220 | return asn1_wrap(ASN1_SEQUENCE, "cc", | |
221 | ASN1_response_oid, | |
222 | ASN1_response_content); | |
223 | } | |
224 | ||
225 | /** | |
226 | * build requestExtensions | |
227 | */ | |
228 | static chunk_t build_requestExtensions(private_x509_ocsp_request_t *this) | |
229 | { | |
323f9f99 | 230 | return asn1_wrap(ASN1_CONTEXT_C_2, "m", |
552cc11b MW |
231 | asn1_wrap(ASN1_SEQUENCE, "mm", |
232 | build_nonce(this), | |
233 | build_acceptableResponses(this))); | |
234 | } | |
235 | ||
236 | /** | |
237 | * build tbsRequest | |
238 | */ | |
239 | static chunk_t build_tbsRequest(private_x509_ocsp_request_t *this) | |
240 | { | |
241 | return asn1_wrap(ASN1_SEQUENCE, "mmm", | |
242 | build_requestorName(this), | |
243 | build_requestList(this), | |
244 | build_requestExtensions(this)); | |
245 | } | |
246 | ||
247 | /** | |
248 | * Build the optionalSignature | |
249 | */ | |
250 | static chunk_t build_optionalSignature(private_x509_ocsp_request_t *this, | |
251 | chunk_t tbsRequest) | |
252 | { | |
253 | int oid; | |
254 | signature_scheme_t scheme; | |
0406eeaa | 255 | chunk_t certs, signature, encoding; |
7daf5226 | 256 | |
552cc11b MW |
257 | switch (this->key->get_type(this->key)) |
258 | { | |
259 | /* TODO: use a generic mapping function */ | |
260 | case KEY_RSA: | |
261 | oid = OID_SHA1_WITH_RSA; | |
262 | scheme = SIGN_RSA_EMSA_PKCS1_SHA1; | |
263 | break; | |
0d12006d AS |
264 | case KEY_ECDSA: |
265 | oid = OID_ECDSA_WITH_SHA1; | |
472cb4ce | 266 | scheme = SIGN_ECDSA_WITH_SHA1_DER; |
0d12006d | 267 | break; |
552cc11b | 268 | default: |
8b0e0910 TB |
269 | DBG1(DBG_LIB, "unable to sign OCSP request, %N signature not " |
270 | "supported", key_type_names, this->key->get_type(this->key)); | |
552cc11b MW |
271 | return chunk_empty; |
272 | } | |
7daf5226 | 273 | |
552cc11b MW |
274 | if (!this->key->sign(this->key, scheme, tbsRequest, &signature)) |
275 | { | |
8b0e0910 | 276 | DBG1(DBG_LIB, "creating OCSP signature failed, skipped"); |
552cc11b MW |
277 | return chunk_empty; |
278 | } | |
0406eeaa MW |
279 | if (this->cert && |
280 | this->cert->get_encoding(this->cert, CERT_ASN1_DER, &encoding)) | |
552cc11b MW |
281 | { |
282 | certs = asn1_wrap(ASN1_CONTEXT_C_0, "m", | |
0406eeaa | 283 | asn1_wrap(ASN1_SEQUENCE, "m", encoding)); |
552cc11b MW |
284 | } |
285 | return asn1_wrap(ASN1_CONTEXT_C_0, "m", | |
7daf5226 | 286 | asn1_wrap(ASN1_SEQUENCE, "cmm", |
552cc11b MW |
287 | asn1_algorithmIdentifier(oid), |
288 | asn1_bitstring("m", signature), | |
289 | certs)); | |
290 | } | |
291 | ||
292 | /** | |
293 | * Build the OCSPRequest data | |
294 | * | |
295 | */ | |
296 | static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this) | |
297 | { | |
298 | chunk_t tbsRequest, optionalSignature = chunk_empty; | |
7daf5226 | 299 | |
552cc11b MW |
300 | tbsRequest = build_tbsRequest(this); |
301 | if (this->key) | |
302 | { | |
303 | optionalSignature = build_optionalSignature(this, tbsRequest); | |
304 | } | |
305 | return asn1_wrap(ASN1_SEQUENCE, "mm", tbsRequest, optionalSignature); | |
306 | } | |
307 | ||
308 | ||
fbe52bb0 AS |
309 | METHOD(certificate_t, get_type, certificate_type_t, |
310 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
311 | { |
312 | return CERT_X509_OCSP_REQUEST; | |
313 | } | |
314 | ||
fbe52bb0 AS |
315 | METHOD(certificate_t, get_subject, identification_t*, |
316 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
317 | { |
318 | certificate_t *ca = (certificate_t*)this->ca; | |
7daf5226 | 319 | |
552cc11b MW |
320 | if (this->requestor) |
321 | { | |
322 | return this->requestor; | |
323 | } | |
324 | if (this->cert) | |
325 | { | |
326 | return this->cert->get_subject(this->cert); | |
327 | } | |
328 | return ca->get_subject(ca); | |
329 | } | |
330 | ||
fbe52bb0 AS |
331 | METHOD(certificate_t, get_issuer, identification_t*, |
332 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
333 | { |
334 | certificate_t *ca = (certificate_t*)this->ca; | |
7daf5226 | 335 | |
552cc11b MW |
336 | return ca->get_subject(ca); |
337 | } | |
338 | ||
fbe52bb0 AS |
339 | METHOD(certificate_t, has_subject, id_match_t, |
340 | private_x509_ocsp_request_t *this, identification_t *subject) | |
552cc11b MW |
341 | { |
342 | certificate_t *current; | |
343 | enumerator_t *enumerator; | |
344 | id_match_t match, best = ID_MATCH_NONE; | |
345 | ||
346 | enumerator = this->candidates->create_enumerator(this->candidates); | |
347 | while (enumerator->enumerate(enumerator, ¤t)) | |
348 | { | |
349 | match = current->has_subject(current, subject); | |
350 | if (match > best) | |
351 | { | |
7daf5226 | 352 | best = match; |
552cc11b MW |
353 | } |
354 | } | |
355 | enumerator->destroy(enumerator); | |
7daf5226 | 356 | return best; |
552cc11b MW |
357 | } |
358 | ||
fbe52bb0 AS |
359 | METHOD(certificate_t, has_issuer, id_match_t, |
360 | private_x509_ocsp_request_t *this, | |
552cc11b MW |
361 | identification_t *issuer) |
362 | { | |
363 | certificate_t *ca = (certificate_t*)this->ca; | |
364 | ||
365 | return ca->has_subject(ca, issuer); | |
366 | } | |
367 | ||
fbe52bb0 | 368 | METHOD(certificate_t, issued_by, bool, |
a37f2d20 MW |
369 | private_x509_ocsp_request_t *this, certificate_t *issuer, |
370 | signature_scheme_t *scheme) | |
552cc11b | 371 | { |
8b0e0910 | 372 | DBG1(DBG_LIB, "OCSP request validation not implemented!"); |
552cc11b MW |
373 | return FALSE; |
374 | } | |
375 | ||
fbe52bb0 AS |
376 | METHOD(certificate_t, get_public_key, public_key_t*, |
377 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
378 | { |
379 | return NULL; | |
380 | } | |
381 | ||
fbe52bb0 AS |
382 | METHOD(certificate_t, get_validity, bool, |
383 | private_x509_ocsp_request_t *this, time_t *when, time_t *not_before, | |
384 | time_t *not_after) | |
552cc11b MW |
385 | { |
386 | certificate_t *cert; | |
387 | ||
388 | if (this->cert) | |
389 | { | |
390 | cert = this->cert; | |
391 | } | |
392 | else | |
393 | { | |
394 | cert = (certificate_t*)this->ca; | |
395 | } | |
396 | return cert->get_validity(cert, when, not_before, not_after); | |
397 | } | |
7daf5226 | 398 | |
fbe52bb0 AS |
399 | METHOD(certificate_t, get_encoding, bool, |
400 | private_x509_ocsp_request_t *this, cred_encoding_type_t type, | |
401 | chunk_t *encoding) | |
552cc11b | 402 | { |
0406eeaa MW |
403 | if (type == CERT_ASN1_DER) |
404 | { | |
405 | *encoding = chunk_clone(this->encoding); | |
406 | return TRUE; | |
407 | } | |
408 | return lib->encoding->encode(lib->encoding, type, NULL, encoding, | |
409 | CRED_PART_X509_OCSP_REQ_ASN1_DER, this->encoding, CRED_PART_END); | |
552cc11b MW |
410 | } |
411 | ||
fbe52bb0 AS |
412 | METHOD(certificate_t, equals, bool, |
413 | private_x509_ocsp_request_t *this, certificate_t *other) | |
552cc11b | 414 | { |
b5dbcc62 MW |
415 | chunk_t encoding; |
416 | bool equal; | |
7daf5226 | 417 | |
552cc11b MW |
418 | if (this == (private_x509_ocsp_request_t*)other) |
419 | { | |
420 | return TRUE; | |
421 | } | |
422 | if (other->get_type(other) != CERT_X509_OCSP_REQUEST) | |
423 | { | |
424 | return FALSE; | |
425 | } | |
552cc11b | 426 | if (other->equals == (void*)equals) |
b5dbcc62 | 427 | { /* skip allocation if we have the same implementation */ |
7daf5226 | 428 | return chunk_equals(this->encoding, ((private_x509_ocsp_request_t*)other)->encoding); |
552cc11b | 429 | } |
0406eeaa MW |
430 | if (!other->get_encoding(other, CERT_ASN1_DER, &encoding)) |
431 | { | |
432 | return FALSE; | |
433 | } | |
b5dbcc62 MW |
434 | equal = chunk_equals(this->encoding, encoding); |
435 | free(encoding.ptr); | |
436 | return equal; | |
552cc11b MW |
437 | } |
438 | ||
fbe52bb0 AS |
439 | METHOD(certificate_t, get_ref, certificate_t*, |
440 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
441 | { |
442 | ref_get(&this->ref); | |
fbe52bb0 | 443 | return &this->public.interface.interface; |
552cc11b MW |
444 | } |
445 | ||
fbe52bb0 AS |
446 | METHOD(certificate_t, destroy, void, |
447 | private_x509_ocsp_request_t *this) | |
552cc11b MW |
448 | { |
449 | if (ref_put(&this->ref)) | |
450 | { | |
451 | DESTROY_IF((certificate_t*)this->ca); | |
452 | DESTROY_IF(this->requestor); | |
453 | DESTROY_IF(this->cert); | |
454 | DESTROY_IF(this->key); | |
455 | this->candidates->destroy_offset(this->candidates, offsetof(certificate_t, destroy)); | |
456 | chunk_free(&this->nonce); | |
457 | chunk_free(&this->encoding); | |
458 | free(this); | |
459 | } | |
460 | } | |
461 | ||
462 | /** | |
463 | * create an empty but initialized OCSP request | |
464 | */ | |
465 | static private_x509_ocsp_request_t *create_empty() | |
466 | { | |
fbe52bb0 AS |
467 | private_x509_ocsp_request_t *this; |
468 | ||
469 | INIT(this, | |
470 | .public = { | |
471 | .interface = { | |
472 | .interface = { | |
473 | .get_type = _get_type, | |
474 | .get_subject = _get_subject, | |
475 | .get_issuer = _get_issuer, | |
476 | .has_subject = _has_subject, | |
477 | .has_issuer = _has_issuer, | |
478 | .issued_by = _issued_by, | |
479 | .get_public_key = _get_public_key, | |
480 | .get_validity = _get_validity, | |
481 | .get_encoding = _get_encoding, | |
482 | .equals = _equals, | |
483 | .get_ref = _get_ref, | |
484 | .destroy = _destroy, | |
485 | }, | |
486 | }, | |
487 | }, | |
488 | .candidates = linked_list_create(), | |
489 | .ref = 1, | |
490 | ); | |
7daf5226 | 491 | |
552cc11b MW |
492 | return this; |
493 | } | |
494 | ||
552cc11b | 495 | /** |
d73f453c | 496 | * See header. |
552cc11b | 497 | */ |
d73f453c | 498 | x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args) |
552cc11b MW |
499 | { |
500 | private_x509_ocsp_request_t *req; | |
d73f453c MW |
501 | certificate_t *cert; |
502 | private_key_t *private; | |
503 | identification_t *subject; | |
7daf5226 | 504 | |
d73f453c MW |
505 | req = create_empty(); |
506 | while (TRUE) | |
507 | { | |
508 | switch (va_arg(args, builder_part_t)) | |
509 | { | |
510 | case BUILD_CA_CERT: | |
511 | cert = va_arg(args, certificate_t*); | |
512 | if (cert->get_type(cert) == CERT_X509) | |
513 | { | |
514 | req->ca = (x509_t*)cert->get_ref(cert); | |
515 | } | |
516 | continue; | |
517 | case BUILD_CERT: | |
518 | cert = va_arg(args, certificate_t*); | |
519 | if (cert->get_type(cert) == CERT_X509) | |
520 | { | |
521 | req->candidates->insert_last(req->candidates, | |
522 | cert->get_ref(cert)); | |
523 | } | |
524 | continue; | |
525 | case BUILD_SIGNING_CERT: | |
526 | cert = va_arg(args, certificate_t*); | |
527 | req->cert = cert->get_ref(cert); | |
528 | continue; | |
529 | case BUILD_SIGNING_KEY: | |
530 | private = va_arg(args, private_key_t*); | |
531 | req->key = private->get_ref(private); | |
532 | continue; | |
533 | case BUILD_SUBJECT: | |
534 | subject = va_arg(args, identification_t*); | |
535 | req->requestor = subject->clone(subject); | |
536 | continue; | |
537 | case BUILD_END: | |
538 | break; | |
539 | default: | |
540 | destroy(req); | |
541 | return NULL; | |
542 | } | |
543 | break; | |
544 | } | |
552cc11b MW |
545 | if (req->ca) |
546 | { | |
547 | req->encoding = build_OCSPRequest(req); | |
548 | return &req->public; | |
549 | } | |
550 | destroy(req); | |
551 | return NULL; | |
552 | } | |
553 |