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