2 * Copyright (c) 2003-2004,2006-2007,2009-2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <TargetConditionals.h>
27 #if TARGET_OS_EMBEDDED
29 #include "SecurityCommands.h"
32 #include <uuid/uuid.h>
33 #include <AssertMacros.h>
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>
42 #include <utilities/array_size.h>
43 #include <utilities/SecIOFormat.h>
45 #include <CommonCrypto/CommonDigest.h>
47 #include <CFNetwork/CFNetwork.h>
48 #include "SecBase64.h"
52 static inline void write_data(const char * path
, CFDataRef data
)
54 int data_file
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0644);
55 write(data_file
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
61 static CFHTTPMessageRef
load_request(CFHTTPMessageRef request
, CFMutableDataRef data
, int retry
, bool validate_cert
)
63 CFHTTPMessageRef result
= NULL
;
69 rs
= CFReadStreamCreateForHTTPRequest(NULL
, request
);
72 const void *keys
[] = {
73 kCFStreamSSLValidatesCertificateChain
,
75 const void *values
[] = {
78 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
,
80 &kCFTypeDictionaryKeyCallBacks
,
81 &kCFTypeDictionaryValueCallBacks
);
82 CFReadStreamSetProperty(rs
, kCFStreamPropertySSLSettings
, dict
);
84 CFReadStreamSetProperty(rs
, kCFStreamPropertyHTTPAttemptPersistentConnection
, kCFBooleanTrue
);
87 if (CFReadStreamOpen(rs
)) {
90 CFIndex bytesRead
= CFReadStreamRead(rs
, buf
, BUFSIZE
);
92 CFDataAppendBytes(data
, buf
, bytesRead
);
93 } else if (bytesRead
== 0) {
94 result
= (CFHTTPMessageRef
)CFReadStreamCopyProperty(rs
, kCFStreamPropertyHTTPResponseHeader
);
97 CFStreamStatus status
= CFReadStreamGetStatus(rs
);
98 CFStreamError error
= CFReadStreamGetError(rs
);
99 fprintf(stderr
, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex
" error=%ld)\n",
100 status
, error
.domain
, (long) error
.error
);
105 CFStreamStatus status
= CFReadStreamGetStatus(rs
);
106 CFStreamError error
= CFReadStreamGetError(rs
);
107 fprintf(stderr
, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex
" error=%ld)\n",
108 status
, error
.domain
, (long) error
.error
);
111 CFReadStreamClose(rs
);
116 static CFDataRef
MCNetworkLoadRequest(CFURLRef url
, CFDataRef content
, CFStringRef type
,
117 CFStringRef
*contentType
, bool validate_cert
)
119 CFMutableDataRef out_data
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
120 CFHTTPMessageRef response
= NULL
;
121 CFStringRef request_type
= content
? CFSTR("POST") : CFSTR("GET");
122 CFHTTPMessageRef request
= CFHTTPMessageCreateRequest(kCFAllocatorDefault
,
123 request_type
, url
, kCFHTTPVersion1_0
);
125 CFHTTPMessageSetBody(request
, content
);
126 CFHTTPMessageSetHeaderFieldValue(request
, CFSTR("Content-Type"), type
);
130 response
= load_request(request
, out_data
, 1, validate_cert
);
131 if (!response
&& retries
) {
133 CFDataSetLength(out_data
, 0);
135 } while (!response
&& retries
--);
139 CFIndex status_code
= response
? CFHTTPMessageGetResponseStatusCode(response
) : 0;
140 if (!response
|| (200 != status_code
)) {
141 CFStringRef url_string
= CFURLGetString(url
);
142 if (url_string
&& request_type
&& out_data
)
143 fprintf(stderr
, "MCNetworkLoadRequest failed to load\n");
148 *contentType
= CFHTTPMessageCopyHeaderFieldValue(response
, CFSTR("Content-Type"));
154 static void _query_string_apply(CFMutableStringRef query_string
, const void *key
, const void *value
)
156 CFStringRef escaped_key
=
157 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault
,
158 (CFStringRef
)key
, NULL
, CFSTR("+/="), kCFStringEncodingUTF8
);
159 CFStringRef escaped_value
=
160 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault
,
161 (CFStringRef
)value
, NULL
, CFSTR("+/="), kCFStringEncodingUTF8
);
164 if (CFStringGetLength(query_string
) > 1)
165 format
= CFSTR("&%@=%@");
167 format
= CFSTR("%@=%@");
169 CFStringAppendFormat(query_string
, NULL
, format
, escaped_key
, escaped_value
);
170 CFRelease(escaped_key
);
171 CFRelease(escaped_value
);
174 static CFURLRef
scep_url_operation(CFStringRef base
, CFStringRef operation
, CFStringRef message
)
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
);
183 _query_string_apply(query_string
, CFSTR("message"), message
);
184 base_url
= CFURLCreateWithString(kCFAllocatorDefault
, base
, NULL
);
185 url
= CFURLCreateWithString(kCFAllocatorDefault
, query_string
, base_url
);
188 CFRelease(query_string
);
194 static CFDataRef
perform_pki_op(CFStringRef scep_base_url
, CFDataRef scep_request
, bool scep_can_use_post
, bool validate_cert
)
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
);
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
);
214 fprintf(stderr
, "Performing PKIOperation using GET\n");
215 scep_reply
= MCNetworkLoadRequest(pki_op
, NULL
, CFSTR("application/x-pki-message"), NULL
, validate_cert
);
218 if (pki_op
) CFRelease(pki_op
);
223 static inline CFStringRef
uuid_cfstring()
225 char uuid_string
[40] = "CN=";
227 uuid_generate_random(uuid
);
228 uuid_unparse(uuid
, uuid_string
+3);
229 return CFStringCreateWithCString(kCFAllocatorDefault
, uuid_string
, kCFStringEncodingASCII
);
232 /* /O=foo/CN=blah => [ [ [ O, foo ] ], [ [ CN, blah ] ] ] */
233 static void make_subject_pairs(const void *value
, void *context
)
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
);
243 CFArrayAppendValue((CFMutableArrayRef
)context
, array
);
246 if (entries
) CFRelease(entries
);
247 if (array
) CFRelease(array
);
250 static CFArrayRef
make_scep_subject(CFStringRef scep_subject_name
)
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
);
259 if (CFArrayGetCount(subject
))
262 if (subject
) CFRelease(subject
);
267 extern int command_scep(int argc
, char * const *argv
)
269 int result
= 1, verbose
= 0;
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
;
277 while ((ch
= getopt(argc
, argv
, "vu:b:c:n:s:h:xo:")) != -1)
285 key_usage
= atoi(optarg
);
288 key_bitsize
= atoi(optarg
);
291 scep_challenge
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
294 scep_instance_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
297 scep_subject_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
300 scep_subject_alt_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
303 validate_cert
= false;
306 scep_capabilities
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
309 return 2; /* Trigger usage message. */
317 return 2; /* Trigger usage message. */
319 CFDataRef scep_request
= NULL
;
320 CFArrayRef issued_certs
= NULL
;
321 SecCertificateRef leaf
= NULL
;
322 SecIdentityRef candidate_identity
= NULL
;
323 CFMutableDictionaryRef csr_parameters
= NULL
;
324 CFDataRef scep_reply
= NULL
;
325 SecKeyRef phone_publicKey
= NULL
, phone_privateKey
= NULL
;
326 CFStringRef scep_base_url
= NULL
;
327 CFDictionaryRef identity_add
= NULL
;
329 scep_base_url
= CFStringCreateWithCString(kCFAllocatorDefault
, argv
[0], kCFStringEncodingASCII
);
332 CFStringRef uuid_cfstr
= uuid_cfstring();
333 require(uuid_cfstr
, out
);
334 const void * ca_cn
[] = { kSecOidCommonName
, uuid_cfstr
};
335 CFArrayRef ca_cn_dn
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
);
336 const void *ca_dn_array
[1];
337 ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
);
338 CFArrayRef scep_subject
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, array_size(ca_dn_array
), NULL
);
340 CFArrayRef scep_subject
= make_scep_subject(scep_subject_name
);
341 require(scep_subject
, out
);
342 CFShow(scep_subject
);
345 CFNumberRef scep_key_usage
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_usage
);
346 CFNumberRef scep_key_bitsize
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_bitsize
);
348 const void *keygen_keys
[] = { kSecAttrKeyType
, kSecAttrKeySizeInBits
};
349 const void *keygen_vals
[] = { kSecAttrKeyTypeRSA
, scep_key_bitsize
};
350 CFDictionaryRef keygen_parameters
= CFDictionaryCreate(kCFAllocatorDefault
,
351 keygen_keys
, keygen_vals
, array_size(keygen_vals
),
352 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
353 CFRelease(scep_key_bitsize
);
354 require_noerr(SecKeyGeneratePair(keygen_parameters
, &phone_publicKey
, &phone_privateKey
), out
);
355 CFRelease(keygen_parameters
);
359 A binary X.509 CA certificate is sent back as a MIME object with a
360 Content-Type of application/x-x509-ca-cert.
362 When an RA exists, both CA and RA certificates must be sent back in
363 the response to the GetCACert request. The RA certificate(s) must be
364 signed by the CA. A certificates-only PKCS#7 [RFC2315] SignedData is
365 used to carry the certificates to the requester, with a Content-Type
366 of application/x-x509-ca-ra-cert.
368 CFDataRef data
= NULL
;
369 CFStringRef ctype
= NULL
;
370 SecCertificateRef ca_certificate
= NULL
, ra_certificate
= NULL
;
371 SecCertificateRef ra_encryption_certificate
= NULL
;
372 CFArrayRef ra_certificates
= NULL
;
373 CFTypeRef scep_certificates
= NULL
;
374 SecCertificateRef scep_signing_certificate
= NULL
;
376 CFURLRef url
= scep_url_operation(scep_base_url
, CFSTR("GetCACert"), scep_instance_name
);
377 data
= MCNetworkLoadRequest(url
, NULL
, NULL
, &ctype
, validate_cert
);
380 if (CFEqual(CFSTR("application/x-x509-ca-cert"), ctype
)) {
381 ca_certificate
= SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)data
);
382 fprintf(stderr
, "GetCACert returned a single CA certificate.\n");
383 } else if (CFEqual(ctype
, CFSTR("application/x-x509-ca-ra-cert"))) {
384 CFArrayRef cert_array
= SecCMSCertificatesOnlyMessageCopyCertificates(data
);
386 require_noerr(SecSCEPValidateCACertMessage(cert_array
,
390 &ra_encryption_certificate
), out
);
392 if (ra_certificate
&& ra_encryption_certificate
) {
393 const void *ra_certs
[] = { ra_encryption_certificate
, ra_certificate
};
394 ra_certificates
= CFArrayCreate(kCFAllocatorDefault
, ra_certs
, array_size(ra_certs
), &kCFTypeArrayCallBacks
);
395 fprintf(stderr
, "GetCACert returned a separate signing and encryption certificates for RA.\n");
397 CFRelease(cert_array
);
401 fprintf(stderr
, "CA certificate to issue cert:\n");
402 CFShow(ca_certificate
);
404 if (ra_certificates
) {
405 scep_certificates
= ra_certificates
;
406 scep_signing_certificate
= ra_certificate
;
407 } else if (ra_certificate
) {
408 scep_certificates
= ra_certificate
;
409 scep_signing_certificate
= ra_certificate
;
410 } else if (ca_certificate
) {
411 scep_certificates
= ca_certificate
;
412 scep_signing_certificate
= ca_certificate
;
414 fprintf(stderr
, "Unsupported GetCACert configuration: please file a bug.\n");
419 GetCACaps capabilities advertised by SCEP server
:
421 +--------------------+----------------------------------------------+
422 | Keyword
| Description
|
423 +--------------------+----------------------------------------------+
424 | "GetNextCACert" | CA Supports the GetNextCACert message
. |
425 | "POSTPKIOperation" | PKIOPeration messages may be sent via HTTP
|
427 | "Renewal" | Clients may use current certificate
and key
|
428 | | to authenticate an enrollment request
for a
|
429 | | new certificate
. |
430 | "SHA-512" | CA Supports the SHA
-512 hashing algorithm in
|
431 | | signatures
and fingerprints
. |
432 | "SHA-256" | CA Supports the SHA
-256 hashing algorithm in
|
433 | | signatures
and fingerprints
. |
434 | "SHA-1" | CA Supports the SHA
-1 hashing algorithm in
|
435 | | signatures
and fingerprints
. |
436 | "DES3" | CA Supports triple
-DES
for encryption
. |
437 +--------------------+----------------------------------------------+
440 bool scep_can_use_post
= false;
441 bool scep_use_3des
= false;
442 bool scep_can_use_sha1
= false;
444 CFArrayRef caps
= NULL
;
445 if (!scep_capabilities
) {
446 CFURLRef ca_caps_url
= scep_url_operation(scep_base_url
, CFSTR("GetCACaps"), scep_instance_name
);
447 require(ca_caps_url
, out
);
448 CFDataRef caps_data
= MCNetworkLoadRequest(ca_caps_url
, NULL
, NULL
, NULL
, validate_cert
);
449 CFRelease(ca_caps_url
);
451 CFStringRef caps_data_string
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, caps_data
, kCFStringEncodingASCII
);
452 require(caps_data_string
, out
);
453 caps
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, caps_data_string
, CFSTR("\n"));
455 fprintf(stderr
, "GetCACaps couldn't be parsed:\n");
458 CFRelease(caps_data
);
461 caps
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, scep_capabilities
, CFSTR(","));
465 fprintf(stderr
, "GetCACaps advertised following capabilities:\n");
468 CFRange caps_length
= CFRangeMake(0, CFArrayGetCount(caps
));
469 scep_can_use_post
= CFArrayContainsValue(caps
, caps_length
, CFSTR("POSTPKIOperation"));
470 scep_use_3des
= CFArrayContainsValue(caps
, caps_length
, CFSTR("DES3"));
471 scep_can_use_sha1
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-1"));
474 scep_use_3des
= true;
475 scep_can_use_sha1
= true;
477 csr_parameters
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
478 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
480 CFDictionarySetValue(csr_parameters
, kSecCertificateKeyUsage
, scep_key_usage
);
482 CFDictionarySetValue(csr_parameters
, kSecCSRChallengePassword
, scep_challenge
);
484 fprintf(stderr
, "No SCEP challenge provided, hope that's ok.\n");
486 if (!scep_use_3des
) {
487 CFDictionarySetValue(csr_parameters
, kSecCMSBulkEncryptionAlgorithm
, kSecCMSEncryptionAlgorithmDESCBC
);
488 fprintf(stderr
, "SCEP server does not support 3DES, falling back to DES. You should reconfigure your server.\n");
490 if (!scep_can_use_sha1
) {
491 CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmMD5
);
492 fprintf(stderr
, "SCEP server does not support SHA-1, falling back to MD5. You should reconfigure your server.\n");
495 if (scep_subject_alt_name
) {
496 fprintf(stderr
, "Adding subjectAltName to request\n");
497 CFStringRef name
= CFSTR("dnsName");
498 CFDictionaryRef subject_alt_name
= CFDictionaryCreate(kCFAllocatorDefault
,
499 (const void **)&name
, (const void **)&scep_subject_alt_name
,
500 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
501 CFDictionarySetValue(csr_parameters
, kSecSubjectAltName
, subject_alt_name
);
504 SecIdentityRef self_signed_identity
= SecSCEPCreateTemporaryIdentity(phone_publicKey
, phone_privateKey
);
506 // store encryption identity in the keychain because the decrypt function looks in there only
507 identity_add
= CFDictionaryCreate(NULL
,
508 &kSecValueRef
, (const void **)&self_signed_identity
, 1, NULL
, NULL
);
509 require_noerr(SecItemAdd(identity_add
, NULL
), out
);
511 require(scep_request
= SecSCEPGenerateCertificateRequest((CFArrayRef
)scep_subject
,
512 csr_parameters
, phone_publicKey
, phone_privateKey
, self_signed_identity
,
513 scep_certificates
), out
);
515 fprintf(stderr
, "storing scep_request.der\n");
516 write_data("scep_request.der", scep_request
);
518 scep_reply
= perform_pki_op(scep_base_url
, scep_request
, scep_can_use_post
, validate_cert
);
519 require(scep_reply
, out
);
521 require_action(CFDataGetLength(scep_reply
), out
, fprintf(stderr
, "Empty scep_reply, exiting.\n"));
522 fprintf(stderr
, "Storing scep_reply.der\n");
523 write_data("scep_reply.der", scep_reply
);
525 CFErrorRef server_error
= NULL
;
527 while ( !(issued_certs
= SecSCEPVerifyReply(scep_request
, scep_reply
, scep_certificates
, &server_error
)) &&
531 CFDataRef retry_get_cert_initial
= NULL
;
532 CFDictionaryRef error_dict
= CFErrorCopyUserInfo(server_error
);
533 retry_get_cert_initial
= SecSCEPGetCertInitial(ra_certificate
? ra_certificate
: ca_certificate
, scep_subject
, NULL
, error_dict
, self_signed_identity
, scep_certificates
);
534 if (scep_reply
) CFRelease(scep_reply
);
535 fprintf(stderr
, "Waiting 10 seconds before trying a GetCertInitial\n");
537 scep_reply
= perform_pki_op(scep_base_url
, retry_get_cert_initial
, scep_can_use_post
, validate_cert
);
540 require(issued_certs
, out
);
541 require_string(CFArrayGetCount(issued_certs
) > 0, out
, "No certificates issued.");
543 leaf
= (SecCertificateRef
)CFArrayGetValueAtIndex(issued_certs
, 0);
545 CFDataRef leaf_data
= SecCertificateCopyData(leaf
);
547 fprintf(stderr
, "Storing issued_cert.der\n");
548 write_data("issued_cert.der", leaf_data
);
549 CFRelease(leaf_data
);
553 candidate_identity
= SecIdentityCreate(kCFAllocatorDefault
, leaf
, phone_privateKey
);
555 const void *keys_ref_to_persist
[] = {
556 /*kSecReturnPersistentRef, */kSecValueRef
, kSecAttrLabel
};
557 const void *values_ref_to_persist
[] = {
558 /*kCFBooleanTrue, */candidate_identity
, scep_subject_name
};
559 CFDictionaryRef dict
= CFDictionaryCreate(NULL
,
560 (const void **)keys_ref_to_persist
,
561 (const void **)values_ref_to_persist
,
562 array_size(keys_ref_to_persist
),
563 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
564 OSStatus status
= SecItemAdd(dict
, NULL
);
565 require_noerr_action(status
, out
, fprintf(stderr
, "failed to store new identity, SecItemAdd: %" PRIdOSStatus
, status
));
569 SecItemDelete(identity_add
);
570 if (identity_add
) CFRelease(identity_add
);
571 //if (uuid_cfstr) CFRelease(uuid_cfstr);
572 if (candidate_identity
) CFRelease(candidate_identity
);
573 if (scep_request
) CFRelease(scep_request
);
574 if (scep_reply
) CFRelease(scep_reply
);
575 if (csr_parameters
) CFRelease(csr_parameters
);
581 #endif // TARGET_OS_MAC