]> git.saurik.com Git - apple/security.git/blob - SecurityTool/sharedTool/scep.c
Security-59754.41.1.tar.gz
[apple/security.git] / SecurityTool / sharedTool / scep.c
1 /*
2 * Copyright (c) 2003-2004,2006-2007,2009-2010,2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * scep.c
24 */
25
26 #include <TargetConditionals.h>
27 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
28
29 #include "SecurityCommands.h"
30
31 #include <unistd.h>
32 #include <uuid/uuid.h>
33 #include <AssertMacros.h>
34
35 #include <Security/SecItem.h>
36 #include <Security/SecCertificateRequest.h>
37 #include <Security/SecCertificatePriv.h>
38 #include <Security/SecIdentityPriv.h>
39 #include <Security/SecSCEP.h>
40 #include <Security/SecCMS.h>
41
42 #include <utilities/array_size.h>
43 #include <utilities/SecIOFormat.h>
44
45 #include <CommonCrypto/CommonDigest.h>
46
47 #include <CFNetwork/CFNetwork.h>
48 #include "SecBase64.h"
49 #include <utilities/SecCFRelease.h>
50
51
52 #include <fcntl.h>
53 static inline void write_data(const char * path, CFDataRef data)
54 {
55 int data_file = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0644);
56 write(data_file, CFDataGetBytePtr(data), CFDataGetLength(data));
57 close(data_file);
58 }
59
60 #define BUFSIZE 1024
61
62 static CF_RETURNS_RETAINED CFHTTPMessageRef load_request(CFHTTPMessageRef request, CFMutableDataRef data, int retry, bool validate_cert)
63 {
64 CFHTTPMessageRef result = NULL;
65
66 if (retry < 0)
67 return result;
68
69 CFReadStreamRef rs;
70 rs = CFReadStreamCreateForHTTPRequest(NULL, request);
71
72 if (!validate_cert) {
73 const void *keys[] = {
74 kCFStreamSSLValidatesCertificateChain,
75 };
76 const void *values[] = {
77 kCFBooleanFalse,
78 };
79 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
80 array_size(keys),
81 &kCFTypeDictionaryKeyCallBacks,
82 &kCFTypeDictionaryValueCallBacks);
83 CFReadStreamSetProperty(rs, kCFStreamPropertySSLSettings, dict);
84 CFRelease(dict);
85 CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
86 }
87
88 if (CFReadStreamOpen(rs)) {
89 do {
90 UInt8 buf[BUFSIZE];
91 CFIndex bytesRead = CFReadStreamRead(rs, buf, BUFSIZE);
92 if (bytesRead > 0) {
93 CFDataAppendBytes(data, buf, bytesRead);
94 } else if (bytesRead == 0) {
95 result = (CFHTTPMessageRef)CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader);
96 break;
97 } else {
98 CFStreamStatus status = CFReadStreamGetStatus(rs);
99 CFStreamError error = CFReadStreamGetError(rs);
100 fprintf(stderr, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex " error=%ld)\n",
101 status, error.domain, (long) error.error);
102 break;
103 }
104 } while (true);
105 } else {
106 CFStreamStatus status = CFReadStreamGetStatus(rs);
107 CFStreamError error = CFReadStreamGetError(rs);
108 fprintf(stderr, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex " error=%ld)\n",
109 status, error.domain, (long) error.error);
110 }
111
112 CFReadStreamClose(rs);
113 CFRelease(rs);
114 return result;
115 }
116
117 static CF_RETURNS_RETAINED CFDataRef MCNetworkLoadRequest(CFURLRef url, CFDataRef content, CFStringRef type,
118 CFStringRef *contentType, bool validate_cert)
119 {
120 CFMutableDataRef out_data = CFDataCreateMutable(kCFAllocatorDefault, 0);
121 CFHTTPMessageRef response = NULL;
122 CFStringRef request_type = content ? CFSTR("POST") : CFSTR("GET");
123 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
124 request_type, url, kCFHTTPVersion1_0);
125 if (content)
126 CFHTTPMessageSetBody(request, content);
127 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), type);
128
129 int retries = 1;
130 do {
131 response = load_request(request, out_data, 1, validate_cert);
132 if (!response && retries) {
133 sleep(5);
134 CFDataSetLength(out_data, 0);
135 }
136 } while (!response && retries--);
137
138 CFRelease(request);
139
140 CFIndex status_code = response ? CFHTTPMessageGetResponseStatusCode(response) : 0;
141 if (!response || (200 != status_code)) {
142 CFStringRef url_string = CFURLGetString(url);
143 if (url_string && request_type && out_data)
144 fprintf(stderr, "MCNetworkLoadRequest failed to load\n");
145
146 CFReleaseNull(out_data);
147 goto out;
148 }
149
150 if (contentType)
151 *contentType = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type"));
152 out:
153 CFReleaseSafe(response);
154 return out_data;
155 }
156
157 static void _query_string_apply(CFMutableStringRef query_string, const void *key, const void *value)
158 {
159 CFStringRef escaped_key =
160 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
161 (CFStringRef)key, NULL, CFSTR("+/="), kCFStringEncodingUTF8);
162 CFStringRef escaped_value =
163 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
164 (CFStringRef)value, NULL, CFSTR("+/="), kCFStringEncodingUTF8);
165
166 if (CFStringGetLength(query_string) > 1)
167 CFStringAppend(query_string, CFSTR("&"));
168
169 CFStringAppendFormat(query_string, NULL, CFSTR("%@=%@"), escaped_key, escaped_value);
170 CFRelease(escaped_key);
171 CFRelease(escaped_value);
172 }
173
174 static CF_RETURNS_RETAINED CFURLRef scep_url_operation(CFStringRef base, CFStringRef operation, CFStringRef message)
175 {
176 CFURLRef url = NULL, base_url = NULL;
177 CFMutableStringRef query_string =
178 CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("?"));
179 require(query_string, out);
180 require(operation, out);
181 _query_string_apply(query_string, CFSTR("operation"), operation);
182 if (message)
183 _query_string_apply(query_string, CFSTR("message"), message);
184 base_url = CFURLCreateWithString(kCFAllocatorDefault, base, NULL);
185 url = CFURLCreateWithString(kCFAllocatorDefault, query_string, base_url);
186 out:
187 if (query_string)
188 CFRelease(query_string);
189 if (base_url)
190 CFRelease(base_url);
191 return url;
192 }
193
194 static CF_RETURNS_RETAINED CFDataRef perform_pki_op(CFStringRef scep_base_url, CFDataRef scep_request, bool scep_can_use_post, bool validate_cert)
195 {
196 CFDataRef scep_reply = NULL;
197 CFURLRef pki_op = NULL;
198 if (scep_can_use_post) {
199 pki_op = scep_url_operation(scep_base_url, CFSTR("PKIOperation"), NULL);
200 scep_reply = MCNetworkLoadRequest(pki_op, scep_request, CFSTR("application/x-pki-message"), NULL, validate_cert);
201 } else {
202 SecBase64Result base64_result;
203 size_t buffer_length = CFDataGetLength(scep_request)*2+1;
204 char *buffer = malloc(buffer_length);
205 require(buffer, out);
206 size_t output_size = SecBase64Encode2(CFDataGetBytePtr(scep_request), CFDataGetLength(scep_request), buffer, buffer_length,
207 kSecB64_F_LINE_LEN_INFINITE, -1, &base64_result);
208 *(buffer + output_size) = '\0';
209 require(!base64_result, out);
210 CFStringRef message = CFStringCreateWithCString(kCFAllocatorDefault, buffer, kCFStringEncodingASCII);
211 require(message, out);
212 pki_op = scep_url_operation(scep_base_url, CFSTR("PKIOperation"), message);
213 CFRelease(message);
214 fprintf(stderr, "Performing PKIOperation using GET\n");
215 scep_reply = MCNetworkLoadRequest(pki_op, NULL, CFSTR("application/x-pki-message"), NULL, validate_cert);
216 }
217 out:
218 CFReleaseSafe(pki_op);
219 return scep_reply;
220 }
221
222
223 static inline CF_RETURNS_RETAINED CFStringRef uuid_cfstring()
224 {
225 char uuid_string[40] = "CN=";
226 uuid_t uuid;
227 uuid_generate_random(uuid);
228 uuid_unparse(uuid, uuid_string+3);
229 return CFStringCreateWithCString(kCFAllocatorDefault, uuid_string, kCFStringEncodingASCII);
230 }
231
232 /* /O=foo/CN=blah => [ [ [ O, foo ] ], [ [ CN, blah ] ] ] */
233 static void make_subject_pairs(const void *value, void *context)
234 {
235 CFArrayRef entries = NULL, array = NULL;
236 if (!CFStringGetLength(value))
237 return; /* skip '/'s that aren't separating key/vals */
238 entries = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, value, CFSTR("="));
239 require(entries, out);
240 if (CFArrayGetCount(entries)) {
241 array = CFArrayCreate(kCFAllocatorDefault, (const void **)&entries, 1, &kCFTypeArrayCallBacks);
242 require(array, out);
243 CFArrayAppendValue((CFMutableArrayRef)context, array);
244 }
245 out:
246 if (entries) CFRelease(entries);
247 if (array) CFRelease(array);
248 }
249
250 static CFArrayRef make_scep_subject(CFStringRef scep_subject_name)
251 {
252 CFMutableArrayRef subject = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
253 require(subject, out);
254 CFArrayRef entries = NULL;
255 entries = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, scep_subject_name, CFSTR("/"));
256 require(entries, out);
257 CFArrayApplyFunction(entries, CFRangeMake(0, CFArrayGetCount(entries)), make_subject_pairs, subject);
258 CFRelease(entries);
259 if (CFArrayGetCount(subject))
260 return subject;
261 out:
262 if (subject) CFRelease(subject);
263 return NULL;
264 }
265
266
267 extern int command_scep(int argc, char * const *argv)
268 {
269 int result = 1, verbose = 0;
270 char ch;
271 int key_usage = 1, key_bitsize = 1024;
272 bool validate_cert = true;
273 CFStringRef scep_challenge = NULL, scep_instance_name = NULL,
274 scep_subject_name = uuid_cfstring(), scep_subject_alt_name = NULL,
275 scep_capabilities = NULL;
276
277 SecCertificateRef ca_certificate = NULL, ra_certificate = NULL;
278 CFArrayRef cert_array = NULL;
279 CFDataRef caps_data = NULL;
280
281 while ((ch = getopt(argc, argv, "vu:b:c:n:s:h:xo:")) != -1)
282 {
283 switch (ch)
284 {
285 case 'v':
286 verbose++;
287 break;
288 case 'u':
289 key_usage = atoi(optarg);
290 break;
291 case 'b':
292 key_bitsize = atoi(optarg);
293 break;
294 case 'c':
295 CFReleaseNull(scep_challenge);
296 scep_challenge = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
297 break;
298 case 'n':
299 CFReleaseNull(scep_instance_name);
300 scep_instance_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
301 break;
302 case 's':
303 CFReleaseNull(scep_subject_name);
304 scep_subject_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
305 break;
306 case 'h':
307 CFReleaseNull(scep_subject_alt_name);
308 scep_subject_alt_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
309 break;
310 case 'x':
311 validate_cert = false;
312 break;
313 case 'o':
314 CFReleaseNull(scep_capabilities);
315 scep_capabilities = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
316 break;
317 default:
318 CFReleaseNull(scep_subject_name);
319 CFReleaseNull(scep_capabilities);
320 CFReleaseNull(scep_challenge);
321 CFReleaseNull(scep_instance_name);
322 CFReleaseNull(scep_subject_alt_name);
323 return SHOW_USAGE_MESSAGE;
324 }
325 }
326
327 argc -= optind;
328 argv += optind;
329
330 if (argc != 1) {
331 CFReleaseNull(scep_subject_name);
332 CFReleaseNull(scep_capabilities);
333 CFReleaseNull(scep_challenge);
334 CFReleaseNull(scep_instance_name);
335 CFReleaseNull(scep_subject_alt_name);
336 return SHOW_USAGE_MESSAGE;
337 }
338
339 CFDataRef scep_request = NULL;
340 CFArrayRef issued_certs = NULL;
341 SecCertificateRef leaf = NULL;
342 SecIdentityRef candidate_identity = NULL;
343 CFMutableDictionaryRef csr_parameters = NULL;
344 CFDataRef scep_reply = NULL;
345 SecKeyRef phone_publicKey = NULL, phone_privateKey = NULL;
346 CFStringRef scep_base_url = NULL;
347 CFDictionaryRef identity_add = NULL;
348
349 CFNumberRef scep_key_usage = NULL;
350 CFNumberRef scep_key_bitsize = NULL;
351
352 CFURLRef url = NULL;
353
354 CFDataRef data = NULL;
355 CFStringRef ctype = NULL;
356
357 scep_base_url = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingASCII);
358
359 #if 0
360 CFStringRef uuid_cfstr = uuid_cfstring();
361 require(uuid_cfstr, out);
362 const void * ca_cn[] = { kSecOidCommonName, uuid_cfstr };
363 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
364 const void *ca_dn_array[1];
365 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
366 CFArrayRef scep_subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, array_size(ca_dn_array), NULL);
367 #else
368 CFArrayRef scep_subject = make_scep_subject(scep_subject_name);
369 require(scep_subject, out);
370 CFShow(scep_subject);
371 #endif
372
373 scep_key_usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage);
374 scep_key_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_bitsize);
375
376 const void *keygen_keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
377 const void *keygen_vals[] = { kSecAttrKeyTypeRSA, scep_key_bitsize };
378 CFDictionaryRef keygen_parameters = CFDictionaryCreate(kCFAllocatorDefault,
379 keygen_keys, keygen_vals, array_size(keygen_vals),
380 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
381 CFRelease(scep_key_bitsize);
382 require_noerr(SecKeyGeneratePair(keygen_parameters, &phone_publicKey, &phone_privateKey), out);
383 CFRelease(keygen_parameters);
384
385 /* GetCACert
386
387 A binary X.509 CA certificate is sent back as a MIME object with a
388 Content-Type of application/x-x509-ca-cert.
389
390 When an RA exists, both CA and RA certificates must be sent back in
391 the response to the GetCACert request. The RA certificate(s) must be
392 signed by the CA. A certificates-only PKCS#7 [RFC2315] SignedData is
393 used to carry the certificates to the requester, with a Content-Type
394 of application/x-x509-ca-ra-cert.
395 */
396 SecCertificateRef ra_encryption_certificate = NULL;
397 CFArrayRef ra_certificates = NULL;
398 CFTypeRef scep_certificates = NULL;
399 SecCertificateRef scep_signing_certificate = NULL;
400
401 url = scep_url_operation(scep_base_url, CFSTR("GetCACert"), scep_instance_name);
402 data = MCNetworkLoadRequest(url, NULL, NULL, &ctype, validate_cert);
403
404 if (data && ctype) {
405 if (CFEqual(CFSTR("application/x-x509-ca-cert"), ctype)) {
406 ca_certificate = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)data);
407 fprintf(stderr, "GetCACert returned a single CA certificate.\n");
408 } else if (CFEqual(ctype, CFSTR("application/x-x509-ca-ra-cert"))) {
409 cert_array = SecCMSCertificatesOnlyMessageCopyCertificates(data);
410
411 require_noerr(SecSCEPValidateCACertMessage(cert_array,
412 NULL,
413 &ca_certificate,
414 &ra_certificate,
415 &ra_encryption_certificate), out);
416
417 if (ra_certificate && ra_encryption_certificate) {
418 const void *ra_certs[] = { ra_encryption_certificate, ra_certificate };
419 ra_certificates = CFArrayCreate(kCFAllocatorDefault, ra_certs, array_size(ra_certs), &kCFTypeArrayCallBacks);
420 fprintf(stderr, "GetCACert returned a separate signing and encryption certificates for RA.\n");
421 }
422 }
423 }
424
425 fprintf(stderr, "CA certificate to issue cert:\n");
426 CFShow(ca_certificate);
427
428 if (ra_certificates) {
429 scep_certificates = ra_certificates;
430 scep_signing_certificate = ra_certificate;
431 } else if (ra_certificate) {
432 scep_certificates = ra_certificate;
433 scep_signing_certificate = ra_certificate;
434 } else if (ca_certificate) {
435 scep_certificates = ca_certificate;
436 scep_signing_certificate = ca_certificate;
437 } else {
438 fprintf(stderr, "Unsupported GetCACert configuration: please file a bug.\n");
439 goto out;
440 }
441
442 (void) scep_signing_certificate; // Silence analyzer
443
444 /*
445 GetCACaps capabilities advertised by SCEP server:
446
447 +--------------------+----------------------------------------------+
448 | Keyword | Description |
449 +--------------------+----------------------------------------------+
450 | "AES" | CA supports the AES128-CBC encryption |
451 | | algorithm. |
452 | | |
453 | "DES3" | CA supports the triple DES-CBC encryption |
454 | | algorithm. |
455 | | |
456 | "GetNextCACert" | CA supports the GetNextCACert |
457 | | message. |
458 | | |
459 | "POSTPKIOperation" | CA supports PKIOPeration messages sent |
460 | | via HTTP POST. |
461 | | |
462 | "Renewal" | CA supports the Renewal CA operation. |
463 | | |
464 | "SHA-1" | CA supports the SHA-1 hashing algorithm. |
465 | | |
466 | "SHA-256" | CA supports the SHA-256 hashing algorithm. |
467 | | |
468 | "SHA-512" | CA supports the SHA-512 hashing algorithm. |
469 | | |
470 | "SCEPStandard" | CA supports all mandatory-to-implement |
471 | | sections of the SCEP standard. This keyword |
472 | | implies "AES", |
473 | | "POSTPKIOperation", and "SHA-256", as well |
474 | | as the provisions of |
475 | | Section 2.9. |
476 +--------------------+----------------------------------------------+
477 */
478
479 bool scep_can_use_post = false;
480 bool scep_can_use_3des = false;
481 bool scep_can_use_aes = false;
482 bool scep_can_use_sha1 = false;
483 bool scep_can_use_sha512 = false;
484 bool scep_can_use_sha256 = false;
485
486 CFArrayRef caps = NULL;
487 if (!scep_capabilities) {
488 CFURLRef ca_caps_url = scep_url_operation(scep_base_url, CFSTR("GetCACaps"), scep_instance_name);
489 require(ca_caps_url, out);
490 caps_data = MCNetworkLoadRequest(ca_caps_url, NULL, NULL, NULL, validate_cert);
491 CFRelease(ca_caps_url);
492 if (caps_data) {
493 CFStringRef caps_data_string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, caps_data, kCFStringEncodingASCII);
494 require(caps_data_string, out);
495 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, caps_data_string, CFSTR("\n"));
496 if (!caps) {
497 fprintf(stderr, "GetCACaps couldn't be parsed:\n");
498 CFShow(caps_data);
499 }
500 CFRelease(caps_data);
501 CFRelease(caps_data_string);
502 }
503 } else {
504 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, scep_capabilities, CFSTR(","));
505 }
506
507 if (caps) {
508 fprintf(stderr, "GetCACaps advertised following capabilities:\n");
509 CFShow(caps);
510
511 CFRange caps_length = CFRangeMake(0, CFArrayGetCount(caps));
512 scep_can_use_post = CFArrayContainsValue(caps, caps_length, CFSTR("POSTPKIOperation"));
513 scep_can_use_3des = CFArrayContainsValue(caps, caps_length, CFSTR("DES3"));
514 scep_can_use_aes = CFArrayContainsValue(caps, caps_length, CFSTR("AES"));
515 scep_can_use_sha1 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-1"));
516 scep_can_use_sha256 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-256"));
517 scep_can_use_sha512 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-512"));
518 CFRelease(caps);
519 }
520
521 csr_parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
522 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
523 if (scep_key_usage)
524 CFDictionarySetValue(csr_parameters, kSecCertificateKeyUsage, scep_key_usage);
525 if (scep_challenge)
526 CFDictionarySetValue(csr_parameters, kSecCSRChallengePassword, scep_challenge);
527 else
528 fprintf(stderr, "No SCEP challenge provided, hope that's ok.\n");
529
530 // set encryption algorithm for CMS
531 if (scep_can_use_aes) {
532 CFDictionarySetValue(csr_parameters, kSecCMSBulkEncryptionAlgorithm, kSecCMSEncryptionAlgorithmAESCBC);
533 } else if (!scep_can_use_3des) {
534 fprintf(stderr, "SCEP server does not support 3DES -- using it anyway. You must reconfigure your server.\n");
535 }
536
537 // set hash algorithmn for CMS
538 if (scep_can_use_sha512) {
539 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA512);
540 } else if (scep_can_use_sha256) {
541 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA256);
542 } else if (scep_can_use_sha1) {
543 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA1);
544 } else {
545 fprintf(stderr, "SCEP server does not support SHA-1 -- using it anyway. You must reconfigure your server.\n");
546 }
547
548 if (scep_subject_alt_name) {
549 fprintf(stderr, "Adding subjectAltName to request\n");
550 CFDictionaryRef subject_alt_name = CFDictionaryCreate(kCFAllocatorDefault,
551 (const void **)&kSecSubjectAltNameDNSName, (const void **)&scep_subject_alt_name,
552 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
553 CFDictionarySetValue(csr_parameters, kSecSubjectAltName, subject_alt_name);
554 }
555
556 SecIdentityRef self_signed_identity = SecSCEPCreateTemporaryIdentity(phone_publicKey, phone_privateKey);
557
558 // store encryption identity in the keychain because the decrypt function looks in there only
559 identity_add = CFDictionaryCreate(NULL,
560 (const void **)&kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL);
561 require_noerr(SecItemAdd(identity_add, NULL), out);
562
563 require(scep_request = SecSCEPGenerateCertificateRequest((CFArrayRef)scep_subject,
564 csr_parameters, phone_publicKey, phone_privateKey, self_signed_identity,
565 scep_certificates), out);
566
567 fprintf(stderr, "storing scep_request.der\n");
568 write_data("scep_request.der", scep_request);
569
570 scep_reply = perform_pki_op(scep_base_url, scep_request, scep_can_use_post, validate_cert);
571 require(scep_reply, out);
572
573 require_action(CFDataGetLength(scep_reply), out, fprintf(stderr, "Empty scep_reply, exiting.\n"));
574 fprintf(stderr, "Storing scep_reply.der\n");
575 write_data("scep_reply.der", scep_reply);
576
577 CFErrorRef server_error = NULL;
578 int retry_count = 3;
579 while ( !(issued_certs = SecSCEPVerifyReply(scep_request, scep_reply, scep_certificates, &server_error)) &&
580 server_error &&
581 retry_count--)
582 {
583 CFDataRef retry_get_cert_initial = NULL;
584 CFDictionaryRef error_dict = CFErrorCopyUserInfo(server_error);
585 retry_get_cert_initial = SecSCEPGetCertInitial(ra_certificate ? ra_certificate : ca_certificate, scep_subject, NULL, error_dict, self_signed_identity, scep_certificates);
586 CFReleaseNull(scep_reply);
587 CFReleaseSafe(error_dict);
588 fprintf(stderr, "Waiting 10 seconds before trying a GetCertInitial\n");
589 sleep(10);
590 scep_reply = perform_pki_op(scep_base_url, retry_get_cert_initial, scep_can_use_post, validate_cert);
591 CFReleaseSafe(retry_get_cert_initial);
592 }
593
594 require(issued_certs, out);
595 require_string(CFArrayGetCount(issued_certs) > 0, out, "No certificates issued.");
596
597 leaf = (SecCertificateRef)CFArrayGetValueAtIndex(issued_certs, 0);
598 require(leaf, out);
599 CFDataRef leaf_data = SecCertificateCopyData(leaf);
600 if (leaf_data) {
601 fprintf(stderr, "Storing issued_cert.der\n");
602 write_data("issued_cert.der", leaf_data);
603 CFRelease(leaf_data);
604 }
605 CFShow(leaf);
606
607 candidate_identity = SecIdentityCreate(kCFAllocatorDefault, leaf, phone_privateKey);
608
609 const void *keys_ref_to_persist[] = {
610 /*kSecReturnPersistentRef, */kSecValueRef, kSecAttrLabel };
611 const void *values_ref_to_persist[] = {
612 /*kCFBooleanTrue, */candidate_identity, scep_subject_name };
613 CFDictionaryRef dict = CFDictionaryCreate(NULL,
614 (const void **)keys_ref_to_persist,
615 (const void **)values_ref_to_persist,
616 array_size(keys_ref_to_persist),
617 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
618 OSStatus status = SecItemAdd(dict, NULL);
619 require_noerr_action(status, out, fprintf(stderr, "failed to store new identity, SecItemAdd: %" PRIdOSStatus, status));
620 result = 0;
621
622 out:
623 if (identity_add)
624 SecItemDelete(identity_add);
625 CFReleaseSafe(identity_add);
626 //if (uuid_cfstr) CFRelease(uuid_cfstr);
627 CFReleaseSafe(candidate_identity);
628 CFReleaseSafe(scep_request);
629 CFReleaseSafe(scep_reply);
630 CFReleaseSafe(scep_key_usage);
631 CFReleaseSafe(scep_key_bitsize);
632 CFReleaseSafe(csr_parameters);
633 CFReleaseSafe(scep_subject_name);
634 CFReleaseSafe(scep_base_url);
635 CFReleaseSafe(url);
636 CFReleaseSafe(issued_certs);
637 CFReleaseSafe(data);
638 CFReleaseSafe(ctype);
639 CFReleaseNull(ca_certificate);
640 CFReleaseNull(scep_capabilities);
641 CFReleaseNull(scep_challenge);
642 CFReleaseNull(scep_instance_name);
643 CFReleaseNull(scep_subject_alt_name);
644 CFReleaseNull(cert_array);
645 CFReleaseNull(caps_data);
646
647 return result;
648 }
649
650
651 #endif // TARGET_OS_MAC