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 <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 using namespace Security
;
37 using namespace KeychainCore
;
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
43 * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef,
44 * SecCertRef, or SecIdentityRef.
46 static void impExpAddToExportReps(
47 CFTypeRef thing
, // Key, Cert, Identity
48 CFMutableArrayRef exportReps
,
49 unsigned &numCerts
, // IN/OUT - accumulated
50 unsigned &numKeys
) // IN/OUT - accumulated
52 if(CFGetTypeID(thing
) == SecIdentityGetTypeID()) {
53 /* special case for SecIdentities, creates two SecExportReps */
55 SecIdentityRef idRef
= (SecIdentityRef
)thing
;
56 SecCertificateRef certRef
;
61 SecImpExpDbg("impExpAddToExportReps: adding identity cert and key");
62 ortn
= SecIdentityCopyCertificate(idRef
, &certRef
);
64 Security::MacOSError::throwMe(ortn
);
66 rep
= SecExportRep::vend(certRef
);
67 CFArrayAppendValue(exportReps
, rep
);
68 CFRelease(certRef
); // SecExportRep holds a reference
72 ortn
= SecIdentityCopyPrivateKey(idRef
, &keyRef
);
74 Security::MacOSError::throwMe(ortn
);
76 rep
= SecExportRep::vend(keyRef
);
77 CFArrayAppendValue(exportReps
, rep
);
78 CFRelease(keyRef
); // SecExportRep holds a reference
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
) {
96 #pragma mark --- public export function ---
98 OSStatus
SecKeychainItemExport(
99 CFTypeRef keychainItemOrArray
,
100 SecExternalFormat outputFormat
, // a SecExternalFormat
101 SecItemImportExportFlags flags
, // kSecItemPemArmour, etc.
102 const SecKeyImportExportParameters
*keyParams
, // optional
103 CFDataRef
*exportedData
) // external representation
108 /* some basic input validation */
109 if(keychainItemOrArray
== NULL
) {
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)) {
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";
128 /* convert keychainItemOrArray to CFArray of SecExportReps */
129 CFMutableArrayRef exportReps
= CFArrayCreateMutable(NULL
, 0, NULL
);
130 /* subsequent errors to errOut: */
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
);
142 impExpAddToExportReps(keychainItemOrArray
, exportReps
, numCerts
, numKeys
);
145 catch(const Security::MacOSError osErr
) {
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
;
161 * Break out to SecExternalFormat-specific code, appending all data to outputData
163 outputData
= CFDataCreateMutable(NULL
, 0);
164 switch(outputFormat
) {
165 case kSecFormatPKCS7
:
166 ortn
= impExpPkcs7Export(exportReps
, flags
, keyParams
, outputData
);
167 pemHeader
= PEM_STRING_PKCS7
;
169 case kSecFormatPKCS12
:
170 ortn
= impExpPkcs12Export(exportReps
, flags
, keyParams
, outputData
);
171 pemHeader
= PEM_STRING_PKCS12
;
173 case kSecFormatPEMSequence
:
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
181 CFIndex numReps
= CFArrayGetCount(exportReps
);
182 for(CFIndex dex
=0; dex
<numReps
; dex
++) {
184 rep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, dex
);
186 /* default DER encoding */
187 CFMutableDataRef tmpData
= CFDataCreateMutable(NULL
, 0);
188 ortn
= rep
->exportRep(kSecFormatUnknown
, flags
, keyParams
,
189 tmpData
, &pemHeader
);
191 SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData
);
196 /* PEM to accumulating output */
197 assert(rep
->pemParamLines() == NULL
);
198 ortn
= impExpPemEncodeExportRep((CFDataRef
)tmpData
,
199 pemHeader
, NULL
, /* no pemParamLines, right? */
209 /* Enumerate remainder explicitly for clarity; all are single-item forms */
210 case kSecFormatOpenSSL
:
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
222 unsigned foundCount
= 0;
224 /* verify that we have exactly one of specified item */
225 if(outputFormat
== kSecFormatX509Cert
) {
226 foundCount
= numCerts
;
228 else if(outputFormat
== kSecFormatUnknown
) {
230 foundCount
= numTotalExports
;
233 foundCount
= numKeys
;
235 if((numTotalExports
!= 1) || (foundCount
!= 1)) {
236 SecImpExpDbg("Export single item format with other than one item");
240 assert(CFArrayGetCount(exportReps
) == 1);
241 rep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, 0);
242 ortn
= rep
->exportRep(outputFormat
, flags
,
243 keyParams
, outputData
, &pemHeader
);
247 SecImpExpDbg("SecKeychainItemExport: bad format (%u)",
248 (unsigned)outputFormat
);
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).
259 if(ortn
== errSecSuccess
) {
260 if(outputFormat
== kSecFormatPEMSequence
) {
261 *exportedData
= outputData
;
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
273 *exportedData
= tmpData
; // caller gets PEM
276 *exportedData
= outputData
;
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
);
289 CFRelease(exportReps
);
291 if(outputData
!= NULL
) {
292 CFRelease(outputData
);
296 return SecKeychainErrFromOSStatus(ortn
);
299 return errSecSuccess
;
306 OSStatus
SecItemExport(CFTypeRef secItemOrArray
, SecExternalFormat outputFormat
,
307 SecItemImportExportFlags flags
, /* kSecItemPemArmor, etc. */
308 const SecItemImportExportKeyParameters
*keyParams
, /* optional */
309 CFDataRef
*exportedData
)
311 SecKeyImportExportParameters
* oldStructPtr
= NULL
;
312 SecKeyImportExportParameters oldStruct
;
313 memset(&oldStruct
, 0, sizeof(oldStruct
));
315 if (NULL
!= keyParams
)
318 SecKeyRef tempKey
= NULL
;
320 if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray
))
322 tempKey
= (SecKeyRef
)secItemOrArray
;
325 if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey
,
326 keyParams
, &oldStruct
))
328 oldStructPtr
= &oldStruct
;
332 return SecKeychainItemExport(secItemOrArray
, outputFormat
, flags
, oldStructPtr
, exportedData
);