1/* 2 * Copyright (c) 2004,2011-2014 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 * SecExport.cpp - high-level facility for exporting Sec layer objects. 24 */ 25 26#include "SecImportExport.h" 27#include "SecImportExportAgg.h" 28#include "SecImportExportPem.h" 29#include "SecExternalRep.h" 30#include "SecImportExportUtils.h" 31#include <security_utilities/errors.h> 32#include <Security/SecIdentity.h> 33#include <Security/SecIdentityPriv.h> 34#include <Security/SecItem.h> 35#include <Security/SecBase.h> 36using namespace Security; 37using namespace KeychainCore; 38 39/* 40 * Convert Sec item to one or two SecExportReps, append to exportReps array. 41 * The "one or two" clause exists for SecIdentityRefs, which we split into 42 * a cert and a key. 43 * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef, 44 * SecCertRef, or SecIdentityRef. 45 */ 46static void impExpAddToExportReps( 47 CFTypeRef thing, // Key, Cert, Identity 48 CFMutableArrayRef exportReps, 49 unsigned &numCerts, // IN/OUT - accumulated 50 unsigned &numKeys) // IN/OUT - accumulated 51{ 52 if(CFGetTypeID(thing) == SecIdentityGetTypeID()) { 53 /* special case for SecIdentities, creates two SecExportReps */ 54 OSStatus ortn; 55 SecIdentityRef idRef = (SecIdentityRef)thing; 56 SecCertificateRef certRef; 57 SecKeyRef keyRef; 58 SecExportRep *rep; 59 60 /* cert */ 61 SecImpExpDbg("impExpAddToExportReps: adding identity cert and key"); 62 ortn = SecIdentityCopyCertificate(idRef, &certRef); 63 if(ortn) { 64 Security::MacOSError::throwMe(ortn); 65 } 66 rep = SecExportRep::vend(certRef); 67 CFArrayAppendValue(exportReps, rep); 68 CFRelease(certRef); // SecExportRep holds a reference 69 numCerts++; 70 71 /* private key */ 72 ortn = SecIdentityCopyPrivateKey(idRef, &keyRef); 73 if(ortn) { 74 Security::MacOSError::throwMe(ortn); 75 } 76 rep = SecExportRep::vend(keyRef); 77 CFArrayAppendValue(exportReps, rep); 78 CFRelease(keyRef); // SecExportRep holds a reference 79 numKeys++; 80 } 81 else { 82 /* this throws if 'thing' is an unacceptable type */ 83 SecExportRep *rep = SecExportRep::vend(thing); 84 SecImpExpDbg("impExpAddToExportReps: adding single type %d", 85 (int)rep->externType()); 86 CFArrayAppendValue(exportReps, rep); 87 if(rep->externType() == kSecItemTypeCertificate) { 88 numCerts++; 89 } 90 else { 91 numKeys++; 92 } 93 } 94} 95 96#pragma mark --- public export function --- 97 98OSStatus SecKeychainItemExport( 99 CFTypeRef keychainItemOrArray, 100 SecExternalFormat outputFormat, // a SecExternalFormat 101 SecItemImportExportFlags flags, // kSecItemPemArmour, etc. 102 const SecKeyImportExportParameters *keyParams, // optional 103 CFDataRef *exportedData) // external representation 104 // returned here 105{ 106 BEGIN_IMP_EXP_SECAPI 107 108 /* some basic input validation */ 109 if(keychainItemOrArray == NULL) { 110 return errSecParam; 111 } 112 if(keyParams != NULL) { 113 /* can't specify explicit passphrase and ask for secure one */ 114 if( (keyParams->passphrase != NULL) && 115 ((keyParams->flags & kSecKeySecurePassphrase) != 0)) { 116 return errSecParam; 117 } 118 } 119 120 unsigned numKeys = 0; 121 unsigned numCerts = 0; 122 unsigned numTotalExports = 0; 123 OSStatus ortn = errSecSuccess; 124 SecExportRep *rep = NULL; // common temp variable 125 CFMutableDataRef outputData = NULL; 126 const char *pemHeader = "UNKNOWN"; 127 128 /* convert keychainItemOrArray to CFArray of SecExportReps */ 129 CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL); 130 /* subsequent errors to errOut: */ 131 132 try { 133 if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) { 134 CFArrayRef arr = (CFArrayRef)keychainItemOrArray; 135 CFIndex arraySize = CFArrayGetCount(arr); 136 for(CFIndex dex=0; dex<arraySize; dex++) { 137 impExpAddToExportReps(CFArrayGetValueAtIndex(arr, dex), 138 exportReps, numCerts, numKeys); 139 } 140 } 141 else { 142 impExpAddToExportReps(keychainItemOrArray, exportReps, numCerts, numKeys); 143 } 144 } 145 catch(const Security::MacOSError osErr) { 146 ortn = osErr.error; 147 goto errOut; 148 } 149 catch(...) { 150 ortn = errSecParam; 151 goto errOut; 152 } 153 numTotalExports = (unsigned int)CFArrayGetCount(exportReps); 154 assert((numCerts + numKeys) == numTotalExports); 155 if((numTotalExports > 1) && (outputFormat == kSecFormatUnknown)) { 156 /* default aggregate format is PEM sequence */ 157 outputFormat = kSecFormatPEMSequence; 158 } 159 160 /* 161 * Break out to SecExternalFormat-specific code, appending all data to outputData 162 */ 163 outputData = CFDataCreateMutable(NULL, 0); 164 switch(outputFormat) { 165 case kSecFormatPKCS7: 166 ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData); 167 pemHeader = PEM_STRING_PKCS7; 168 break; 169 case kSecFormatPKCS12: 170 ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData); 171 pemHeader = PEM_STRING_PKCS12; 172 break; 173 case kSecFormatPEMSequence: 174 { 175 /* 176 * A bit of a special case. Create an intermediate DER encoding 177 * of each SecExportRef, in the default format for that item; 178 * PEM encode the result, and append the PEM encoding to 179 * outputData. 180 */ 181 CFIndex numReps = CFArrayGetCount(exportReps); 182 for(CFIndex dex=0; dex<numReps; dex++) { 183 184 rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex); 185 186 /* default DER encoding */ 187 CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0); 188 ortn = rep->exportRep(kSecFormatUnknown, flags, keyParams, 189 tmpData, &pemHeader); 190 if(ortn) { 191 SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData); 192 CFRelease(tmpData); 193 goto errOut; 194 } 195 196 /* PEM to accumulating output */ 197 assert(rep->pemParamLines() == NULL); 198 ortn = impExpPemEncodeExportRep((CFDataRef)tmpData, 199 pemHeader, NULL, /* no pemParamLines, right? */ 200 outputData); 201 CFRelease(tmpData); 202 if(ortn) { 203 goto errOut; 204 } 205 } 206 break; 207 } 208 209 /* Enumerate remainder explicitly for clarity; all are single-item forms */ 210 case kSecFormatOpenSSL: 211 case kSecFormatSSH: 212 case kSecFormatSSHv2: 213 case kSecFormatBSAFE: 214 case kSecFormatRawKey: 215 case kSecFormatWrappedPKCS8: 216 case kSecFormatWrappedOpenSSL: 217 case kSecFormatWrappedSSH: 218 case kSecFormatWrappedLSH: 219 case kSecFormatX509Cert: 220 case kSecFormatUnknown: // i.e., default, handled by SecExportRep 221 { 222 unsigned foundCount = 0; 223 224 /* verify that we have exactly one of specified item */ 225 if(outputFormat == kSecFormatX509Cert) { 226 foundCount = numCerts; 227 } 228 else if(outputFormat == kSecFormatUnknown) { 229 /* can't go wrong */ 230 foundCount = numTotalExports; 231 } 232 else { 233 foundCount = numKeys; 234 } 235 if((numTotalExports != 1) || (foundCount != 1)) { 236 SecImpExpDbg("Export single item format with other than one item"); 237 ortn = errSecParam; 238 goto errOut; 239 } 240 assert(CFArrayGetCount(exportReps) == 1); 241 rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); 242 ortn = rep->exportRep(outputFormat, flags, 243 keyParams, outputData, &pemHeader); 244 break; 245 } 246 default: 247 SecImpExpDbg("SecKeychainItemExport: bad format (%u)", 248 (unsigned)outputFormat); 249 ortn = errSecParam; 250 goto errOut; 251 } 252 253 /* 254 * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which 255 * case outputData is all ready to ship out to the caller); mandatory 256 * if exportRep has a non-NULL pemParamLines (which can only happen if we're 257 * exporting a single item). 258 */ 259 if(ortn == errSecSuccess) { 260 if(outputFormat == kSecFormatPEMSequence) { 261 *exportedData = outputData; 262 outputData = NULL; 263 } 264 else { 265 rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0); 266 if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) { 267 /* PEM encode a single item */ 268 CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0); 269 ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader, 270 rep->pemParamLines(), tmpData); 271 CFRelease(outputData); // done with this 272 outputData = NULL; 273 *exportedData = tmpData; // caller gets PEM 274 } 275 else { 276 *exportedData = outputData; 277 outputData = NULL; 278 } 279 } 280 } 281errOut: 282 if(exportReps != NULL) { 283 /* CFArray of our own classes, no auto release */ 284 CFIndex num = CFArrayGetCount(exportReps); 285 for(CFIndex dex=0; dex<num; dex++) { 286 rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex); 287 delete rep; 288 } 289 CFRelease(exportReps); 290 } 291 if(outputData != NULL) { 292 CFRelease(outputData); 293 outputData = NULL; 294 } 295 if(ortn) { 296 return SecKeychainErrFromOSStatus(ortn); 297 } 298 else { 299 return errSecSuccess; 300 } 301 302 END_IMP_EXP_SECAPI 303} 304 305 306OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat, 307 SecItemImportExportFlags flags, /* kSecItemPemArmor, etc. */ 308 const SecItemImportExportKeyParameters *keyParams, /* optional */ 309 CFDataRef *exportedData) 310{ 311 SecKeyImportExportParameters* oldStructPtr = NULL; 312 SecKeyImportExportParameters oldStruct; 313 memset(&oldStruct, 0, sizeof(oldStruct)); 314 315 if (NULL != keyParams) 316 { 317 318 SecKeyRef tempKey = NULL; 319 320 if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray)) 321 { 322 tempKey = (SecKeyRef)secItemOrArray; 323 } 324 325 if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey, 326 keyParams, &oldStruct)) 327 { 328 oldStructPtr = &oldStruct; 329 } 330 } 331 332 return SecKeychainItemExport(secItemOrArray, outputFormat, flags, oldStructPtr, exportedData); 333} 334 335 336 337 338 339 340 341