2 * Copyright (c) 2003-2004,2006,2012,2014 Apple 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@
26 #include "keychain_export.h"
27 #include "keychain_utilities.h"
33 #include <Security/SecImportExport.h>
34 #include <Security/SecKeychainItem.h>
35 #include <Security/SecKeychainSearch.h>
36 #include <Security/SecIdentitySearch.h>
37 #include <Security/SecKey.h>
38 #include <Security/SecCertificate.h>
39 #include <security_cdsa_utils/cuFileIo.h>
40 #include <CoreFoundation/CoreFoundation.h>
53 * Add all itmes of specified class from a keychain to an array.
54 * Item class are things like kSecCertificateItemClass, and
55 * CSSM_DL_DB_RECORD_PRIVATE_KEY. Identities are searched separately.
57 static OSStatus
addKcItems(
59 SecItemClass itemClass
, // kSecCertificateItemClass
60 CFMutableArrayRef outArray
,
61 unsigned *numItems
) // UPDATED on return
64 SecKeychainSearchRef srchRef
;
66 ortn
= SecKeychainSearchCreateFromAttributes(kcRef
,
71 sec_perror("SecKeychainSearchCreateFromAttributes", ortn
);
75 SecKeychainItemRef itemRef
;
76 ortn
= SecKeychainSearchCopyNext(srchRef
, &itemRef
);
78 if(ortn
== errSecItemNotFound
) {
79 /* normal search end */
83 sec_perror("SecIdentitySearchCopyNext", ortn
);
87 CFArrayAppendValue(outArray
, itemRef
);
88 CFRelease(itemRef
); // array owns the item
96 * Add all SecIdentityRefs from a keychain into an array.
98 static OSStatus
addIdentities(
100 CFMutableArrayRef outArray
,
101 unsigned *numItems
) // UPDATED on return
103 /* Search for all identities */
104 SecIdentitySearchRef srchRef
;
105 OSStatus ortn
= SecIdentitySearchCreate(kcRef
,
109 sec_perror("SecIdentitySearchCreate", ortn
);
114 SecIdentityRef identity
;
115 ortn
= SecIdentitySearchCopyNext(srchRef
, &identity
);
117 if(ortn
== errSecItemNotFound
) {
118 /* normal search end */
122 sec_perror("SecIdentitySearchCopyNext", ortn
);
126 CFArrayAppendValue(outArray
, identity
);
128 /* the array has the retain count we need */
131 } while(ortn
== noErr
);
136 static int do_keychain_export(
137 SecKeychainRef kcRef
,
138 SecExternalFormat externFormat
,
140 const char *passphrase
,
142 const char *fileName
)
146 unsigned numPrivKeys
= 0;
147 unsigned numPubKeys
= 0;
148 unsigned numCerts
= 0;
149 unsigned numIdents
= 0;
151 uint32 expFlags
= 0; // SecItemImportExportFlags
152 SecKeyImportExportParameters keyParams
;
153 CFStringRef passStr
= NULL
;
154 CFDataRef outData
= NULL
;
158 CFMutableArrayRef exportItems
= CFArrayCreateMutable(NULL
, 0,
159 &kCFTypeArrayCallBacks
);
162 ortn
= addKcItems(kcRef
, kSecCertificateItemClass
, exportItems
, &numCerts
);
170 ortn
= addKcItems(kcRef
, CSSM_DL_DB_RECORD_PRIVATE_KEY
, exportItems
,
179 ortn
= addKcItems(kcRef
, CSSM_DL_DB_RECORD_PUBLIC_KEY
, exportItems
,
188 ortn
= addKcItems(kcRef
, CSSM_DL_DB_RECORD_PRIVATE_KEY
, exportItems
,
194 ortn
= addKcItems(kcRef
, CSSM_DL_DB_RECORD_PUBLIC_KEY
, exportItems
,
203 /* No public keys here - PKCS12 doesn't support them */
204 ortn
= addKcItems(kcRef
, kSecCertificateItemClass
, exportItems
, &numCerts
);
209 ortn
= addKcItems(kcRef
, CSSM_DL_DB_RECORD_PRIVATE_KEY
, exportItems
,
218 ortn
= addIdentities(kcRef
, exportItems
, &numIdents
);
224 numPrivKeys
+= numIdents
;
225 numCerts
+= numIdents
;
229 sec_error("Internal error parsing item_spec");
234 numItems
= CFArrayGetCount(exportItems
);
235 if(externFormat
== kSecFormatUnknown
) {
236 /* Use default export format per set of items */
238 externFormat
= kSecFormatPEMSequence
;
241 externFormat
= kSecFormatX509Cert
;
244 externFormat
= kSecFormatOpenSSL
;
248 expFlags
|= kSecItemPemArmour
;
252 * Key related arguments, ignored if we're not exporting keys.
253 * Always specify some kind of passphrase - default is secure passkey.
255 memset(&keyParams
, 0, sizeof(keyParams
));
256 keyParams
.version
= SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION
;
257 if(passphrase
!= NULL
) {
258 passStr
= CFStringCreateWithCString(NULL
, passphrase
, kCFStringEncodingASCII
);
259 keyParams
.passphrase
= passStr
;
262 keyParams
.flags
= kSecKeySecurePassphrase
;
266 ortn
= SecKeychainItemExport(exportItems
, externFormat
, expFlags
, &keyParams
,
269 sec_perror("SecKeychainItemExport", ortn
);
274 len
= CFDataGetLength(outData
);
276 int rtn
= writeFile(fileName
, CFDataGetBytePtr(outData
), len
);
279 fprintf(stderr
, "...%u bytes written to %s\n", len
, fileName
);
283 sec_error("Error writing to %s: %s", fileName
, strerror(errno
));
288 int irtn
= write(STDOUT_FILENO
, CFDataGetBytePtr(outData
), len
);
289 if(irtn
!= (int)len
) {
295 CFRelease(exportItems
);
307 keychain_export(int argc
, char * const *argv
)
311 char *outFile
= NULL
;
313 SecKeychainRef kcRef
= NULL
;
314 SecExternalFormat externFormat
= kSecFormatUnknown
;
315 ItemSpec itemSpec
= IS_All
;
318 const char *passphrase
= NULL
;
320 while ((ch
= getopt(argc
, argv
, "k:o:t:f:P:wph")) != -1)
331 if(!strcmp("certs", optarg
)) {
334 else if(!strcmp("allKeys", optarg
)) {
335 itemSpec
= IS_AllKeys
;
337 else if(!strcmp("pubKeys", optarg
)) {
338 itemSpec
= IS_PubKeys
;
340 else if(!strcmp("privKeys", optarg
)) {
341 itemSpec
= IS_PrivKeys
;
343 else if(!strcmp("identities", optarg
)) {
344 itemSpec
= IS_Identities
;
346 else if(!strcmp("all", optarg
)) {
350 return 2; /* @@@ Return 2 triggers usage message. */
354 if(!strcmp("openssl", optarg
)) {
355 externFormat
= kSecFormatOpenSSL
;
357 else if(!strcmp("openssh1", optarg
)) {
358 externFormat
= kSecFormatSSH
;
360 else if(!strcmp("openssh2", optarg
)) {
361 externFormat
= kSecFormatSSHv2
;
363 else if(!strcmp("bsafe", optarg
)) {
364 externFormat
= kSecFormatBSAFE
;
366 else if(!strcmp("raw", optarg
)) {
367 externFormat
= kSecFormatRawKey
;
369 else if(!strcmp("pkcs7", optarg
)) {
370 externFormat
= kSecFormatPKCS7
;
372 else if(!strcmp("pkcs8", optarg
)) {
373 externFormat
= kSecFormatWrappedPKCS8
;
375 else if(!strcmp("pkcs12", optarg
)) {
376 externFormat
= kSecFormatPKCS12
;
378 else if(!strcmp("netscape", optarg
)) {
379 externFormat
= kSecFormatNetscapeCertSequence
;
381 else if(!strcmp("x509", optarg
)) {
382 externFormat
= kSecFormatX509Cert
;
384 else if(!strcmp("pemseq", optarg
)) {
385 externFormat
= kSecFormatPEMSequence
;
388 return 2; /* @@@ Return 2 triggers usage message. */
402 return 2; /* @@@ Return 2 triggers usage message. */
407 switch(externFormat
) {
408 case kSecFormatOpenSSL
:
409 case kSecFormatUnknown
: // i.e., use default
410 externFormat
= kSecFormatWrappedOpenSSL
;
413 externFormat
= kSecFormatWrappedSSH
;
415 case kSecFormatSSHv2
:
416 /* there is no wrappedSSHv2 */
417 externFormat
= kSecFormatWrappedOpenSSL
;
419 case kSecFormatWrappedPKCS8
:
423 sec_error("Don't know how to wrap in specified format/type");
424 return 2; /* @@@ Return 2 triggers usage message. */
429 kcRef
= keychain_open(kcName
);
434 result
= do_keychain_export(kcRef
, externFormat
, itemSpec
,
435 passphrase
, doPem
, outFile
);