2 * Copyright (c) 2003-2004,2006-2007,2009-2010,2013-2014 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_IPHONE && !TARGET_OS_SIMULATOR
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"
49 #include <utilities/SecCFRelease.h>
53 static inline void write_data(const char * path
, CFDataRef data
)
55 int data_file
= open(path
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0644);
56 write(data_file
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
62 static CF_RETURNS_RETAINED CFHTTPMessageRef
load_request(CFHTTPMessageRef request
, CFMutableDataRef data
, int retry
, bool validate_cert
)
64 CFHTTPMessageRef result
= NULL
;
70 rs
= CFReadStreamCreateForHTTPRequest(NULL
, request
);
73 const void *keys
[] = {
74 kCFStreamSSLValidatesCertificateChain
,
76 const void *values
[] = {
79 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
,
81 &kCFTypeDictionaryKeyCallBacks
,
82 &kCFTypeDictionaryValueCallBacks
);
83 CFReadStreamSetProperty(rs
, kCFStreamPropertySSLSettings
, dict
);
85 CFReadStreamSetProperty(rs
, kCFStreamPropertyHTTPAttemptPersistentConnection
, kCFBooleanTrue
);
88 if (CFReadStreamOpen(rs
)) {
91 CFIndex bytesRead
= CFReadStreamRead(rs
, buf
, BUFSIZE
);
93 CFDataAppendBytes(data
, buf
, bytesRead
);
94 } else if (bytesRead
== 0) {
95 result
= (CFHTTPMessageRef
)CFReadStreamCopyProperty(rs
, kCFStreamPropertyHTTPResponseHeader
);
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
);
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
);
112 CFReadStreamClose(rs
);
117 static CF_RETURNS_RETAINED CFDataRef
MCNetworkLoadRequest(CFURLRef url
, CFDataRef content
, CFStringRef type
,
118 CFStringRef
*contentType
, bool validate_cert
)
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
);
126 CFHTTPMessageSetBody(request
, content
);
127 CFHTTPMessageSetHeaderFieldValue(request
, CFSTR("Content-Type"), type
);
131 response
= load_request(request
, out_data
, 1, validate_cert
);
132 if (!response
&& retries
) {
134 CFDataSetLength(out_data
, 0);
136 } while (!response
&& retries
--);
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");
146 CFReleaseNull(out_data
);
151 *contentType
= CFHTTPMessageCopyHeaderFieldValue(response
, CFSTR("Content-Type"));
153 CFReleaseSafe(response
);
157 static void _query_string_apply(CFMutableStringRef query_string
, const void *key
, const void *value
)
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
);
166 if (CFStringGetLength(query_string
) > 1)
167 CFStringAppend(query_string
, CFSTR("&"));
169 CFStringAppendFormat(query_string
, NULL
, CFSTR("%@=%@"), escaped_key
, escaped_value
);
170 CFRelease(escaped_key
);
171 CFRelease(escaped_value
);
174 static CF_RETURNS_RETAINED 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 CF_RETURNS_RETAINED 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 CFReleaseSafe(pki_op
);
223 static inline CF_RETURNS_RETAINED 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 SecCertificateRef ca_certificate
= NULL
, ra_certificate
= NULL
;
278 CFArrayRef cert_array
= NULL
;
279 CFDataRef caps_data
= NULL
;
281 while ((ch
= getopt(argc
, argv
, "vu:b:c:n:s:h:xo:")) != -1)
289 key_usage
= atoi(optarg
);
292 key_bitsize
= atoi(optarg
);
295 CFReleaseNull(scep_challenge
);
296 scep_challenge
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
299 CFReleaseNull(scep_instance_name
);
300 scep_instance_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
303 CFReleaseNull(scep_subject_name
);
304 scep_subject_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
307 CFReleaseNull(scep_subject_alt_name
);
308 scep_subject_alt_name
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
311 validate_cert
= false;
314 CFReleaseNull(scep_capabilities
);
315 scep_capabilities
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
);
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
;
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
;
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
;
349 CFNumberRef scep_key_usage
= NULL
;
350 CFNumberRef scep_key_bitsize
= NULL
;
354 CFDataRef data
= NULL
;
355 CFStringRef ctype
= NULL
;
357 scep_base_url
= CFStringCreateWithCString(kCFAllocatorDefault
, argv
[0], kCFStringEncodingASCII
);
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
);
368 CFArrayRef scep_subject
= make_scep_subject(scep_subject_name
);
369 require(scep_subject
, out
);
370 CFShow(scep_subject
);
373 scep_key_usage
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_usage
);
374 scep_key_bitsize
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_bitsize
);
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
);
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.
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.
396 SecCertificateRef ra_encryption_certificate
= NULL
;
397 CFArrayRef ra_certificates
= NULL
;
398 CFTypeRef scep_certificates
= NULL
;
399 SecCertificateRef scep_signing_certificate
= NULL
;
401 url
= scep_url_operation(scep_base_url
, CFSTR("GetCACert"), scep_instance_name
);
402 data
= MCNetworkLoadRequest(url
, NULL
, NULL
, &ctype
, validate_cert
);
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
);
411 require_noerr(SecSCEPValidateCACertMessage(cert_array
,
415 &ra_encryption_certificate
), out
);
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");
425 fprintf(stderr
, "CA certificate to issue cert:\n");
426 CFShow(ca_certificate
);
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
;
438 fprintf(stderr
, "Unsupported GetCACert configuration: please file a bug.\n");
442 (void) scep_signing_certificate
; // Silence analyzer
445 GetCACaps capabilities advertised by SCEP server
:
447 +--------------------+----------------------------------------------+
448 | Keyword
| Description
|
449 +--------------------+----------------------------------------------+
450 | "GetNextCACert" | CA Supports the GetNextCACert message
. |
451 | "POSTPKIOperation" | PKIOPeration messages may be sent via HTTP
|
453 | "Renewal" | Clients may use current certificate
and key
|
454 | | to authenticate an enrollment request
for a
|
455 | | new certificate
. |
456 | "SHA-512" | CA Supports the SHA
-512 hashing algorithm in
|
457 | | signatures
and fingerprints
. |
458 | "SHA-256" | CA Supports the SHA
-256 hashing algorithm in
|
459 | | signatures
and fingerprints
. |
460 | "SHA-1" | CA Supports the SHA
-1 hashing algorithm in
|
461 | | signatures
and fingerprints
. |
462 | "DES3" | CA Supports triple
-DES
for encryption
. |
463 +--------------------+----------------------------------------------+
466 bool scep_can_use_post
= false;
467 bool scep_use_3des
= false;
468 bool scep_can_use_sha1
= false;
469 bool scep_can_use_sha512
= false;
470 bool scep_can_use_sha256
= false;
472 CFArrayRef caps
= NULL
;
473 if (!scep_capabilities
) {
474 CFURLRef ca_caps_url
= scep_url_operation(scep_base_url
, CFSTR("GetCACaps"), scep_instance_name
);
475 require(ca_caps_url
, out
);
476 caps_data
= MCNetworkLoadRequest(ca_caps_url
, NULL
, NULL
, NULL
, validate_cert
);
477 CFRelease(ca_caps_url
);
479 CFStringRef caps_data_string
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, caps_data
, kCFStringEncodingASCII
);
480 require(caps_data_string
, out
);
481 caps
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, caps_data_string
, CFSTR("\n"));
483 fprintf(stderr
, "GetCACaps couldn't be parsed:\n");
486 CFRelease(caps_data
);
487 CFRelease(caps_data_string
);
490 caps
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, scep_capabilities
, CFSTR(","));
494 fprintf(stderr
, "GetCACaps advertised following capabilities:\n");
497 CFRange caps_length
= CFRangeMake(0, CFArrayGetCount(caps
));
498 scep_can_use_post
= CFArrayContainsValue(caps
, caps_length
, CFSTR("POSTPKIOperation"));
499 scep_use_3des
= CFArrayContainsValue(caps
, caps_length
, CFSTR("DES3"));
500 scep_can_use_sha1
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-1"));
501 scep_can_use_sha256
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-256"));
502 scep_can_use_sha512
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-512"));
504 // We probably inteded these to be the values and not override them below..
505 // but for now to quiet the analyzer we reference them here. see <rdar://problem/15010402> scep.c, command_scep assumes 3des and sha1
506 (void) scep_use_3des
;
507 (void) scep_can_use_sha1
;
511 scep_use_3des
= true;
512 scep_can_use_sha1
= true;
514 csr_parameters
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
515 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
517 CFDictionarySetValue(csr_parameters
, kSecCertificateKeyUsage
, scep_key_usage
);
519 CFDictionarySetValue(csr_parameters
, kSecCSRChallengePassword
, scep_challenge
);
521 fprintf(stderr
, "No SCEP challenge provided, hope that's ok.\n");
523 if (!scep_use_3des
) {
524 CFDictionarySetValue(csr_parameters
, kSecCMSBulkEncryptionAlgorithm
, kSecCMSEncryptionAlgorithmDESCBC
);
525 fprintf(stderr
, "SCEP server does not support 3DES, falling back to DES. You should reconfigure your server.\n");
528 if (scep_can_use_sha512
) {
529 CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA512
);
530 } else if (scep_can_use_sha256
) {
531 CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA256
);
532 } else if (scep_can_use_sha1
) {
533 CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA1
);
535 fprintf(stderr
, "SCEP server does not support SHA-1. You must reconfigure your server.\n");
538 if (scep_subject_alt_name
) {
539 fprintf(stderr
, "Adding subjectAltName to request\n");
540 CFDictionaryRef subject_alt_name
= CFDictionaryCreate(kCFAllocatorDefault
,
541 (const void **)&kSecSubjectAltNameDNSName
, (const void **)&scep_subject_alt_name
,
542 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
543 CFDictionarySetValue(csr_parameters
, kSecSubjectAltName
, subject_alt_name
);
546 SecIdentityRef self_signed_identity
= SecSCEPCreateTemporaryIdentity(phone_publicKey
, phone_privateKey
);
548 // store encryption identity in the keychain because the decrypt function looks in there only
549 identity_add
= CFDictionaryCreate(NULL
,
550 (const void **)&kSecValueRef
, (const void **)&self_signed_identity
, 1, NULL
, NULL
);
551 require_noerr(SecItemAdd(identity_add
, NULL
), out
);
553 require(scep_request
= SecSCEPGenerateCertificateRequest((CFArrayRef
)scep_subject
,
554 csr_parameters
, phone_publicKey
, phone_privateKey
, self_signed_identity
,
555 scep_certificates
), out
);
557 fprintf(stderr
, "storing scep_request.der\n");
558 write_data("scep_request.der", scep_request
);
560 scep_reply
= perform_pki_op(scep_base_url
, scep_request
, scep_can_use_post
, validate_cert
);
561 require(scep_reply
, out
);
563 require_action(CFDataGetLength(scep_reply
), out
, fprintf(stderr
, "Empty scep_reply, exiting.\n"));
564 fprintf(stderr
, "Storing scep_reply.der\n");
565 write_data("scep_reply.der", scep_reply
);
567 CFErrorRef server_error
= NULL
;
569 while ( !(issued_certs
= SecSCEPVerifyReply(scep_request
, scep_reply
, scep_certificates
, &server_error
)) &&
573 CFDataRef retry_get_cert_initial
= NULL
;
574 CFDictionaryRef error_dict
= CFErrorCopyUserInfo(server_error
);
575 retry_get_cert_initial
= SecSCEPGetCertInitial(ra_certificate
? ra_certificate
: ca_certificate
, scep_subject
, NULL
, error_dict
, self_signed_identity
, scep_certificates
);
576 CFReleaseNull(scep_reply
);
577 CFReleaseSafe(error_dict
);
578 fprintf(stderr
, "Waiting 10 seconds before trying a GetCertInitial\n");
580 scep_reply
= perform_pki_op(scep_base_url
, retry_get_cert_initial
, scep_can_use_post
, validate_cert
);
581 CFReleaseSafe(retry_get_cert_initial
);
584 require(issued_certs
, out
);
585 require_string(CFArrayGetCount(issued_certs
) > 0, out
, "No certificates issued.");
587 leaf
= (SecCertificateRef
)CFArrayGetValueAtIndex(issued_certs
, 0);
589 CFDataRef leaf_data
= SecCertificateCopyData(leaf
);
591 fprintf(stderr
, "Storing issued_cert.der\n");
592 write_data("issued_cert.der", leaf_data
);
593 CFRelease(leaf_data
);
597 candidate_identity
= SecIdentityCreate(kCFAllocatorDefault
, leaf
, phone_privateKey
);
599 const void *keys_ref_to_persist
[] = {
600 /*kSecReturnPersistentRef, */kSecValueRef
, kSecAttrLabel
};
601 const void *values_ref_to_persist
[] = {
602 /*kCFBooleanTrue, */candidate_identity
, scep_subject_name
};
603 CFDictionaryRef dict
= CFDictionaryCreate(NULL
,
604 (const void **)keys_ref_to_persist
,
605 (const void **)values_ref_to_persist
,
606 array_size(keys_ref_to_persist
),
607 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
608 OSStatus status
= SecItemAdd(dict
, NULL
);
609 require_noerr_action(status
, out
, fprintf(stderr
, "failed to store new identity, SecItemAdd: %" PRIdOSStatus
, status
));
614 SecItemDelete(identity_add
);
615 CFReleaseSafe(identity_add
);
616 //if (uuid_cfstr) CFRelease(uuid_cfstr);
617 CFReleaseSafe(candidate_identity
);
618 CFReleaseSafe(scep_request
);
619 CFReleaseSafe(scep_reply
);
620 CFReleaseSafe(scep_key_usage
);
621 CFReleaseSafe(scep_key_bitsize
);
622 CFReleaseSafe(csr_parameters
);
623 CFReleaseSafe(scep_subject_name
);
624 CFReleaseSafe(scep_base_url
);
626 CFReleaseSafe(issued_certs
);
628 CFReleaseSafe(ctype
);
629 CFReleaseNull(ca_certificate
);
630 CFReleaseNull(scep_capabilities
);
631 CFReleaseNull(scep_challenge
);
632 CFReleaseNull(scep_instance_name
);
633 CFReleaseNull(scep_subject_alt_name
);
634 CFReleaseNull(cert_array
);
635 CFReleaseNull(caps_data
);
641 #endif // TARGET_OS_MAC