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_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" 
  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     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             CFReleaseNull(scep_subject_name
); 
 298             scep_subject_name 
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
); 
 301             scep_subject_alt_name 
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
); 
 304             validate_cert 
= false; 
 307             scep_capabilities 
= CFStringCreateWithCString(kCFAllocatorDefault
, optarg
, kCFStringEncodingUTF8
); 
 310             return 2; /* Trigger usage message. */ 
 318         return 2; /* Trigger usage message. */ 
 320     CFDataRef scep_request 
= NULL
; 
 321     CFArrayRef issued_certs 
= NULL
; 
 322     SecCertificateRef leaf 
= NULL
; 
 323     SecIdentityRef candidate_identity 
= NULL
; 
 324     CFMutableDictionaryRef csr_parameters 
= NULL
; 
 325     CFDataRef scep_reply 
= NULL
; 
 326     SecKeyRef phone_publicKey 
= NULL
, phone_privateKey 
= NULL
; 
 327     CFStringRef scep_base_url 
= NULL
; 
 328     CFDictionaryRef identity_add 
= NULL
; 
 330     CFNumberRef scep_key_usage 
= NULL
; 
 331     CFNumberRef scep_key_bitsize 
= NULL
; 
 335     CFDataRef data 
= NULL
; 
 336     CFStringRef ctype 
= NULL
; 
 338     scep_base_url 
= CFStringCreateWithCString(kCFAllocatorDefault
, argv
[0], kCFStringEncodingASCII
); 
 341     CFStringRef uuid_cfstr 
= uuid_cfstring(); 
 342     require(uuid_cfstr
, out
); 
 343     const void * ca_cn
[] = { kSecOidCommonName
, uuid_cfstr 
}; 
 344     CFArrayRef ca_cn_dn 
= CFArrayCreate(kCFAllocatorDefault
, ca_cn
, 2, NULL
); 
 345     const void *ca_dn_array
[1]; 
 346     ca_dn_array
[0] = CFArrayCreate(kCFAllocatorDefault
, (const void **)&ca_cn_dn
, 1, NULL
); 
 347     CFArrayRef scep_subject 
= CFArrayCreate(kCFAllocatorDefault
, ca_dn_array
, array_size(ca_dn_array
), NULL
); 
 349     CFArrayRef scep_subject 
= make_scep_subject(scep_subject_name
); 
 350     require(scep_subject
, out
); 
 351     CFShow(scep_subject
); 
 354     scep_key_usage 
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_usage
); 
 355     scep_key_bitsize 
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &key_bitsize
); 
 357     const void *keygen_keys
[] = { kSecAttrKeyType
, kSecAttrKeySizeInBits 
}; 
 358     const void *keygen_vals
[] = { kSecAttrKeyTypeRSA
, scep_key_bitsize 
}; 
 359     CFDictionaryRef keygen_parameters 
= CFDictionaryCreate(kCFAllocatorDefault
,  
 360         keygen_keys
, keygen_vals
, array_size(keygen_vals
), 
 361         &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks 
); 
 362     CFRelease(scep_key_bitsize
); 
 363     require_noerr(SecKeyGeneratePair(keygen_parameters
, &phone_publicKey
, &phone_privateKey
), out
); 
 364     CFRelease(keygen_parameters
); 
 368         A binary X.509 CA certificate is sent back as a MIME object with a 
 369         Content-Type of application/x-x509-ca-cert. 
 371         When an RA exists, both CA and RA certificates must be sent back in 
 372         the response to the GetCACert request.  The RA certificate(s) must be 
 373         signed by the CA.  A certificates-only PKCS#7 [RFC2315] SignedData is 
 374         used to carry the certificates to the requester, with a Content-Type 
 375         of application/x-x509-ca-ra-cert. 
 377     SecCertificateRef ca_certificate 
= NULL
, ra_certificate 
= NULL
; 
 378     SecCertificateRef ra_encryption_certificate 
= NULL
; 
 379     CFArrayRef ra_certificates 
= NULL
; 
 380     CFTypeRef scep_certificates 
