1/* 2 * Copyright (c) 2004 Apple Computer, 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 * SecExternalRep.cpp - private class representing an external representation of 24 * a SecKeychainItemRef, used by SecImportExport.h 25 */ 26 27#include "SecExternalRep.h" 28#include "SecImportExportPem.h" 29#include "SecImportExportAgg.h" 30#include "SecImportExportUtils.h" 31#include "SecImportExportPkcs8.h" 32#include "SecImportExportCrypto.h" 33#include "SecImportExportOpenSSH.h" 34#include <security_utilities/errors.h> 35#include <Security/SecBase.h> 36#include <Security/SecKeyPriv.h> 37#include <Security/SecCertificate.h> 38#include <Security/cssmapi.h> 39 40using namespace Security; 41using namespace KeychainCore; 42 43 44#pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() --- 45 46namespace SecExport { 47 48class Key : public SecExportRep 49{ 50 friend class SecExportRep; 51protected: 52 Key( 53 CFTypeRef kcItemRef); 54 ~Key(); 55 OSStatus exportRep( 56 SecExternalFormat format, 57 SecItemImportExportFlags flags, 58 const SecKeyImportExportParameters *keyParams, // optional 59 CFMutableDataRef outData, // data appended here 60 const char **pemHeader); // e.g., "RSA PUBLIC KEY" 61 62private: 63 CSSM_ALGORITHMS mKeyAlg; 64 const CSSM_KEY *mCssmKey; 65}; 66 67class Cert : public SecExportRep 68{ 69 friend class SecExportRep; 70protected: 71 Cert( 72 CFTypeRef kcItemRef); 73 ~Cert(); 74 OSStatus exportRep( 75 SecExternalFormat format, 76 SecItemImportExportFlags flags, 77 const SecKeyImportExportParameters *keyParams, // optional 78 CFMutableDataRef outData, // data appended here 79 const char **pemHeader); // e.g., "CERTIFICATE" 80}; 81 82} /* namespace SecExport */ 83 84#pragma mark --- SecExportRep: Representation of an internal object on export --- 85 86SecExportRep::SecExportRep( 87 CFTypeRef kcItemRef) : 88 mKcItem((SecKeychainItemRef)kcItemRef), 89 mPemParamLines(NULL) 90{ 91 CFRetain(mKcItem); 92} 93 94SecExportRep::~SecExportRep() 95{ 96 if(mKcItem) { 97 CFRelease(mKcItem); 98 } 99 if(mPemParamLines) { 100 CFRelease(mPemParamLines); 101 } 102} 103 104SecExportRep::SecExportRep() { 105 MacOSError::throwMe(errSecInvalidItemRef); 106} 107 108/* must be implemented by subclass */ 109OSStatus SecExportRep::exportRep( 110 SecExternalFormat format, 111 SecItemImportExportFlags flags, 112 const SecKeyImportExportParameters *keyParams, // optional 113 CFMutableDataRef outData, // data appended here 114 const char **pemHeader) // e.g., "X509 CERTIFICATE" 115{ 116 MacOSError::throwMe(errSecInvalidItemRef); 117} 118 119/* 120 * Sole public means of obtaining a SecExportRep object. In fact only instances 121 * of subclasses are vended but caller does not know that. 122 * 123 * Gleans SecExternalItemType from incoming type, throws MacOSError if 124 * incoming type is bogus. 125 * 126 * Vended object holds a reference to kcItem for its lifetime. 127 */ 128SecExportRep *SecExportRep::vend( 129 CFTypeRef kcItemRef) 130{ 131 CFTypeID itemType = CFGetTypeID(kcItemRef); 132 if(itemType == SecCertificateGetTypeID()) { 133 return new SecExport::Cert(kcItemRef); 134 } 135 else if(itemType == SecKeyGetTypeID()) { 136 return new SecExport::Key(kcItemRef); 137 } 138 else { 139 MacOSError::throwMe(errSecInvalidItemRef); 140 } 141} 142 143#pragma mark --- Key External rep --- 144 145SecExport::Key::Key( 146 CFTypeRef kcItemRef) : 147 SecExportRep(kcItemRef) 148{ 149 150 /* figure out if it's public, private, or session */ 151 OSStatus ortn; 152 ortn = SecKeyGetCSSMKey((SecKeyRef)kcItemRef, &mCssmKey); 153 if(ortn) { 154 SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()"); 155 MacOSError::throwMe(ortn); 156 } 157 switch(mCssmKey->KeyHeader.KeyClass) { 158 case CSSM_KEYCLASS_PUBLIC_KEY: 159 mExternType = kSecItemTypePublicKey; 160 SecImpExpDbg("SecExportRep::Key(): SET_PubKey"); 161 break; 162 case CSSM_KEYCLASS_PRIVATE_KEY: 163 mExternType = kSecItemTypePrivateKey; 164 SecImpExpDbg("SecExportRep::Key(): SET_PrivKey"); 165 break; 166 case CSSM_KEYCLASS_SESSION_KEY: 167 mExternType = kSecItemTypeSessionKey; 168 SecImpExpDbg("SecExportRep::Key(): SET_SessionKey"); 169 break; 170 default: 171 SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)", 172 (unsigned long)mCssmKey->KeyHeader.KeyClass); 173 MacOSError::throwMe(errSecInvalidItemRef); 174 } 175 mKeyAlg = mCssmKey->KeyHeader.AlgorithmId; 176} 177 178SecExport::Key::~Key() 179{ 180 /* nothing for now */ 181} 182 183/* 184 * The heart of this class: cook up external representation, appending to 185 * existing CFMutableDataRef. 186 */ 187OSStatus SecExport::Key::exportRep( 188 SecExternalFormat format, 189 SecItemImportExportFlags flags, 190 const SecKeyImportExportParameters *keyParams, // optional 191 CFMutableDataRef outData, // data appended here 192 const char **pemHeader)// e.g., "X509 CERTIFICATE" 193{ 194 assert(outData != NULL); 195 assert(mKcItem != NULL); 196 assert(mCssmKey != NULL); 197 198 /* 199 * Currently only OpsnSSH formats allow for a DescriptiveData field 200 * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is 201 * the 'comment' field). Infer the DescriptiveData to be embedded 202 * in the exported key from the item's PrintName attribute. 203 */ 204 CssmAutoData descrData(Allocator::standard()); 205 switch(format) { 206 case kSecFormatSSH: 207 case kSecFormatSSHv2: 208 case kSecFormatWrappedSSH: 209 impExpOpensshInferDescData((SecKeyRef)mKcItem, descrData); 210 break; 211 default: 212 break; 213 } 214 215 /* 216 * Handle wrapped key formats. 217 */ 218 switch(format) { 219 case kSecFormatWrappedPKCS8: 220 return impExpPkcs8Export((SecKeyRef)mKcItem, flags, keyParams, 221 outData, pemHeader); 222 case kSecFormatWrappedOpenSSL: 223 return impExpWrappedKeyOpenSslExport((SecKeyRef)mKcItem, flags, keyParams, 224 outData, pemHeader, &mPemParamLines); 225 case kSecFormatWrappedSSH: 226 return impExpWrappedOpenSSHExport((SecKeyRef)mKcItem, flags, keyParams, 227 descrData, outData); 228 case kSecFormatWrappedLSH: 229 return errSecUnsupportedFormat; 230 default: 231 break; 232 } 233 234 /* 235 * Remaining formats just do a NULL key wrap. Figure out the appropriate 236 * CDSA-specific format and wrap parameters. 237 */ 238 OSStatus ortn = errSecSuccess; 239 CSSM_KEYBLOB_FORMAT blobForm; 240 241 switch(mExternType) { 242 case kSecItemTypePublicKey: 243 switch(mKeyAlg) { 244 case CSSM_ALGID_RSA: 245 *pemHeader = PEM_STRING_RSA_PUBLIC; 246 break; 247 case CSSM_ALGID_DH: 248 *pemHeader = PEM_STRING_DH_PUBLIC; 249 break; 250 case CSSM_ALGID_DSA: 251 *pemHeader = PEM_STRING_DSA_PUBLIC; 252 break; 253 case CSSM_ALGID_ECDSA: 254 *pemHeader = PEM_STRING_ECDSA_PUBLIC; 255 break; 256 default: 257 SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu", 258 (unsigned long)mKeyAlg); 259 return errSecUnsupportedFormat; 260 } /* end switch(mKeyAlg) */ 261 break; /* from case externType kSecItemTypePublicKey */ 262 263 case kSecItemTypePrivateKey: 264 switch(mKeyAlg) { 265 case CSSM_ALGID_RSA: 266 *pemHeader = PEM_STRING_RSA; 267 break; 268 case CSSM_ALGID_DH: 269 *pemHeader = PEM_STRING_DH_PRIVATE; 270 break; 271 case CSSM_ALGID_DSA: 272 *pemHeader = PEM_STRING_DSA; 273 break; 274 case CSSM_ALGID_ECDSA: 275 *pemHeader = PEM_STRING_ECDSA_PRIVATE; 276 break; 277 default: 278 SecImpExpDbg("SecExportRep::exportRep unknown private key alg " 279 "%lu", (unsigned long)mKeyAlg); 280 return errSecUnsupportedFormat; 281 } /* end switch(mKeyAlg) */ 282 break; /* from case externType kSecItemTypePrivateKey */ 283 284 case kSecItemTypeSessionKey: 285 *pemHeader = PEM_STRING_SESSION; 286 break; 287 default: 288 assert(0); 289 return errSecInvalidItemRef; 290 } /* switch(mExternType) */ 291 292 /* Map our external params to CDSA blob format */ 293 CSSM_KEYCLASS keyClass; 294 ortn = impExpKeyForm(format, mExternType, mKeyAlg, &blobForm, &keyClass); 295 if(ortn) { 296 return ortn; 297 } 298 299 /* Specify format of null-wrapped key */ 300 CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE; 301 switch(mExternType) { 302 case kSecItemTypePrivateKey: 303 formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT; 304 break; 305 case kSecItemTypePublicKey: 306 formatAttrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT; 307 break; 308 /* symmetric key doesn't have a format */ 309 default: 310 break; 311 } 312 313 CSSM_CSP_HANDLE cspHand; 314 ortn = SecKeyGetCSPHandle((SecKeyRef)mKcItem, &cspHand); 315 if(ortn) { 316 SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error"); 317 return ortn; 318 } 319 320 /* perform the NULL wrap --> wrapped Key */ 321 CSSM_KEY wrappedKey; 322 memset(&wrappedKey, 0, sizeof(wrappedKey)); 323 const CSSM_DATA &dd = descrData; 324 ortn = impExpExportKeyCommon(cspHand, 325 (SecKeyRef)mKcItem, 326 NULL, // wrappingKey not used for NULL 327 &wrappedKey, // destination 328 CSSM_ALGID_NONE, 329 CSSM_ALGMODE_NONE, 330 CSSM_PADDING_NONE, 331 CSSM_KEYBLOB_WRAPPED_FORMAT_NONE, 332 formatAttrType, 333 blobForm, 334 &dd, // descriptiveData 335 NULL); // IV 336 337 if(ortn == CSSM_OK) { 338 /* pass key data back to caller */ 339 CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length); 340 } 341 CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE); 342 return ortn; 343} 344 345#pragma mark --- Certificate External rep --- 346 347SecExport::Cert::Cert( 348 CFTypeRef kcItemRef) : 349 SecExportRep(kcItemRef) 350{ 351 mExternType = kSecItemTypeCertificate; 352} 353 354SecExport::Cert::~Cert() 355{ 356 /* nothing for now */ 357} 358 359/* 360 * The heart of this class: cook up external representation, appending to 361 * existing CFMutableDataRef. 362 */ 363OSStatus SecExport::Cert::exportRep( 364 SecExternalFormat format, 365 SecItemImportExportFlags flags, 366 const SecKeyImportExportParameters *keyParams, // optional 367 CFMutableDataRef outData, // data appended here 368 const char **pemHeader)// e.g., "X509 CERTIFICATE" 369{ 370 assert(outData != NULL); 371 assert(mKcItem != NULL); 372 373 switch(format) { 374 case kSecFormatUnknown: // default 375 case kSecFormatX509Cert: // currently, only supported format 376 break; 377 default: 378 SecImpExpDbg("SecExportRep::exportRep unsupported format for cert"); 379 return errSecUnsupportedFormat; 380 } 381 382 CFDataRef cdata = SecCertificateCopyData((SecCertificateRef)mKcItem); 383 if(!cdata) { 384 SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error"); 385 return errSecUnsupportedFormat; 386 } 387 388 CFDataAppendBytes(outData, CFDataGetBytePtr(cdata), CFDataGetLength(cdata)); 389 CFRelease(cdata); 390 *pemHeader = PEM_STRING_X509; 391 return errSecSuccess; 392} 393 394#pragma mark --- SecImportRep: Representation of an external object on import --- 395 396/* 397 * for import, when we have the external representation. 398 * All arguments except for the CFDataRef are optional (i.e., "unknown" 399 * is legal). 400 */ 401SecImportRep::SecImportRep( 402 CFDataRef external, 403 SecExternalItemType externType, // may be unknown 404 SecExternalFormat externFormat, // may be unknown 405 CSSM_ALGORITHMS keyAlg, // may be unknown, CSSM_ALGID_NONE 406 CFArrayRef pemParamLines /* = NULL */ ) : 407 mPrintName(NULL), 408 mExternal(external), 409 mExternType(externType), 410 mExternFormat(externFormat), 411 mKeyAlg(keyAlg), 412 mPemParamLines(pemParamLines) 413{ 414 CFRetain(mExternal); 415} 416 417SecImportRep::~SecImportRep() 418{ 419 if(mPrintName) { 420 free(mPrintName); 421 } 422 if(mExternal) { 423 CFRelease(mExternal); 424 } 425 if(mPemParamLines) { 426 CFRelease(mPemParamLines); 427 } 428} 429 430/* 431 * Convert to one or more SecItemRefs and/or import to keychain. 432 * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle. 433 */ 434OSStatus SecImportRep::importRep( 435 SecKeychainRef importKeychain, // optional 436 CSSM_CSP_HANDLE cspHand, // required 437 SecItemImportExportFlags flags, 438 const SecKeyImportExportParameters *keyParams, // optional 439 ImpPrivKeyImportState &keyImportState, // IN/OUT 440 CFMutableArrayRef outArray) // optional, append here 441{ 442 /* caller must have sorted this out by now */ 443 assert((mExternType != kSecItemTypeUnknown) && 444 (mExternFormat != kSecFormatUnknown)); 445 446 /* app could conceivably botch these with crafty PEM hacking */ 447 if((mExternal == NULL) || (CFDataGetLength(mExternal) == 0)) { 448 return errSecParam; 449 } 450 451 /* handle the easy ones first */ 452 switch(mExternFormat) { 453 case kSecFormatPKCS12: 454 return impExpPkcs12Import(mExternal, flags, keyParams, 455 keyImportState, importKeychain, cspHand, outArray); 456 case kSecFormatX509Cert: 457 case kSecFormatPKCS7: 458 { 459 OSStatus rx = impExpPkcs7Import(mExternal, flags, keyParams, importKeychain, 460 outArray); 461 if (rx == errSecUnknownFormat) 462 { 463 CSSM_DATA cdata; 464 cdata.Data = (uint8 *)CFDataGetBytePtr(mExternal); 465 cdata.Length = (CSSM_SIZE)CFDataGetLength(mExternal); 466 return impExpImportCertCommon(&cdata, importKeychain, outArray); 467 } 468 return rx; 469 } 470 case kSecFormatNetscapeCertSequence: 471 return impExpNetscapeCertImport(mExternal, flags, keyParams, importKeychain, 472 outArray); 473 default: 474 break; 475 } 476 477 if((mExternType == kSecItemTypeCertificate) || 478 (mExternType == kSecItemTypeAggregate)) { 479 SecImpExpDbg("SecImportRep::importRep screwup"); 480 return errSecUnimplemented; 481 } 482 483 /* 484 * All that's left: keys. 485 */ 486 if((mExternType == kSecItemTypePrivateKey) && (keyImportState == PIS_NoMore)) { 487 /* multi key import against caller's wishes */ 488 return errSecMultiplePrivKeys; 489 } 490 491 /* optionally infer PrintName attribute */ 492 switch(mExternFormat) { 493 case kSecFormatSSH: 494 case kSecFormatWrappedSSH: 495 case kSecFormatSSHv2: 496 mPrintName = impExpOpensshInferPrintName(mExternal, mExternType, mExternFormat); 497 break; 498 default: 499 /* use defaults */ 500 break; 501 } 502 503 OSStatus ortn = errSecSuccess; 504 505 switch(mExternFormat) { 506 case kSecFormatOpenSSL: 507 case kSecFormatSSH: 508 case kSecFormatSSHv2: 509 case kSecFormatBSAFE: 510 case kSecFormatRawKey: 511 ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType, 512 mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray); 513 break; 514 case kSecFormatWrappedPKCS8: 515 ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags, 516 keyParams, outArray); 517 break; 518 case kSecFormatWrappedOpenSSL: 519 ortn = importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams, 520 outArray); 521 break; 522 case kSecFormatWrappedSSH: 523 ortn = impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand, 524 flags, keyParams, mPrintName, outArray); 525 break; 526 case kSecFormatWrappedLSH: 527 default: 528 return errSecUnknownFormat; 529 } 530 if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) { 531 /* reached our limit */ 532 keyImportState = PIS_NoMore; 533 } 534 return ortn; 535} 536 537