2 * Copyright (c) 2004,2011-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@
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>
32 #include <Security/SecBase.h>
34 #define SecImpInferDbg(args...) secdebug("SecImpInfer", ## args)
36 using namespace Security
;
37 using namespace KeychainCore
;
40 * Do our best to ensure that a SecImportRep's type and format are known.
41 * A return of true means that both format and type (and, if the item
42 * is a raw public or private key, the algorithm) are known.
44 static bool impExpInferTypeAndFormat(
47 SecExternalFormat inputFormat
,
48 SecExternalItemType itemType
)
50 /* fill in blanks if caller knows them */
51 if((rep
->mExternType
== kSecItemTypeUnknown
) && (itemType
!= kSecItemTypeUnknown
)) {
52 rep
->mExternType
= itemType
;
54 if((rep
->mExternFormat
== kSecFormatUnknown
) && (inputFormat
!= kSecFormatUnknown
)) {
55 rep
->mExternFormat
= inputFormat
;
58 /* some types can be inferred from format */
59 if(rep
->mExternType
== kSecItemTypeUnknown
) {
60 SecExternalFormat format
;
61 if(rep
->mExternFormat
== kSecFormatUnknown
) {
62 /* caller specified */
66 /* maybe this is already set */
67 format
= rep
->mExternFormat
;
70 case kSecFormatUnknown
:
73 case kSecFormatPKCS12
:
74 case kSecFormatPEMSequence
:
75 case kSecFormatNetscapeCertSequence
:
76 rep
->mExternType
= kSecItemTypeAggregate
;
78 case kSecFormatRawKey
:
79 rep
->mExternType
= kSecItemTypeSessionKey
;
81 case kSecFormatX509Cert
:
82 rep
->mExternType
= kSecItemTypeCertificate
;
84 case kSecFormatWrappedPKCS8
:
85 case kSecFormatWrappedOpenSSL
:
86 case kSecFormatWrappedSSH
:
87 rep
->mExternType
= kSecItemTypePrivateKey
;
90 rep
->mExternType
= kSecItemTypePublicKey
;
92 case kSecFormatOpenSSL
:
94 case kSecFormatWrappedLSH
:
96 /* can be private or session (right? */
101 /* some formats can be inferred from type */
102 if(rep
->mExternFormat
== kSecFormatUnknown
) {
103 SecExternalItemType thisType
;
104 if(rep
->mExternType
== kSecItemTypeUnknown
) {
105 /* caller specified */
109 /* maybe this is already set */
110 thisType
= rep
->mExternType
;
113 case kSecItemTypeCertificate
:
114 rep
->mExternFormat
= kSecFormatX509Cert
;
123 * Wrapped private keys don't need algorithm
124 * Some formats implies algorithm
126 bool isWrapped
= false;
127 switch(rep
->mExternFormat
) {
128 case kSecFormatWrappedPKCS8
:
129 case kSecFormatWrappedOpenSSL
:
130 case kSecFormatWrappedLSH
:
133 case kSecFormatWrappedSSH
:
135 rep
->mKeyAlg
= CSSM_ALGID_RSA
;
138 rep
->mKeyAlg
= CSSM_ALGID_RSA
;
144 /* Are we there yet? */
146 if((rep
->mExternType
== kSecItemTypeUnknown
) ||
147 (rep
->mExternFormat
== kSecFormatUnknown
)) {
151 switch(rep
->mExternType
) {
152 case kSecItemTypePrivateKey
:
153 case kSecItemTypePublicKey
:
154 if(!isWrapped
&& (rep
->mKeyAlg
== CSSM_ALGID_NONE
)) {
155 /* gotta know this too */
164 /* infer from filename if possible */
165 done
= impExpImportParseFileExten(fileStr
, &rep
->mExternFormat
,
172 /* invoke black magic: try decoding various forms */
173 return impExpImportGuessByExamination(rep
->mExternal
, &rep
->mExternFormat
,
174 &rep
->mExternType
, &rep
->mKeyAlg
);
180 CSSM_CSP_HANDLE mHandle
;
181 RecursiveMutex mMutex
;
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
= errSecSuccess
;
204 OSStatus pem_ortn
= errSecSuccess
;
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
== errSecSuccess
) && (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
);
379 return errSecSuccess
;
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
);