= NULL
; 
 381     SecCertificateRef scep_signing_certificate 
= NULL
; 
 383     url 
= scep_url_operation(scep_base_url
, CFSTR("GetCACert"), scep_instance_name
); 
 384     data 
= MCNetworkLoadRequest(url
, NULL
, NULL
, &ctype
, validate_cert
); 
 387         if (CFEqual(CFSTR("application/x-x509-ca-cert"), ctype
)) { 
 388             ca_certificate 
= SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)data
); 
 389             fprintf(stderr
, "GetCACert returned a single CA certificate.\n"); 
 390         } else if (CFEqual(ctype
, CFSTR("application/x-x509-ca-ra-cert"))) { 
 391             CFArrayRef cert_array 
= SecCMSCertificatesOnlyMessageCopyCertificates(data
); 
 393             require_noerr(SecSCEPValidateCACertMessage(cert_array
, 
 397                 &ra_encryption_certificate
), out
); 
 399             if (ra_certificate 
&& ra_encryption_certificate
) { 
 400                 const void *ra_certs
[] = { ra_encryption_certificate
, ra_certificate 
}; 
 401                 ra_certificates 
= CFArrayCreate(kCFAllocatorDefault
, ra_certs
, array_size(ra_certs
), &kCFTypeArrayCallBacks
); 
 402                 fprintf(stderr
, "GetCACert returned a separate signing and encryption certificates for RA.\n"); 
 404             CFRelease(cert_array
); 
 408     fprintf(stderr
, "CA certificate to issue cert:\n"); 
 409     CFShow(ca_certificate
); 
 411     if (ra_certificates
) { 
 412         scep_certificates 
= ra_certificates
; 
 413         scep_signing_certificate 
= ra_certificate
; 
 414     } else if (ra_certificate
) { 
 415         scep_certificates 
= ra_certificate
; 
 416         scep_signing_certificate 
= ra_certificate
; 
 417     } else if (ca_certificate
) {  
 418         scep_certificates 
= ca_certificate
; 
 419         scep_signing_certificate 
= ca_certificate
; 
 421         fprintf(stderr
, "Unsupported GetCACert configuration: please file a bug.\n"); 
 425     (void) scep_signing_certificate
; // Silence analyzer 
 428         GetCACaps capabilities advertised by SCEP server
: 
 430    +--------------------+----------------------------------------------+ 
 431    | Keyword            
| Description                                  
| 
 432    +--------------------+----------------------------------------------+ 
 433    | "GetNextCACert"    | CA Supports the GetNextCACert message
.       | 
 434    | "POSTPKIOperation" | PKIOPeration messages may be sent via HTTP   
| 
 436    | "Renewal"          | Clients may use current certificate 
and key  
| 
 437    |                    | to authenticate an enrollment request 
for a  
| 
 438    |                    | new certificate
.                             | 
 439    | "SHA-512"          | CA Supports the SHA
-512 hashing algorithm in 
| 
 440    |                    | signatures 
and fingerprints
.                 | 
 441    | "SHA-256"          | CA Supports the SHA
-256 hashing algorithm in 
| 
 442    |                    | signatures 
and fingerprints
.                 | 
 443    | "SHA-1"            | CA Supports the SHA
-1 hashing algorithm in   
| 
 444    |                    | signatures 
and fingerprints
.                 | 
 445    | "DES3"             | CA Supports triple
-DES 
for encryption
.       | 
 446    +--------------------+----------------------------------------------+ 
 449     bool scep_can_use_post 
= false; 
 450     bool scep_use_3des 
= false; 
 451     bool scep_can_use_sha1 
= false; 
 452     bool scep_can_use_sha512 
= false; 
 453     bool scep_can_use_sha256 
= false; 
 455     CFArrayRef caps 
= NULL
; 
 456     if (!scep_capabilities
) { 
 457         CFURLRef ca_caps_url 
= scep_url_operation(scep_base_url
, CFSTR("GetCACaps"), scep_instance_name
); 
 458         require(ca_caps_url
, out
); 
 459         CFDataRef caps_data 
= MCNetworkLoadRequest(ca_caps_url
, NULL
, NULL
, NULL
, validate_cert
); 
 460         CFRelease(ca_caps_url
); 
 462             CFStringRef caps_data_string 
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, caps_data
, kCFStringEncodingASCII
); 
 463             require(caps_data_string
, out
); 
 464             caps 
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, caps_data_string
, CFSTR("\n")); 
 466                 fprintf(stderr
, "GetCACaps couldn't be parsed:\n"); 
 469             CFRelease(caps_data
); 
 470             CFRelease(caps_data_string
); 
 473         caps 
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault
, scep_capabilities
, CFSTR(",")); 
 477         fprintf(stderr
, "GetCACaps advertised following capabilities:\n"); 
 480         CFRange caps_length 
= CFRangeMake(0, CFArrayGetCount(caps
)); 
 481         scep_can_use_post 
= CFArrayContainsValue(caps
, caps_length
, CFSTR("POSTPKIOperation")); 
 482         scep_use_3des 
= CFArrayContainsValue(caps
, caps_length
, CFSTR("DES3")); 
 483         scep_can_use_sha1 
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-1")); 
 484         scep_can_use_sha256 
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-256")); 
 485         scep_can_use_sha512 
= CFArrayContainsValue(caps
, caps_length
, CFSTR("SHA-512")); 
 487         // We probably inteded these to be the values and not override them below.. 
 488         // but for now to quiet the analyzer we reference them here. see <rdar://problem/15010402> scep.c, command_scep assumes 3des and sha1 
 489         (void) scep_use_3des
; 
 490         (void) scep_can_use_sha1
; 
 494     scep_use_3des 
= true; 
 495     scep_can_use_sha1 
= true; 
 497     csr_parameters 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,  
 498         &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 500         CFDictionarySetValue(csr_parameters
, kSecCertificateKeyUsage
, scep_key_usage
); 
 502         CFDictionarySetValue(csr_parameters
, kSecCSRChallengePassword
, scep_challenge
); 
 504         fprintf(stderr
, "No SCEP challenge provided, hope that's ok.\n"); 
 506     if (!scep_use_3des
) { 
 507         CFDictionarySetValue(csr_parameters
, kSecCMSBulkEncryptionAlgorithm
, kSecCMSEncryptionAlgorithmDESCBC
); 
 508         fprintf(stderr
, "SCEP server does not support 3DES, falling back to DES.  You should reconfigure your server.\n"); 
 511     if (scep_can_use_sha512
) { 
 512         CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA512
); 
 513     } else if (scep_can_use_sha256
) { 
 514         CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA256
); 
 515     } else if (scep_can_use_sha1
) { 
 516         CFDictionarySetValue(csr_parameters
, kSecCMSSignHashAlgorithm
, kSecCMSHashingAlgorithmSHA1
); 
 518         fprintf(stderr
, "SCEP server does not support SHA-1.  You must reconfigure your server.\n"); 
 521     if (scep_subject_alt_name
) { 
 522         fprintf(stderr
, "Adding subjectAltName to request\n"); 
 523         CFStringRef name 
= CFSTR("dnsName"); 
 524         CFDictionaryRef subject_alt_name 
= CFDictionaryCreate(kCFAllocatorDefault
, 
 525             (const void **)&name
, (const void **)&scep_subject_alt_name
, 
 526             1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 527         CFDictionarySetValue(csr_parameters
, kSecSubjectAltName
, subject_alt_name
); 
 530     SecIdentityRef self_signed_identity 
= SecSCEPCreateTemporaryIdentity(phone_publicKey
, phone_privateKey
); 
 532     // store encryption identity in the keychain because the decrypt function looks in there only 
 533     identity_add 
= CFDictionaryCreate(NULL
, 
 534         (const void **)&kSecValueRef
, (const void **)&self_signed_identity
, 1, NULL
, NULL
); 
 535         require_noerr(SecItemAdd(identity_add
, NULL
), out
); 
 537     require(scep_request 
= SecSCEPGenerateCertificateRequest((CFArrayRef
)scep_subject
,  
 538         csr_parameters
, phone_publicKey
, phone_privateKey
, self_signed_identity
, 
 539         scep_certificates
), out
); 
 541     fprintf(stderr
, "storing scep_request.der\n"); 
 542     write_data("scep_request.der", scep_request
); 
 544         scep_reply 
= perform_pki_op(scep_base_url
, scep_request
, scep_can_use_post
, validate_cert
); 
 545     require(scep_reply
, out
); 
 547     require_action(CFDataGetLength(scep_reply
), out
, fprintf(stderr
, "Empty scep_reply, exiting.\n")); 
 548     fprintf(stderr
, "Storing scep_reply.der\n"); 
 549     write_data("scep_reply.der", scep_reply
); 
 551         CFErrorRef server_error 
= NULL
; 
 553     while ( !(issued_certs 
= SecSCEPVerifyReply(scep_request
, scep_reply
, scep_certificates
, &server_error
)) && 
 557                 CFDataRef retry_get_cert_initial 
= NULL
; 
 558                 CFDictionaryRef error_dict 
= CFErrorCopyUserInfo(server_error
); 
 559                 retry_get_cert_initial 
= SecSCEPGetCertInitial(ra_certificate 
? ra_certificate 
: ca_certificate
, scep_subject
, NULL
, error_dict
, self_signed_identity
, scep_certificates
); 
 560                 CFReleaseNull(scep_reply
); 
 561         CFReleaseSafe(error_dict
); 
 562                 fprintf(stderr
, "Waiting 10 seconds before trying a GetCertInitial\n"); 
 564                 scep_reply 
= perform_pki_op(scep_base_url
, retry_get_cert_initial
, scep_can_use_post
, validate_cert
); 
 565         CFReleaseSafe(retry_get_cert_initial
); 
 568     require(issued_certs
, out
); 
 569         require_string(CFArrayGetCount(issued_certs
) > 0, out
, "No certificates issued."); 
 571     leaf 
= (SecCertificateRef
)CFArrayGetValueAtIndex(issued_certs
, 0); 
 573     CFDataRef leaf_data 
= SecCertificateCopyData(leaf
); 
 575         fprintf(stderr
, "Storing issued_cert.der\n"); 
 576         write_data("issued_cert.der", leaf_data
); 
 577         CFRelease(leaf_data
); 
 581     candidate_identity 
= SecIdentityCreate(kCFAllocatorDefault
, leaf
, phone_privateKey
); 
 583     const void *keys_ref_to_persist
[] = {  
 584         /*kSecReturnPersistentRef, */kSecValueRef
, kSecAttrLabel 
}; 
 585     const void *values_ref_to_persist
[] = {  
 586         /*kCFBooleanTrue, */candidate_identity
, scep_subject_name 
}; 
 587     CFDictionaryRef dict 
= CFDictionaryCreate(NULL
,  
 588             (const void **)keys_ref_to_persist
,  
 589             (const void **)values_ref_to_persist
,  
 590             array_size(keys_ref_to_persist
), 
 591             &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 592     OSStatus status 
= SecItemAdd(dict
, NULL
); 
 593     require_noerr_action(status
, out
, fprintf(stderr
, "failed to store new identity, SecItemAdd: %" PRIdOSStatus
, status
)); 
 598         SecItemDelete(identity_add
); 
 599     CFReleaseSafe(identity_add
); 
 600     //if (uuid_cfstr) CFRelease(uuid_cfstr); 
 601     CFReleaseSafe(candidate_identity
); 
 602     CFReleaseSafe(scep_request
); 
 603     CFReleaseSafe(scep_reply
); 
 604     CFReleaseSafe(scep_key_usage
); 
 605     CFReleaseSafe(scep_key_bitsize
); 
 606     CFReleaseSafe(csr_parameters
); 
 607     CFReleaseSafe(scep_subject_name
); 
 608     CFReleaseSafe(scep_base_url
); 
 610     CFReleaseSafe(issued_certs
); 
 612     CFReleaseSafe(ctype
); 
 618 #endif // TARGET_OS_MAC