--- /dev/null
+/*
+ * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * SecExport.cpp - high-level facility for exporting Sec layer objects.
+ */
+
+#include "SecImportExport.h"
+#include "SecImportExportAgg.h"
+#include "SecImportExportPem.h"
+#include "SecExternalRep.h"
+#include "SecImportExportUtils.h"
+#include <security_utilities/errors.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecIdentityPriv.h>
+#include <Security/SecItem.h>
+#include <Security/SecBase.h>
+using namespace Security;
+using namespace KeychainCore;
+
+/*
+ * Convert Sec item to one or two SecExportReps, append to exportReps array.
+ * The "one or two" clause exists for SecIdentityRefs, which we split into
+ * a cert and a key.
+ * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef,
+ * SecCertRef, or SecIdentityRef.
+ */
+static void impExpAddToExportReps(
+ CFTypeRef thing, // Key, Cert, Identity
+ CFMutableArrayRef exportReps,
+ unsigned &numCerts, // IN/OUT - accumulated
+ unsigned &numKeys) // IN/OUT - accumulated
+{
+ if(CFGetTypeID(thing) == SecIdentityGetTypeID()) {
+ /* special case for SecIdentities, creates two SecExportReps */
+ OSStatus ortn;
+ SecIdentityRef idRef = (SecIdentityRef)thing;
+ SecCertificateRef certRef;
+ SecKeyRef keyRef;
+ SecExportRep *rep;
+
+ /* cert */
+ SecImpExpDbg("impExpAddToExportReps: adding identity cert and key");
+ ortn = SecIdentityCopyCertificate(idRef, &certRef);
+ if(ortn) {
+ Security::MacOSError::throwMe(ortn);
+ }
+ rep = SecExportRep::vend(certRef);
+ CFArrayAppendValue(exportReps, rep);
+ CFRelease(certRef); // SecExportRep holds a reference
+ numCerts++;
+
+ /* private key */
+ ortn = SecIdentityCopyPrivateKey(idRef, &keyRef);
+ if(ortn) {
+ Security::MacOSError::throwMe(ortn);
+ }
+ rep = SecExportRep::vend(keyRef);
+ CFArrayAppendValue(exportReps, rep);
+ CFRelease(keyRef); // SecExportRep holds a reference
+ numKeys++;
+ }
+ else {
+ /* this throws if 'thing' is an unacceptable type */
+ SecExportRep *rep = SecExportRep::vend(thing);
+ SecImpExpDbg("impExpAddToExportReps: adding single type %d",
+ (int)rep->externType());
+ CFArrayAppendValue(exportReps, rep);
+ if(rep->externType() == kSecItemTypeCertificate) {
+ numCerts++;
+ }
+ else {
+ numKeys++;
+ }
+ }
+}
+
+#pragma mark --- public export function ---
+
+OSStatus SecKeychainItemExport(
+ CFTypeRef keychainItemOrArray,
+ SecExternalFormat outputFormat, // a SecExternalFormat
+ SecItemImportExportFlags flags, // kSecItemPemArmour, etc.
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFDataRef *exportedData) // external representation
+ // returned here
+{
+ BEGIN_IMP_EXP_SECAPI
+
+ /* some basic input validation */
+ if(keychainItemOrArray == NULL) {
+ return errSecParam;
+ }
+ if(keyParams != NULL) {
+ /* can't specify explicit passphrase and ask for secure one */
+ if( (keyParams->passphrase != NULL) &&
+ ((keyParams->flags & kSecKeySecurePassphrase) != 0)) {
+ return errSecParam;
+ }
+ }
+
+ unsigned numKeys = 0;
+ unsigned numCerts = 0;
+ unsigned numTotalExports = 0;
+ OSStatus ortn = errSecSuccess;
+ SecExportRep *rep = NULL; // common temp variable
+ CFMutableDataRef outputData = NULL;
+ const char *pemHeader = "UNKNOWN";
+
+ /* convert keychainItemOrArray to CFArray of SecExportReps */
+ CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL);
+ /* subsequent errors to errOut: */
+
+ try {
+ if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) {
+ CFArrayRef arr = (CFArrayRef)keychainItemOrArray;
+ CFIndex arraySize = CFArrayGetCount(arr);
+ for(CFIndex dex=0; dex<arraySize; dex++) {
+ impExpAddToExportReps(CFArrayGetValueAtIndex(arr, dex),
+ exportReps, numCerts, numKeys);
+ }
+ }
+ else {
+ impExpAddToExportReps(keychainItemOrArray, exportReps, numCerts, numKeys);
+ }
+ }
+ catch(const Security::MacOSError osErr) {
+ ortn = osErr.error;
+ goto errOut;
+ }
+ catch(...) {
+ ortn = errSecParam;
+ goto errOut;
+ }
+ numTotalExports = (unsigned int)CFArrayGetCount(exportReps);
+ assert((numCerts + numKeys) == numTotalExports);
+ if((numTotalExports > 1) && (outputFormat == kSecFormatUnknown)) {
+ /* default aggregate format is PEM sequence */
+ outputFormat = kSecFormatPEMSequence;
+ }
+
+ /*
+ * Break out to SecExternalFormat-specific code, appending all data to outputData
+ */
+ outputData = CFDataCreateMutable(NULL, 0);
+ switch(outputFormat) {
+ case kSecFormatPKCS7:
+ ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData);
+ pemHeader = PEM_STRING_PKCS7;
+ break;
+ case kSecFormatPKCS12:
+ ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData);
+ pemHeader = PEM_STRING_PKCS12;
+ break;
+ case kSecFormatPEMSequence:
+ {
+ /*
+ * A bit of a special case. Create an intermediate DER encoding
+ * of each SecExportRef, in the default format for that item;
+ * PEM encode the result, and append the PEM encoding to
+ * outputData.
+ */
+ CFIndex numReps = CFArrayGetCount(exportReps);
+ for(CFIndex dex=0; dex<numReps; dex++) {
+
+ rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
+
+ /* default DER encoding */
+ CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
+ ortn = rep->exportRep(kSecFormatUnknown, flags, keyParams,
+ tmpData, &pemHeader);
+ if(ortn) {
+ SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData);
+ CFRelease(tmpData);
+ goto errOut;
+ }
+
+ /* PEM to accumulating output */
+ assert(rep->pemParamLines() == NULL);
+ ortn = impExpPemEncodeExportRep((CFDataRef)tmpData,
+ pemHeader, NULL, /* no pemParamLines, right? */
+ outputData);
+ CFRelease(tmpData);
+ if(ortn) {
+ goto errOut;
+ }
+ }
+ break;
+ }
+
+ /* Enumerate remainder explicitly for clarity; all are single-item forms */
+ case kSecFormatOpenSSL:
+ case kSecFormatSSH:
+ case kSecFormatSSHv2:
+ case kSecFormatBSAFE:
+ case kSecFormatRawKey:
+ case kSecFormatWrappedPKCS8:
+ case kSecFormatWrappedOpenSSL:
+ case kSecFormatWrappedSSH:
+ case kSecFormatWrappedLSH:
+ case kSecFormatX509Cert:
+ case kSecFormatUnknown: // i.e., default, handled by SecExportRep
+ {
+ unsigned foundCount = 0;
+
+ /* verify that we have exactly one of specified item */
+ if(outputFormat == kSecFormatX509Cert) {
+ foundCount = numCerts;
+ }
+ else if(outputFormat == kSecFormatUnknown) {
+ /* can't go wrong */
+ foundCount = numTotalExports;
+ }
+ else {
+ foundCount = numKeys;
+ }
+ if((numTotalExports != 1) || (foundCount != 1)) {
+ SecImpExpDbg("Export single item format with other than one item");
+ ortn = errSecParam;
+ goto errOut;
+ }
+ assert(CFArrayGetCount(exportReps) == 1);
+ rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
+ ortn = rep->exportRep(outputFormat, flags,
+ keyParams, outputData, &pemHeader);
+ break;
+ }
+ default:
+ SecImpExpDbg("SecKeychainItemExport: bad format (%u)",
+ (unsigned)outputFormat);
+ ortn = errSecParam;
+ goto errOut;
+ }
+
+ /*
+ * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which
+ * case outputData is all ready to ship out to the caller); mandatory
+ * if exportRep has a non-NULL pemParamLines (which can only happen if we're
+ * exporting a single item).
+ */
+ if(ortn == errSecSuccess) {
+ if(outputFormat == kSecFormatPEMSequence) {
+ *exportedData = outputData;
+ outputData = NULL;
+ }
+ else {
+ rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
+ if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) {
+ /* PEM encode a single item */
+ CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
+ ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader,
+ rep->pemParamLines(), tmpData);
+ CFRelease(outputData); // done with this
+ outputData = NULL;
+ *exportedData = tmpData; // caller gets PEM
+ }
+ else {
+ *exportedData = outputData;
+ outputData = NULL;
+ }
+ }
+ }
+errOut:
+ if(exportReps != NULL) {
+ /* CFArray of our own classes, no auto release */
+ CFIndex num = CFArrayGetCount(exportReps);
+ for(CFIndex dex=0; dex<num; dex++) {
+ rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
+ delete rep;
+ }
+ CFRelease(exportReps);
+ }
+ if(outputData != NULL) {
+ CFRelease(outputData);
+ outputData = NULL;
+ }
+ if(ortn) {
+ return SecKeychainErrFromOSStatus(ortn);
+ }
+ else {
+ return errSecSuccess;
+ }
+
+ END_IMP_EXP_SECAPI
+}
+
+
+OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat,
+ SecItemImportExportFlags flags, /* kSecItemPemArmor, etc. */
+ const SecItemImportExportKeyParameters *keyParams, /* optional */
+ CFDataRef *exportedData)
+{
+ SecKeyImportExportParameters* oldStructPtr = NULL;
+ SecKeyImportExportParameters oldStruct;
+ memset(&oldStruct, 0, sizeof(oldStruct));
+
+ if (NULL != keyParams)
+ {
+
+ SecKeyRef tempKey = NULL;
+
+ if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray))
+ {
+ tempKey = (SecKeyRef)secItemOrArray;
+ }
+
+ if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey,
+ keyParams, &oldStruct))
+ {
+ oldStructPtr = &oldStruct;
+ }
+ }
+
+ return SecKeychainItemExport(secItemOrArray, outputFormat, flags, oldStructPtr, exportedData);
+}
+
+
+
+
+
+
+