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 * SecImport.cpp - high-level facility for importing Sec layer objects.
26 #include "SecImportExport.h"
27 #include "SecExternalRep.h"
28 #include "SecImportExportPem.h"
29 #include "SecImportExportUtils.h"
30 #include <security_cdsa_utils/cuCdsaUtils.h>
31 #include <security_utilities/globalizer.h>
33 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
35 #define SecImpInferDbg(args...) secdebug("SecImpInfer", ## args)
37 using namespace Security
;
38 using namespace KeychainCore
;
41 * Do our best to ensure that a SecImportRep's type and format are known.
42 * A return of true means that both format and type (and, if the item
43 * is a raw public or private key, the algorithm) are known.
45 static bool impExpInferTypeAndFormat(
48 SecExternalFormat inputFormat
,
49 SecExternalItemType itemType
)
51 /* fill in blanks if caller knows them */
52 if((rep
->mExternType
== kSecItemTypeUnknown
) && (itemType
!= kSecItemTypeUnknown
)) {
53 rep
->mExternType
= itemType
;
55 if((rep
->mExternFormat
== kSecFormatUnknown
) && (inputFormat
!= kSecFormatUnknown
)) {
56 rep
->mExternFormat
= inputFormat
;
59 /* some types can be inferred from format */
60 if(rep
->mExternType
== kSecItemTypeUnknown
) {
61 SecExternalFormat format
;
62 if(rep
->mExternFormat
== kSecFormatUnknown
) {
63 /* caller specified */
67 /* maybe this is already set */
68 format
= rep
->mExternFormat
;
71 case kSecFormatUnknown
:
74 case kSecFormatPKCS12
:
75 case kSecFormatPEMSequence
:
76 case kSecFormatNetscapeCertSequence
:
77 rep
->mExternType
= kSecItemTypeAggregate
;
79 case kSecFormatRawKey
:
80 rep
->mExternType
= kSecItemTypeSessionKey
;
82 case kSecFormatX509Cert
:
83 rep
->mExternType
= kSecItemTypeCertificate
;
85 case kSecFormatWrappedPKCS8
:
86 case kSecFormatWrappedOpenSSL
:
87 case kSecFormatWrappedSSH
:
88 rep
->mExternType
= kSecItemTypePrivateKey
;
91 rep
->mExternType
= kSecItemTypePublicKey
;
93 case kSecFormatOpenSSL
:
95 case kSecFormatWrappedLSH
:
97 /* can be private or session (right? */
102 /* some formats can be inferred from type */
103 if(rep
->mExternFormat
== kSecFormatUnknown
) {
104 SecExternalItemType thisType
;
105 if(rep
->mExternType
== kSecItemTypeUnknown
) {
106 /* caller specified */
110 /* maybe this is already set */
111 thisType
= rep
->mExternType
;
114 case kSecItemTypeCertificate
:
115 rep
->mExternFormat
= kSecFormatX509Cert
;
124 * Wrapped private keys don't need algorithm
125 * Some formats implies algorithm
127 bool isWrapped
= false;
128 switch(rep
->mExternFormat
) {
129 case kSecFormatWrappedPKCS8
:
130 case kSecFormatWrappedOpenSSL
:
131 case kSecFormatWrappedLSH
:
134 case kSecFormatWrappedSSH
:
136 rep
->mKeyAlg
= CSSM_ALGID_RSA
;
139 rep
->mKeyAlg
= CSSM_ALGID_RSA
;
145 /* Are we there yet? */
147 if((rep
->mExternType
== kSecItemTypeUnknown
) ||
148 (rep
->mExternFormat
== kSecFormatUnknown
)) {
152 switch(rep
->mExternType
) {
153 case kSecItemTypePrivateKey
:
154 case kSecItemTypePublicKey
:
155 if(!isWrapped
&& (rep
->mKeyAlg
== CSSM_ALGID_NONE
)) {
156 /* gotta know this too */
165 /* infer from filename if possible */
166 done
= impExpImportParseFileExten(fileStr
, &rep
->mExternFormat
,
173 /* invoke black magic: try decoding various forms */
174 return impExpImportGuessByExamination(rep
->mExternal
, &rep
->mExternFormat
,
175 &rep
->mExternType
, &rep
->mKeyAlg
);
181 CSSM_CSP_HANDLE mHandle
;
184 CSPDLMaker() : mHandle(cuCspStartup(CSSM_FALSE
)) {}
185 operator CSSM_CSP_HANDLE() {return mHandle
;}
188 static ModuleNexus
<CSPDLMaker
> gCSPHandle
;
190 OSStatus
SecKeychainItemImport(
191 CFDataRef importedData
,
192 CFStringRef fileNameOrExtension
, // optional
193 SecExternalFormat
*inputFormat
, // optional, IN/OUT
194 SecExternalItemType
*itemType
, // optional, IN/OUT
195 SecItemImportExportFlags flags
,
196 const SecKeyImportExportParameters
*keyParams
, // optional
197 SecKeychainRef importKeychain
, // optional
198 CFArrayRef
*outItems
) /* optional */
203 OSStatus ortn
= noErr
;
204 OSStatus pem_ortn
= noErr
;
205 SecImportRep
*rep
= NULL
;
206 SecExternalFormat callerInputFormat
;
207 SecExternalItemType callerItemType
;
208 CSSM_CSP_HANDLE cspHand
= 0;
210 CFStringRef ourFileStr
= NULL
;
212 if((importedData
== NULL
) || (CFDataGetLength(importedData
) == 0)) {
215 /* all other args are optional */
218 callerInputFormat
= *inputFormat
;
221 callerInputFormat
= kSecFormatUnknown
;
224 callerItemType
= *itemType
;
227 callerItemType
= kSecItemTypeUnknown
;
231 SecExternalFormat tempFormat
= callerInputFormat
;
232 SecExternalItemType tempType
= callerItemType
;
233 ImpPrivKeyImportState keyImportState
= PIS_NoLimit
;
235 CFMutableArrayRef importReps
= CFArrayCreateMutable(NULL
, 0, NULL
);
236 CFMutableArrayRef createdKcItems
= CFArrayCreateMutable(NULL
, 0,
237 &kCFTypeArrayCallBacks
);
238 /* subsequent errors to errOut: */
241 * importedData --> one or more SecImportReps.
242 * Note successful PEM decode can override caller's inputFormat and/or itemType.
244 pem_ortn
= impExpParsePemToImportRefs(importedData
, importReps
, &isPem
);
245 /* remember how PEM decode failed, but continue to examine other possibilities */
247 /* incoming blob is one binary item, type possibly unknown */
248 rep
= new SecImportRep(importedData
, callerItemType
, callerInputFormat
,
250 CFArrayAppendValue(importReps
, rep
);
251 if(fileNameOrExtension
) {
252 ourFileStr
= fileNameOrExtension
;
253 CFRetain(ourFileStr
);
258 * Strip off possible .pem extension in case there's another one in
261 assert(CFArrayGetCount(importReps
) >= 1);
262 if(fileNameOrExtension
) {
263 if(CFStringHasSuffix(fileNameOrExtension
, CFSTR(".pem"))) {
264 ourFileStr
= impExpImportDeleteExtension(fileNameOrExtension
);
267 ourFileStr
= fileNameOrExtension
;
268 CFRetain(ourFileStr
);
274 * Ensure we know type and format (and, for raw keys, algorithm) of each item.
276 numReps
= CFArrayGetCount(importReps
);
279 * Incoming kSecFormatPEMSequence, caller specs are useless now.
280 * Hopefully the PEM parsing disclosed the info we'll need.
283 CFRelease(ourFileStr
);
286 tempFormat
= kSecFormatUnknown
;
287 tempType
= kSecItemTypeUnknown
;
289 for(dex
=0; dex
<numReps
; dex
++) {
290 rep
= (SecImportRep
*)CFArrayGetValueAtIndex(importReps
, dex
);
291 bool ok
= impExpInferTypeAndFormat(rep
, ourFileStr
, tempFormat
, tempType
);
293 ortn
= errSecUnknownFormat
;
298 /* Get a CSPDL handle, somehow, as convenience for lower level code */
299 if(importKeychain
!= NULL
) {
300 ortn
= SecKeychainGetCSPHandle(importKeychain
, &cspHand
);
306 cspHand
= gCSPHandle();
309 if(keyParams
&& (keyParams
->flags
& kSecKeyImportOnlyOne
)) {
310 keyImportState
= PIS_AllowOne
;
313 /* Everything looks good: Go */
314 for(CFIndex dex
=0; dex
<numReps
; dex
++) {
315 rep
= (SecImportRep
*)CFArrayGetValueAtIndex(importReps
, dex
);
316 ortn
= rep
->importRep(importKeychain
, cspHand
, flags
, keyParams
,
317 keyImportState
, createdKcItems
);
323 /* Give as much info to caller as we can even if we got an error on import */
324 if(inputFormat
!= NULL
) {
327 *inputFormat
= kSecFormatPEMSequence
;
330 /* format from sole item in importReps */
331 assert(numReps
!= 0);
332 rep
= (SecImportRep
*)CFArrayGetValueAtIndex(importReps
, 0);
333 *inputFormat
= rep
->mExternFormat
;
336 if(itemType
!= NULL
) {
339 *itemType
= kSecItemTypeAggregate
;
342 /* itemType from sole item in importReps */
343 assert(numReps
!= 0);
344 rep
= (SecImportRep
*)CFArrayGetValueAtIndex(importReps
, 0);
345 *itemType
= rep
->mExternType
;
348 if((ortn
== noErr
) && (outItems
!= NULL
)) {
349 /* return the array */
350 *outItems
= createdKcItems
;
351 createdKcItems
= NULL
;
353 /* else caller doesn't want SecKeychainItemsRefs; we'll release below */
357 CFRelease(createdKcItems
);
359 if(importReps
!= NULL
) {
360 /* CFArray of our own classes, no auto release */
361 CFIndex num
= CFArrayGetCount(importReps
);
362 for(dex
=0; dex
<num
; dex
++) {
363 rep
= (SecImportRep
*)CFArrayGetValueAtIndex(importReps
, dex
);
366 CFRelease(importReps
);
369 CFRelease(ourFileStr
);
372 /* error occurred importing non-PEM representation */
373 return SecKeychainErrFromOSStatus(ortn
);
375 if(pem_ortn
== errSecUnsupportedFormat
&& numReps
== 0) {
376 /* error occurred importing as PEM, and no other rep was imported */
377 return SecKeychainErrFromOSStatus(pem_ortn
);
384 OSStatus
SecItemImport(
385 CFDataRef importedData
,
386 CFStringRef fileNameOrExtension
, /* optional */
387 SecExternalFormat
*inputFormat
, /* optional, IN/OUT */
388 SecExternalItemType
*itemType
, /* optional, IN/OUT */
389 SecItemImportExportFlags flags
,
390 const SecItemImportExportKeyParameters
*keyParams
, /* optional */
391 SecKeychainRef importKeychain
, /* optional */
392 CFArrayRef
*outItems
)
395 SecKeyImportExportParameters
* oldStructPtr
= NULL
;
396 SecKeyImportExportParameters oldStruct
;
397 memset(&oldStruct
, 0, sizeof(oldStruct
));
400 if (NULL
!= keyParams
)
402 if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(NULL
,
403 keyParams
, &oldStruct
))
405 oldStructPtr
= &oldStruct
;
409 return SecKeychainItemImport(importedData
, fileNameOrExtension
, inputFormat
,
410 itemType
, flags
, oldStructPtr
, importKeychain
, outItems
);