1/* 2 * Copyright (c) 2007-2010 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 24#include <Security/SecBase.h> 25#include <Security/SecBasePriv.h> 26#include <Security/SecItem.h> 27//#include <Security/SecRSAKey.h> 28#include <Security/SecCertificate.h> 29#include <Security/SecIdentity.h> 30#include <Security/SecIdentityPriv.h> 31#include <Security/SecPolicy.h> 32#include <Security/SecTrust.h> 33#include <Security/SecKeyPriv.h> 34#include "SecInternal.h" 35 36//#include <AssertMacros.h> 37#include <CommonCrypto/CommonDigest.h> 38 39//#include "p12import.h" 40#include "SecImportExport.h" 41 42CFStringRef kSecImportExportPassphrase = CFSTR("passphrase"); 43CFStringRef kSecImportExportKeychain = CFSTR("keychain"); 44CFStringRef kSecImportExportAccess = CFSTR("access"); 45 46CFStringRef kSecImportItemLabel = CFSTR("label"); 47CFStringRef kSecImportItemKeyID = CFSTR("keyid"); 48CFStringRef kSecImportItemTrust = CFSTR("trust"); 49CFStringRef kSecImportItemCertChain = CFSTR("chain"); 50CFStringRef kSecImportItemIdentity = CFSTR("identity"); 51 52#if 0 53static void collect_certs(const void *key, const void *value, void *context) 54{ 55 if (!CFDictionaryContainsKey(value, CFSTR("key"))) { 56 CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert")); 57 if (!cert_bytes) 58 return; 59 SecCertificateRef cert = 60 SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes); 61 if (!cert) 62 return; 63 CFMutableArrayRef cert_array = (CFMutableArrayRef)context; 64 CFArrayAppendValue(cert_array, cert); 65 CFRelease(cert); 66 } 67} 68 69typedef struct { 70 CFMutableArrayRef identities; 71 CFArrayRef certs; 72} build_trust_chains_context; 73 74static void build_trust_chains(const void *key, const void *value, 75 void *context) 76{ 77 CFMutableDictionaryRef identity_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 78 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 79 SecKeyRef private_key = NULL; 80 SecCertificateRef cert = NULL; 81 SecIdentityRef identity = NULL; 82 SecPolicyRef policy = NULL; 83 CFMutableArrayRef cert_chain = NULL, eval_chain = NULL; 84 SecTrustRef trust = NULL; 85 build_trust_chains_context * a_build_trust_chains_context = (build_trust_chains_context*)context; 86 87 CFDataRef key_bytes = CFDictionaryGetValue(value, CFSTR("key")); 88 if(!key_bytes) goto out; //require(key_bytes, out); 89 CFDataRef cert_bytes = CFDictionaryGetValue(value, CFSTR("cert")); 90 if(!cert_bytes) goto out; //require(cert_bytes, out); 91 92 /* p12import only passes up rsa keys */ 93//FIXME: needs SecKeyCreateRSAPrivateKey implementation 94//#if 0 95// private_key = SecKeyCreateRSAPrivateKey(kCFAllocatorDefault, 96// CFDataGetBytePtr(key_bytes), CFDataGetLength(key_bytes), 97// kSecKeyEncodingPkcs1); 98//#endif 99 if(!private_key) goto out; //require(private_key, out); 100 cert = SecCertificateCreateWithData(kCFAllocatorDefault, cert_bytes); 101 if(!cert) goto out; //require(cert, out); 102 identity = SecIdentityCreate(kCFAllocatorDefault, cert, private_key); 103 if(!identity) goto out; //require(identity, out); 104 CFDictionarySetValue(identity_dict, kSecImportItemIdentity, identity); 105 106 eval_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 107 if(!eval_chain) goto out; //require(eval_chain, out); 108 CFArrayAppendValue(eval_chain, cert); 109 CFRange all_certs = { 0, CFArrayGetCount(a_build_trust_chains_context->certs) }; 110 CFArrayAppendArray(eval_chain, a_build_trust_chains_context->certs, all_certs); 111 policy = SecPolicyCreateBasicX509(); 112 if(!policy) goto out; //require(policy, out); 113 SecTrustResultType result; 114 SecTrustCreateWithCertificates(eval_chain, policy, &trust); 115 if(!trust) goto out; //require(trust, out); 116 SecTrustEvaluate(trust, &result); 117 CFDictionarySetValue(identity_dict, kSecImportItemTrust, trust); 118 119 cert_chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 120 if(!cert_chain) goto out; //require(cert_chain, out); 121 CFIndex cert_chain_length = SecTrustGetCertificateCount(trust); 122 int i; 123 for (i = 0; i < cert_chain_length; i++) 124 CFArrayAppendValue(cert_chain, SecTrustGetCertificateAtIndex(trust, i)); 125 CFDictionarySetValue(identity_dict, kSecImportItemCertChain, cert_chain); 126 127 CFArrayAppendValue(a_build_trust_chains_context->identities, identity_dict); 128out: 129 CFReleaseSafe(identity_dict); 130 CFReleaseSafe(identity); 131 CFReleaseSafe(private_key); 132 CFReleaseSafe(cert); 133 CFReleaseSafe(policy); 134 CFReleaseSafe(cert_chain); 135 CFReleaseSafe(eval_chain); 136 CFReleaseSafe(trust); 137} 138#endif // if 0 139 140OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options, CFArrayRef *items) 141{ 142 // SecPKCS12Import is implemented on Mac OS X in terms of the existing 143 // SecKeychainItemImport API, which supports importing items into a 144 // specified keychain with initial access control settings for keys. 145 // 146 OSStatus status = errSecSuccess; 147 SecExternalFormat inputFormat = kSecFormatPKCS12; 148 SecExternalItemType itemType = kSecItemTypeAggregate; 149 SecItemImportExportFlags flags = 0; /* don't know if it's PEM armoured */ 150 SecKeyImportExportParameters keyParams; /* filled in below... */ 151 SecKeychainRef importKeychain = NULL; 152 SecAccessRef importAccess = NULL; 153 CFStringRef importPassword = NULL; 154 CFArrayRef tmpItems = NULL; /* items returned by SecKeychainItemImport */ 155 CFMutableArrayRef certs = NULL; /* certificates imported by this function */ 156 CFMutableArrayRef identities = NULL; /* items returned by this function */ 157 158 if (options) { 159 importKeychain = (SecKeychainRef) CFDictionaryGetValue(options, kSecImportExportKeychain); 160 if (importKeychain) 161 CFRetain(importKeychain); 162 importAccess = (SecAccessRef) CFDictionaryGetValue(options, kSecImportExportAccess); 163 if (importAccess) 164 CFRetain(importAccess); 165 importPassword = (CFStringRef) CFDictionaryGetValue(options, kSecImportExportPassphrase); 166 if (importPassword) 167 CFRetain(importPassword); 168 } 169 170 if (!importKeychain) { 171 // SecKeychainItemImport requires a keychain, so use default 172 status = SecKeychainCopyDefault(&importKeychain); 173 } 174 175 memset(&keyParams, 0, sizeof(SecKeyImportExportParameters)); 176 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; 177 keyParams.passphrase = importPassword; 178 keyParams.accessRef = importAccess; 179 180 status = SecKeychainItemImport(pkcs12_data, 181 NULL, /* no filename */ 182 &inputFormat, 183 &itemType, 184 flags, 185 &keyParams, 186 importKeychain, 187 &tmpItems); 188 189 // build an array of all non-identity certificates which were imported 190 if (!status) { 191 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 192 CFIndex i, count = CFArrayGetCount(tmpItems); 193 for (i=0; i<count; i++) { 194 CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i); 195 CFTypeID itemID = CFGetTypeID(anItem); 196 if (itemID == SecCertificateGetTypeID()) { 197 CFArrayAppendValue(certs, anItem); 198 } 199 } 200 } 201 202 // now build the output items (array of dictionaries) 203 if (!status) { 204 identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 205 CFIndex i, count = CFArrayGetCount(tmpItems); 206 for (i=0; i<count; i++) { 207 CFTypeRef anItem = (CFTypeRef)CFArrayGetValueAtIndex(tmpItems, i); 208 CFTypeID itemID = CFGetTypeID(anItem); 209 if (itemID == SecIdentityGetTypeID()) { 210 CFMutableDictionaryRef itemDict; 211 itemDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 212 0, 213 &kCFTypeDictionaryKeyCallBacks, 214 &kCFTypeDictionaryValueCallBacks); 215 216 SecCertificateRef itemCert = NULL; 217 status = SecIdentityCopyCertificate((SecIdentityRef)anItem, &itemCert); 218 219 // label 220 if (!status) { 221 CFStringRef label = SecCertificateCopySubjectSummary(itemCert); 222 if (label) { 223 CFDictionaryAddValue(itemDict, kSecImportItemLabel, label); 224 CFRelease(label); 225 } 226 } 227 228 // key ID 229 if (!status) { 230 SecKeyRef itemKey = NULL; 231 status = SecCertificateCopyPublicKey(itemCert, &itemKey); 232 if (!status) { 233 const CSSM_KEY *cssmKey; 234 status = SecKeyGetCSSMKey(itemKey, &cssmKey); 235 if (!status) { 236 unsigned char hash[CC_SHA1_DIGEST_LENGTH]; 237 CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, &hash[0]); 238 CFDataRef digest = CFDataCreate(NULL, (const UInt8 *)hash, CC_SHA1_DIGEST_LENGTH); 239 if (digest) { 240 CFDictionaryAddValue(itemDict, kSecImportItemKeyID, digest); 241 CFRelease(digest); 242 } 243 } 244 CFRelease(itemKey); 245 } 246 } 247 248 // trust 249 SecTrustRef trust = NULL; 250 SecPolicyRef policy = SecPolicyCreateBasicX509(); 251 CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 252 CFArrayAppendValue(certArray, itemCert); 253 if (certs) { 254 CFArrayAppendArray(certArray, certs, CFRangeMake(0, CFArrayGetCount(certs))); 255 } 256 status = SecTrustCreateWithCertificates(certArray, policy, &trust); 257 if (policy) { 258 CFRelease(policy); 259 } 260 if (trust) { 261 CFDictionaryAddValue(itemDict, kSecImportItemTrust, trust); 262 CFRelease(trust); 263 } 264 265 // certificate chain 266 if (certArray) { 267 CFDictionaryAddValue(itemDict, kSecImportItemCertChain, certArray); 268 CFRelease(certArray); 269 } 270 271 // identity 272 CFDictionaryAddValue(itemDict, kSecImportItemIdentity, anItem); 273 274 if (itemCert) 275 CFRelease(itemCert); 276 CFArrayAppendValue(identities, itemDict); 277 CFRelease(itemDict); 278 } 279 } 280 } 281 282 if (items) 283 *items = identities; 284 else if (identities) 285 CFRelease(identities); 286 287 if (certs) 288 CFRelease(certs); 289 if (tmpItems) 290 CFRelease(tmpItems); 291 if (importKeychain) 292 CFRelease(importKeychain); 293 if (importAccess) 294 CFRelease(importAccess); 295 if (importPassword) 296 CFRelease(importPassword); 297 298 return status; 299 300//FIXME: needs SecAsn1Coder implementation 301#if 0 302 pkcs12_context context = {}; 303 SecAsn1CoderCreate(&context.coder); 304 if (options) 305 context.passphrase = CFDictionaryGetValue(options, kSecImportExportPassphrase); 306 context.items = CFDictionaryCreateMutable(kCFAllocatorDefault, 307 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 308 int status = p12decode(&context, pkcs12_data); 309 if (!status) { 310 CFMutableArrayRef certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 311 CFDictionaryApplyFunction(context.items, collect_certs, certs); 312 313 CFMutableArrayRef identities = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 314 build_trust_chains_context a_build_trust_chains_context = { identities, certs }; 315 CFDictionaryApplyFunction(context.items, build_trust_chains, &a_build_trust_chains_context); 316 CFReleaseSafe(certs); 317 318 /* ignoring certs that weren't picked up as part of the certchain for found keys */ 319 320 *items = identities; 321 } 322 323 CFReleaseSafe(context.items); 324 SecAsn1CoderRelease(context.coder); 325 326 switch (status) { 327 case p12_noErr: return errSecSuccess; 328 case p12_passwordErr: return errSecAuthFailed; 329 case p12_decodeErr: return errSecDecode; 330 default: return errSecInternal; 331 }; 332 return errSecSuccess; 333#endif 334} 335 336