2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * SecExport.cpp - high-level facility for exporting Sec layer objects.
26 #include "SecImportExport.h"
27 #include "SecImportExportAgg.h"
28 #include "SecImportExportPem.h"
29 #include "SecExternalRep.h"
30 #include "SecImportExportUtils.h"
31 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
32 #include <security_utilities/errors.h>
33 #include <Security/SecIdentity.h>
34 #include <Security/SecIdentityPriv.h>
35 #include <Security/SecItem.h>
37 using namespace Security
;
38 using namespace KeychainCore
;
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
44 * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef,
45 * SecCertRef, or SecIdentityRef.
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
53 if(CFGetTypeID(thing
) == SecIdentityGetTypeID()) {
54 /* special case for SecIdentities, creates two SecExportReps */
56 SecIdentityRef idRef
= (SecIdentityRef
)thing
;
57 SecCertificateRef certRef
;
62 SecImpExpDbg("impExpAddToExportReps: adding identity cert and key");
63 ortn
= SecIdentityCopyCertificate(idRef
, &certRef
);
65 Security::MacOSError::throwMe(ortn
);
67 rep
= SecExportRep::vend(certRef
);
68 CFArrayAppendValue(exportReps
, rep
);
69 CFRelease(certRef
); // SecExportRep holds a reference
73 ortn
= SecIdentityCopyPrivateKey(idRef
, &keyRef
);
75 Security::MacOSError::throwMe(ortn
);
77 rep
= SecExportRep::vend(keyRef
);
78 CFArrayAppendValue(exportReps
, rep
);
79 CFRelease(keyRef
); // SecExportRep holds a reference
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
) {
97 #pragma mark --- public export function ---
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
109 /* some basic input validation */
110 if(keychainItemOrArray
== NULL
) {
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)) {
121 unsigned numKeys
= 0;
122 unsigned numCerts
= 0;
123 unsigned numTotalExports
= 0;
124 OSStatus ortn
= noErr
;
125 SecExportRep
*rep
= NULL
; // common temp variable
126 CFMutableDataRef outputData
= NULL
;
127 const char *pemHeader
= "UNKNOWN";
129 /* convert keychainItemOrArray to CFArray of SecExportReps */
130 CFMutableArrayRef exportReps
= CFArrayCreateMutable(NULL
, 0, NULL
);
131 /* subsequent errors to errOut: */
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
);
143 impExpAddToExportReps(keychainItemOrArray
, exportReps
, numCerts
, numKeys
);
146 catch(const Security::MacOSError osErr
) {
154 numTotalExports
= CFArrayGetCount(exportReps
);
155 assert((numCerts
+ numKeys
) == numTotalExports
);
156 if((numTotalExports
> 1) && (outputFormat
== kSecFormatUnknown
)) {
157 /* default aggregate format is PEM sequence */
158 outputFormat
= kSecFormatPEMSequence
;
162 * Break out to SecExternalFormat-specific code, appending all data to outputData
164 outputData
= CFDataCreateMutable(NULL
, 0);
165 switch(outputFormat
) {
166 case kSecFormatPKCS7
:
167 ortn
= impExpPkcs7Export(exportReps
, flags
, keyParams
, outputData
);
168 pemHeader
= PEM_STRING_PKCS7
;
170 case kSecFormatPKCS12
:
171 ortn
= impExpPkcs12Export(exportReps
, flags
, keyParams
, outputData
);
172 pemHeader
= PEM_STRING_PKCS12
;
174 case kSecFormatPEMSequence
:
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
182 CFIndex numReps
= CFArrayGetCount(exportReps
);
183 for(CFIndex dex
=0; dex
<numReps
; dex
++) {
185 rep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, dex
);
187 /* default DER encoding */
188 CFMutableDataRef tmpData
= CFDataCreateMutable(NULL
, 0);
189 ortn
= rep
->exportRep(kSecFormatUnknown
, flags
, keyParams
,
190 tmpData
, &pemHeader
);
192 SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData
);
197 /* PEM to accumulating output */
198 assert(rep
->pemParamLines() == NULL
);
199 ortn
= impExpPemEncodeExportRep((CFDataRef
)tmpData
,
200 pemHeader
, NULL
, /* no pemParamLines, right? */
210 /* Enumerate remainder explicitly for clarity; all are single-item forms */
211 case kSecFormatOpenSSL
:
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
223 unsigned foundCount
= 0;
225 /* verify that we have exactly one of specified item */
226 if(outputFormat
== kSecFormatX509Cert
) {
227 foundCount
= numCerts
;
229 else if(outputFormat
== kSecFormatUnknown
) {
231 foundCount
= numTotalExports
;
234 foundCount
= numKeys
;
236 if((numTotalExports
!= 1) || (foundCount
!= 1)) {
237 SecImpExpDbg("Export single item format with other than one item");
241 assert(CFArrayGetCount(exportReps
) == 1);
242 rep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, 0);
243 ortn
= rep
->exportRep(outputFormat
, flags
,
244 keyParams
, outputData
, &pemHeader
);
248 SecImpExpDbg("SecKeychainItemExport: bad format (%u)",
249 (unsigned)outputFormat
);
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).
261 if(outputFormat
== kSecFormatPEMSequence
) {
262 *exportedData
= outputData
;
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
274 *exportedData
= tmpData
; // caller gets PEM
277 *exportedData
= outputData
;
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
);
290 CFRelease(exportReps
);
292 if(outputData
!= NULL
) {
293 CFRelease(outputData
);
297 return SecKeychainErrFromOSStatus(ortn
);
307 OSStatus
SecItemExport(CFTypeRef secItemOrArray
, SecExternalFormat outputFormat
,
308 SecItemImportExportFlags flags
, /* kSecItemPemArmor, etc. */
309 const SecItemImportExportKeyParameters
*keyParams
, /* optional */
310 CFDataRef
*exportedData
)
312 SecKeyImportExportParameters
* oldStructPtr
= NULL
;
313 SecKeyImportExportParameters oldStruct
;
314 memset(&oldStruct
, 0, sizeof(oldStruct
));
316 if (NULL
!= keyParams
)
319 SecKeyRef tempKey
= NULL
;
321 if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray
))
323 tempKey
= (SecKeyRef
)secItemOrArray
;
326 if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey
,
327 keyParams
, &oldStruct
))
329 oldStructPtr
= &oldStruct
;
333 return SecKeychainItemExport(secItemOrArray
, outputFormat
, flags
, oldStructPtr
, exportedData
);