1178825Sdfr/* 2178825Sdfr * Copyright (c) 2007 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "hx_locl.h" 35178825SdfrRCSID("$Id: ks_keychain.c 22084 2007-11-16 20:12:30Z lha $"); 36178825Sdfr 37178825Sdfr#ifdef HAVE_FRAMEWORK_SECURITY 38178825Sdfr 39178825Sdfr#include <Security/Security.h> 40178825Sdfr 41178825Sdfr/* Missing function decls in pre Leopard */ 42178825Sdfr#ifdef NEED_SECKEYGETCSPHANDLE_PROTO 43178825SdfrOSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *); 44178825SdfrOSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG, 45178825Sdfr int, const CSSM_ACCESS_CREDENTIALS **); 46178825Sdfr#define kSecCredentialTypeDefault 0 47178825Sdfr#endif 48178825Sdfr 49178825Sdfr 50178825Sdfrstatic int 51178825SdfrgetAttribute(SecKeychainItemRef itemRef, SecItemAttr item, 52178825Sdfr SecKeychainAttributeList **attrs) 53178825Sdfr{ 54178825Sdfr SecKeychainAttributeInfo attrInfo; 55178825Sdfr UInt32 attrFormat = 0; 56178825Sdfr OSStatus ret; 57178825Sdfr 58178825Sdfr *attrs = NULL; 59178825Sdfr 60178825Sdfr attrInfo.count = 1; 61178825Sdfr attrInfo.tag = &item; 62178825Sdfr attrInfo.format = &attrFormat; 63178825Sdfr 64178825Sdfr ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, 65178825Sdfr attrs, NULL, NULL); 66178825Sdfr if (ret) 67178825Sdfr return EINVAL; 68178825Sdfr return 0; 69178825Sdfr} 70178825Sdfr 71178825Sdfr 72178825Sdfr/* 73178825Sdfr * 74178825Sdfr */ 75178825Sdfr 76178825Sdfrstruct kc_rsa { 77178825Sdfr SecKeychainItemRef item; 78178825Sdfr size_t keysize; 79178825Sdfr}; 80178825Sdfr 81178825Sdfr 82178825Sdfrstatic int 83178825Sdfrkc_rsa_public_encrypt(int flen, 84178825Sdfr const unsigned char *from, 85178825Sdfr unsigned char *to, 86178825Sdfr RSA *rsa, 87178825Sdfr int padding) 88178825Sdfr{ 89178825Sdfr return -1; 90178825Sdfr} 91178825Sdfr 92178825Sdfrstatic int 93178825Sdfrkc_rsa_public_decrypt(int flen, 94178825Sdfr const unsigned char *from, 95178825Sdfr unsigned char *to, 96178825Sdfr RSA *rsa, 97178825Sdfr int padding) 98178825Sdfr{ 99178825Sdfr return -1; 100178825Sdfr} 101178825Sdfr 102178825Sdfr 103178825Sdfrstatic int 104178825Sdfrkc_rsa_private_encrypt(int flen, 105178825Sdfr const unsigned char *from, 106178825Sdfr unsigned char *to, 107178825Sdfr RSA *rsa, 108178825Sdfr int padding) 109178825Sdfr{ 110178825Sdfr struct kc_rsa *kc = RSA_get_app_data(rsa); 111178825Sdfr 112178825Sdfr CSSM_RETURN cret; 113178825Sdfr OSStatus ret; 114178825Sdfr const CSSM_ACCESS_CREDENTIALS *creds; 115178825Sdfr SecKeyRef privKeyRef = (SecKeyRef)kc->item; 116178825Sdfr CSSM_CSP_HANDLE cspHandle; 117178825Sdfr const CSSM_KEY *cssmKey; 118178825Sdfr CSSM_CC_HANDLE sigHandle = 0; 119178825Sdfr CSSM_DATA sig, in; 120178825Sdfr int fret = 0; 121178825Sdfr 122178825Sdfr 123178825Sdfr cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey); 124178825Sdfr if(cret) abort(); 125178825Sdfr 126178825Sdfr cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle); 127178825Sdfr if(cret) abort(); 128178825Sdfr 129178825Sdfr ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN, 130178825Sdfr kSecCredentialTypeDefault, &creds); 131178825Sdfr if(ret) abort(); 132178825Sdfr 133178825Sdfr ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA, 134178825Sdfr creds, cssmKey, &sigHandle); 135178825Sdfr if(ret) abort(); 136178825Sdfr 137178825Sdfr in.Data = (uint8 *)from; 138178825Sdfr in.Length = flen; 139178825Sdfr 140178825Sdfr sig.Data = (uint8 *)to; 141178825Sdfr sig.Length = kc->keysize; 142178825Sdfr 143178825Sdfr cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig); 144178825Sdfr if(cret) { 145178825Sdfr /* cssmErrorString(cret); */ 146178825Sdfr fret = -1; 147178825Sdfr } else 148178825Sdfr fret = sig.Length; 149178825Sdfr 150178825Sdfr if(sigHandle) 151178825Sdfr CSSM_DeleteContext(sigHandle); 152178825Sdfr 153178825Sdfr return fret; 154178825Sdfr} 155178825Sdfr 156178825Sdfrstatic int 157178825Sdfrkc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to, 158178825Sdfr RSA * rsa, int padding) 159178825Sdfr{ 160178825Sdfr return -1; 161178825Sdfr} 162178825Sdfr 163178825Sdfrstatic int 164178825Sdfrkc_rsa_init(RSA *rsa) 165178825Sdfr{ 166178825Sdfr return 1; 167178825Sdfr} 168178825Sdfr 169178825Sdfrstatic int 170178825Sdfrkc_rsa_finish(RSA *rsa) 171178825Sdfr{ 172178825Sdfr struct kc_rsa *kc_rsa = RSA_get_app_data(rsa); 173178825Sdfr CFRelease(kc_rsa->item); 174178825Sdfr memset(kc_rsa, 0, sizeof(*kc_rsa)); 175178825Sdfr free(kc_rsa); 176178825Sdfr return 1; 177178825Sdfr} 178178825Sdfr 179178825Sdfrstatic const RSA_METHOD kc_rsa_pkcs1_method = { 180178825Sdfr "hx509 Keychain PKCS#1 RSA", 181178825Sdfr kc_rsa_public_encrypt, 182178825Sdfr kc_rsa_public_decrypt, 183178825Sdfr kc_rsa_private_encrypt, 184178825Sdfr kc_rsa_private_decrypt, 185178825Sdfr NULL, 186178825Sdfr NULL, 187178825Sdfr kc_rsa_init, 188178825Sdfr kc_rsa_finish, 189178825Sdfr 0, 190178825Sdfr NULL, 191178825Sdfr NULL, 192178825Sdfr NULL 193178825Sdfr}; 194178825Sdfr 195178825Sdfrstatic int 196178825Sdfrset_private_key(hx509_context context, 197178825Sdfr SecKeychainItemRef itemRef, 198178825Sdfr hx509_cert cert) 199178825Sdfr{ 200178825Sdfr struct kc_rsa *kc; 201178825Sdfr hx509_private_key key; 202178825Sdfr RSA *rsa; 203178825Sdfr int ret; 204178825Sdfr 205178825Sdfr ret = _hx509_private_key_init(&key, NULL, NULL); 206178825Sdfr if (ret) 207178825Sdfr return ret; 208178825Sdfr 209178825Sdfr kc = calloc(1, sizeof(*kc)); 210178825Sdfr if (kc == NULL) 211178825Sdfr _hx509_abort("out of memory"); 212178825Sdfr 213178825Sdfr kc->item = itemRef; 214178825Sdfr 215178825Sdfr rsa = RSA_new(); 216178825Sdfr if (rsa == NULL) 217178825Sdfr _hx509_abort("out of memory"); 218178825Sdfr 219178825Sdfr /* Argh, fake modulus since OpenSSL API is on crack */ 220178825Sdfr { 221178825Sdfr SecKeychainAttributeList *attrs = NULL; 222178825Sdfr uint32_t size; 223178825Sdfr void *data; 224178825Sdfr 225178825Sdfr rsa->n = BN_new(); 226178825Sdfr if (rsa->n == NULL) abort(); 227178825Sdfr 228178825Sdfr ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs); 229178825Sdfr if (ret) abort(); 230178825Sdfr 231178825Sdfr size = *(uint32_t *)attrs->attr[0].data; 232178825Sdfr SecKeychainItemFreeAttributesAndData(attrs, NULL); 233178825Sdfr 234178825Sdfr kc->keysize = (size + 7) / 8; 235178825Sdfr 236178825Sdfr data = malloc(kc->keysize); 237178825Sdfr memset(data, 0xe0, kc->keysize); 238178825Sdfr BN_bin2bn(data, kc->keysize, rsa->n); 239178825Sdfr free(data); 240178825Sdfr } 241178825Sdfr rsa->e = NULL; 242178825Sdfr 243178825Sdfr RSA_set_method(rsa, &kc_rsa_pkcs1_method); 244178825Sdfr ret = RSA_set_app_data(rsa, kc); 245178825Sdfr if (ret != 1) 246178825Sdfr _hx509_abort("RSA_set_app_data"); 247178825Sdfr 248178825Sdfr _hx509_private_key_assign_rsa(key, rsa); 249178825Sdfr _hx509_cert_assign_key(cert, key); 250178825Sdfr 251178825Sdfr return 0; 252178825Sdfr} 253178825Sdfr 254178825Sdfr/* 255178825Sdfr * 256178825Sdfr */ 257178825Sdfr 258178825Sdfrstruct ks_keychain { 259178825Sdfr int anchors; 260178825Sdfr SecKeychainRef keychain; 261178825Sdfr}; 262178825Sdfr 263178825Sdfrstatic int 264178825Sdfrkeychain_init(hx509_context context, 265178825Sdfr hx509_certs certs, void **data, int flags, 266178825Sdfr const char *residue, hx509_lock lock) 267178825Sdfr{ 268178825Sdfr struct ks_keychain *ctx; 269178825Sdfr 270178825Sdfr ctx = calloc(1, sizeof(*ctx)); 271178825Sdfr if (ctx == NULL) { 272178825Sdfr hx509_clear_error_string(context); 273178825Sdfr return ENOMEM; 274178825Sdfr } 275178825Sdfr 276178825Sdfr if (residue) { 277178825Sdfr if (strcasecmp(residue, "system-anchors") == 0) { 278178825Sdfr ctx->anchors = 1; 279178825Sdfr } else if (strncasecmp(residue, "FILE:", 5) == 0) { 280178825Sdfr OSStatus ret; 281178825Sdfr 282178825Sdfr ret = SecKeychainOpen(residue + 5, &ctx->keychain); 283178825Sdfr if (ret != noErr) { 284178825Sdfr hx509_set_error_string(context, 0, ENOENT, 285178825Sdfr "Failed to open %s", residue); 286178825Sdfr return ENOENT; 287178825Sdfr } 288178825Sdfr } else { 289178825Sdfr hx509_set_error_string(context, 0, ENOENT, 290178825Sdfr "Unknown subtype %s", residue); 291178825Sdfr return ENOENT; 292178825Sdfr } 293178825Sdfr } 294178825Sdfr 295178825Sdfr *data = ctx; 296178825Sdfr return 0; 297178825Sdfr} 298178825Sdfr 299178825Sdfr/* 300178825Sdfr * 301178825Sdfr */ 302178825Sdfr 303178825Sdfrstatic int 304178825Sdfrkeychain_free(hx509_certs certs, void *data) 305178825Sdfr{ 306178825Sdfr struct ks_keychain *ctx = data; 307178825Sdfr if (ctx->keychain) 308178825Sdfr CFRelease(ctx->keychain); 309178825Sdfr memset(ctx, 0, sizeof(*ctx)); 310178825Sdfr free(ctx); 311178825Sdfr return 0; 312178825Sdfr} 313178825Sdfr 314178825Sdfr/* 315178825Sdfr * 316178825Sdfr */ 317178825Sdfr 318178825Sdfrstruct iter { 319178825Sdfr hx509_certs certs; 320178825Sdfr void *cursor; 321178825Sdfr SecKeychainSearchRef searchRef; 322178825Sdfr}; 323178825Sdfr 324178825Sdfrstatic int 325178825Sdfrkeychain_iter_start(hx509_context context, 326178825Sdfr hx509_certs certs, void *data, void **cursor) 327178825Sdfr{ 328178825Sdfr struct ks_keychain *ctx = data; 329178825Sdfr struct iter *iter; 330178825Sdfr 331178825Sdfr iter = calloc(1, sizeof(*iter)); 332178825Sdfr if (iter == NULL) { 333178825Sdfr hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 334178825Sdfr return ENOMEM; 335178825Sdfr } 336178825Sdfr 337178825Sdfr if (ctx->anchors) { 338178825Sdfr CFArrayRef anchors; 339178825Sdfr int ret; 340178825Sdfr int i; 341178825Sdfr 342178825Sdfr ret = hx509_certs_init(context, "MEMORY:ks-file-create", 343178825Sdfr 0, NULL, &iter->certs); 344178825Sdfr if (ret) { 345178825Sdfr free(iter); 346178825Sdfr return ret; 347178825Sdfr } 348178825Sdfr 349178825Sdfr ret = SecTrustCopyAnchorCertificates(&anchors); 350178825Sdfr if (ret != 0) { 351178825Sdfr hx509_certs_free(&iter->certs); 352178825Sdfr free(iter); 353178825Sdfr hx509_set_error_string(context, 0, ENOMEM, 354178825Sdfr "Can't get trust anchors from Keychain"); 355178825Sdfr return ENOMEM; 356178825Sdfr } 357178825Sdfr for (i = 0; i < CFArrayGetCount(anchors); i++) { 358178825Sdfr SecCertificateRef cr; 359178825Sdfr hx509_cert cert; 360178825Sdfr CSSM_DATA cssm; 361178825Sdfr 362178825Sdfr cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); 363178825Sdfr 364178825Sdfr SecCertificateGetData(cr, &cssm); 365178825Sdfr 366178825Sdfr ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert); 367178825Sdfr if (ret) 368178825Sdfr continue; 369178825Sdfr 370178825Sdfr ret = hx509_certs_add(context, iter->certs, cert); 371178825Sdfr hx509_cert_free(cert); 372178825Sdfr } 373178825Sdfr CFRelease(anchors); 374178825Sdfr } 375178825Sdfr 376178825Sdfr if (iter->certs) { 377178825Sdfr int ret; 378178825Sdfr ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); 379178825Sdfr if (ret) { 380178825Sdfr hx509_certs_free(&iter->certs); 381178825Sdfr free(iter); 382178825Sdfr return ret; 383178825Sdfr } 384178825Sdfr } else { 385178825Sdfr OSStatus ret; 386178825Sdfr 387178825Sdfr ret = SecKeychainSearchCreateFromAttributes(ctx->keychain, 388178825Sdfr kSecCertificateItemClass, 389178825Sdfr NULL, 390178825Sdfr &iter->searchRef); 391178825Sdfr if (ret) { 392178825Sdfr free(iter); 393178825Sdfr hx509_set_error_string(context, 0, ret, 394178825Sdfr "Failed to start search for attributes"); 395178825Sdfr return ENOMEM; 396178825Sdfr } 397178825Sdfr } 398178825Sdfr 399178825Sdfr *cursor = iter; 400178825Sdfr return 0; 401178825Sdfr} 402178825Sdfr 403178825Sdfr/* 404178825Sdfr * 405178825Sdfr */ 406178825Sdfr 407178825Sdfrstatic int 408178825Sdfrkeychain_iter(hx509_context context, 409178825Sdfr hx509_certs certs, void *data, void *cursor, hx509_cert *cert) 410178825Sdfr{ 411178825Sdfr SecKeychainAttributeList *attrs = NULL; 412178825Sdfr SecKeychainAttributeInfo attrInfo; 413178825Sdfr UInt32 attrFormat[1] = { 0 }; 414178825Sdfr SecKeychainItemRef itemRef; 415178825Sdfr SecItemAttr item[1]; 416178825Sdfr struct iter *iter = cursor; 417178825Sdfr OSStatus ret; 418178825Sdfr UInt32 len; 419178825Sdfr void *ptr = NULL; 420178825Sdfr 421178825Sdfr if (iter->certs) 422178825Sdfr return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert); 423178825Sdfr 424178825Sdfr *cert = NULL; 425178825Sdfr 426178825Sdfr ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef); 427178825Sdfr if (ret == errSecItemNotFound) 428178825Sdfr return 0; 429178825Sdfr else if (ret != 0) 430178825Sdfr return EINVAL; 431178825Sdfr 432178825Sdfr /* 433178825Sdfr * Pick out certificate and matching "keyid" 434178825Sdfr */ 435178825Sdfr 436178825Sdfr item[0] = kSecPublicKeyHashItemAttr; 437178825Sdfr 438178825Sdfr attrInfo.count = 1; 439178825Sdfr attrInfo.tag = item; 440178825Sdfr attrInfo.format = attrFormat; 441178825Sdfr 442178825Sdfr ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, 443178825Sdfr &attrs, &len, &ptr); 444178825Sdfr if (ret) 445178825Sdfr return EINVAL; 446178825Sdfr 447178825Sdfr ret = hx509_cert_init_data(context, ptr, len, cert); 448178825Sdfr if (ret) 449178825Sdfr goto out; 450178825Sdfr 451178825Sdfr /* 452178825Sdfr * Find related private key if there is one by looking at 453178825Sdfr * kSecPublicKeyHashItemAttr == kSecKeyLabel 454178825Sdfr */ 455178825Sdfr { 456178825Sdfr SecKeychainSearchRef search; 457178825Sdfr SecKeychainAttribute attrKeyid; 458178825Sdfr SecKeychainAttributeList attrList; 459178825Sdfr 460178825Sdfr attrKeyid.tag = kSecKeyLabel; 461178825Sdfr attrKeyid.length = attrs->attr[0].length; 462178825Sdfr attrKeyid.data = attrs->attr[0].data; 463178825Sdfr 464178825Sdfr attrList.count = 1; 465178825Sdfr attrList.attr = &attrKeyid; 466178825Sdfr 467178825Sdfr ret = SecKeychainSearchCreateFromAttributes(NULL, 468178825Sdfr CSSM_DL_DB_RECORD_PRIVATE_KEY, 469178825Sdfr &attrList, 470178825Sdfr &search); 471178825Sdfr if (ret) { 472178825Sdfr ret = 0; 473178825Sdfr goto out; 474178825Sdfr } 475178825Sdfr 476178825Sdfr ret = SecKeychainSearchCopyNext(search, &itemRef); 477178825Sdfr CFRelease(search); 478178825Sdfr if (ret == errSecItemNotFound) { 479178825Sdfr ret = 0; 480178825Sdfr goto out; 481178825Sdfr } else if (ret) { 482178825Sdfr ret = EINVAL; 483178825Sdfr goto out; 484178825Sdfr } 485178825Sdfr set_private_key(context, itemRef, *cert); 486178825Sdfr } 487178825Sdfr 488178825Sdfrout: 489178825Sdfr SecKeychainItemFreeAttributesAndData(attrs, ptr); 490178825Sdfr 491178825Sdfr return ret; 492178825Sdfr} 493178825Sdfr 494178825Sdfr/* 495178825Sdfr * 496178825Sdfr */ 497178825Sdfr 498178825Sdfrstatic int 499178825Sdfrkeychain_iter_end(hx509_context context, 500178825Sdfr hx509_certs certs, 501178825Sdfr void *data, 502178825Sdfr void *cursor) 503178825Sdfr{ 504178825Sdfr struct iter *iter = cursor; 505178825Sdfr 506178825Sdfr if (iter->certs) { 507178825Sdfr int ret; 508178825Sdfr ret = hx509_certs_end_seq(context, iter->certs, iter->cursor); 509178825Sdfr hx509_certs_free(&iter->certs); 510178825Sdfr } else { 511178825Sdfr CFRelease(iter->searchRef); 512178825Sdfr } 513178825Sdfr 514178825Sdfr memset(iter, 0, sizeof(*iter)); 515178825Sdfr free(iter); 516178825Sdfr return 0; 517178825Sdfr} 518178825Sdfr 519178825Sdfr/* 520178825Sdfr * 521178825Sdfr */ 522178825Sdfr 523178825Sdfrstruct hx509_keyset_ops keyset_keychain = { 524178825Sdfr "KEYCHAIN", 525178825Sdfr 0, 526178825Sdfr keychain_init, 527178825Sdfr NULL, 528178825Sdfr keychain_free, 529178825Sdfr NULL, 530178825Sdfr NULL, 531178825Sdfr keychain_iter_start, 532178825Sdfr keychain_iter, 533178825Sdfr keychain_iter_end 534178825Sdfr}; 535178825Sdfr 536178825Sdfr#endif /* HAVE_FRAMEWORK_SECURITY */ 537178825Sdfr 538178825Sdfr/* 539178825Sdfr * 540178825Sdfr */ 541178825Sdfr 542178825Sdfrvoid 543178825Sdfr_hx509_ks_keychain_register(hx509_context context) 544178825Sdfr{ 545178825Sdfr#ifdef HAVE_FRAMEWORK_SECURITY 546178825Sdfr _hx509_ks_register(context, &keyset_keychain); 547178825Sdfr#endif 548178825Sdfr} 549