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 * SecExternalRep.cpp - private class representing an external representation of
24 * a SecKeychainItemRef, used by SecImportExport.h
27 #include "SecExternalRep.h"
28 #include "SecImportExportPem.h"
29 #include "SecImportExportAgg.h"
30 #include "SecImportExportUtils.h"
31 #include "SecImportExportPkcs8.h"
32 #include "SecImportExportCrypto.h"
33 #include "SecImportExportOpenSSH.h"
34 #include <security_utilities/errors.h>
35 #include <Security/SecBase.h>
36 #include <Security/SecKeyPriv.h>
37 #include <Security/SecCertificate.h>
38 #include <Security/cssmapi.h>
40 using namespace Security
;
41 using namespace KeychainCore
;
44 #pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() ---
48 class Key
: public SecExportRep
50 friend class SecExportRep
;
56 SecExternalFormat format
,
57 SecItemImportExportFlags flags
,
58 const SecKeyImportExportParameters
*keyParams
, // optional
59 CFMutableDataRef outData
, // data appended here
60 const char **pemHeader
); // e.g., "RSA PUBLIC KEY"
63 CSSM_ALGORITHMS mKeyAlg
;
64 const CSSM_KEY
*mCssmKey
;
67 class Cert
: public SecExportRep
69 friend class SecExportRep
;
75 SecExternalFormat format
,
76 SecItemImportExportFlags flags
,
77 const SecKeyImportExportParameters
*keyParams
, // optional
78 CFMutableDataRef outData
, // data appended here
79 const char **pemHeader
); // e.g., "CERTIFICATE"
82 } /* namespace SecExport */
84 #pragma mark --- SecExportRep: Representation of an internal object on export ---
86 SecExportRep::SecExportRep(
87 CFTypeRef kcItemRef
) :
88 mKcItem((SecKeychainItemRef
)kcItemRef
),
94 SecExportRep::~SecExportRep()
100 CFRelease(mPemParamLines
);
104 SecExportRep::SecExportRep() {
105 MacOSError::throwMe(errSecInvalidItemRef
);
108 /* must be implemented by subclass */
109 OSStatus
SecExportRep::exportRep(
110 SecExternalFormat format
,
111 SecItemImportExportFlags flags
,
112 const SecKeyImportExportParameters
*keyParams
, // optional
113 CFMutableDataRef outData
, // data appended here
114 const char **pemHeader
) // e.g., "X509 CERTIFICATE"
116 MacOSError::throwMe(errSecInvalidItemRef
);
120 * Sole public means of obtaining a SecExportRep object. In fact only instances
121 * of subclasses are vended but caller does not know that.
123 * Gleans SecExternalItemType from incoming type, throws MacOSError if
124 * incoming type is bogus.
126 * Vended object holds a reference to kcItem for its lifetime.
128 SecExportRep
*SecExportRep::vend(
131 CFTypeID itemType
= CFGetTypeID(kcItemRef
);
132 if(itemType
== SecCertificateGetTypeID()) {
133 return new SecExport::Cert(kcItemRef
);
135 else if(itemType
== SecKeyGetTypeID()) {
136 return new SecExport::Key(kcItemRef
);
139 MacOSError::throwMe(errSecInvalidItemRef
);
143 #pragma mark --- Key External rep ---
146 CFTypeRef kcItemRef
) :
147 SecExportRep(kcItemRef
)
150 /* figure out if it's public, private, or session */
152 ortn
= SecKeyGetCSSMKey((SecKeyRef
)kcItemRef
, &mCssmKey
);
154 SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()");
155 MacOSError::throwMe(ortn
);
157 switch(mCssmKey
->KeyHeader
.KeyClass
) {
158 case CSSM_KEYCLASS_PUBLIC_KEY
:
159 mExternType
= kSecItemTypePublicKey
;
160 SecImpExpDbg("SecExportRep::Key(): SET_PubKey");
162 case CSSM_KEYCLASS_PRIVATE_KEY
:
163 mExternType
= kSecItemTypePrivateKey
;
164 SecImpExpDbg("SecExportRep::Key(): SET_PrivKey");
166 case CSSM_KEYCLASS_SESSION_KEY
:
167 mExternType
= kSecItemTypeSessionKey
;
168 SecImpExpDbg("SecExportRep::Key(): SET_SessionKey");
171 SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)",
172 (unsigned long)mCssmKey
->KeyHeader
.KeyClass
);
173 MacOSError::throwMe(errSecInvalidItemRef
);
175 mKeyAlg
= mCssmKey
->KeyHeader
.AlgorithmId
;
178 SecExport::Key::~Key()
180 /* nothing for now */
184 * The heart of this class: cook up external representation, appending to
185 * existing CFMutableDataRef.
187 OSStatus
SecExport::Key::exportRep(
188 SecExternalFormat format
,
189 SecItemImportExportFlags flags
,
190 const SecKeyImportExportParameters
*keyParams
, // optional
191 CFMutableDataRef outData
, // data appended here
192 const char **pemHeader
)// e.g., "X509 CERTIFICATE"
194 assert(outData
!= NULL
);
195 assert(mKcItem
!= NULL
);
196 assert(mCssmKey
!= NULL
);
199 * Currently only OpsnSSH formats allow for a DescriptiveData field
200 * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is
201 * the 'comment' field). Infer the DescriptiveData to be embedded
202 * in the exported key from the item's PrintName attribute.
204 CssmAutoData
descrData(Allocator::standard());
207 case kSecFormatSSHv2
:
208 case kSecFormatWrappedSSH
:
209 impExpOpensshInferDescData((SecKeyRef
)mKcItem
, descrData
);
216 * Handle wrapped key formats.
219 case kSecFormatWrappedPKCS8
:
220 return impExpPkcs8Export((SecKeyRef
)mKcItem
, flags
, keyParams
,
222 case kSecFormatWrappedOpenSSL
:
223 return impExpWrappedKeyOpenSslExport((SecKeyRef
)mKcItem
, flags
, keyParams
,
224 outData
, pemHeader
, &mPemParamLines
);
225 case kSecFormatWrappedSSH
:
226 return impExpWrappedOpenSSHExport((SecKeyRef
)mKcItem
, flags
, keyParams
,
228 case kSecFormatWrappedLSH
:
229 return errSecUnsupportedFormat
;
235 * Remaining formats just do a NULL key wrap. Figure out the appropriate
236 * CDSA-specific format and wrap parameters.
238 OSStatus ortn
= errSecSuccess
;
239 CSSM_KEYBLOB_FORMAT blobForm
;
241 switch(mExternType
) {
242 case kSecItemTypePublicKey
:
245 *pemHeader
= PEM_STRING_RSA_PUBLIC
;
248 *pemHeader
= PEM_STRING_DH_PUBLIC
;
251 *pemHeader
= PEM_STRING_DSA_PUBLIC
;
253 case CSSM_ALGID_ECDSA
:
254 *pemHeader
= PEM_STRING_ECDSA_PUBLIC
;
257 SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu",
258 (unsigned long)mKeyAlg
);
259 return errSecUnsupportedFormat
;
260 } /* end switch(mKeyAlg) */
261 break; /* from case externType kSecItemTypePublicKey */
263 case kSecItemTypePrivateKey
:
266 *pemHeader
= PEM_STRING_RSA
;
269 *pemHeader
= PEM_STRING_DH_PRIVATE
;
272 *pemHeader
= PEM_STRING_DSA
;
274 case CSSM_ALGID_ECDSA
:
275 *pemHeader
= PEM_STRING_ECDSA_PRIVATE
;
278 SecImpExpDbg("SecExportRep::exportRep unknown private key alg "
279 "%lu", (unsigned long)mKeyAlg
);
280 return errSecUnsupportedFormat
;
281 } /* end switch(mKeyAlg) */
282 break; /* from case externType kSecItemTypePrivateKey */
284 case kSecItemTypeSessionKey
:
285 *pemHeader
= PEM_STRING_SESSION
;
289 return errSecInvalidItemRef
;
290 } /* switch(mExternType) */
292 /* Map our external params to CDSA blob format */
293 CSSM_KEYCLASS keyClass
;
294 ortn
= impExpKeyForm(format
, mExternType
, mKeyAlg
, &blobForm
, &keyClass
);
299 /* Specify format of null-wrapped key */
300 CSSM_ATTRIBUTE_TYPE formatAttrType
= CSSM_ATTRIBUTE_NONE
;
301 switch(mExternType
) {
302 case kSecItemTypePrivateKey
:
303 formatAttrType
= CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT
;
305 case kSecItemTypePublicKey
:
306 formatAttrType
= CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT
;
308 /* symmetric key doesn't have a format */
313 CSSM_CSP_HANDLE cspHand
;
314 ortn
= SecKeyGetCSPHandle((SecKeyRef
)mKcItem
, &cspHand
);
316 SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error");
320 /* perform the NULL wrap --> wrapped Key */
322 memset(&wrappedKey
, 0, sizeof(wrappedKey
));
323 const CSSM_DATA
&dd
= descrData
;
324 ortn
= impExpExportKeyCommon(cspHand
,
326 NULL
, // wrappingKey not used for NULL
327 &wrappedKey
, // destination
331 CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
,
334 &dd
, // descriptiveData
337 if(ortn
== CSSM_OK
) {
338 /* pass key data back to caller */
339 CFDataAppendBytes(outData
, wrappedKey
.KeyData
.Data
, wrappedKey
.KeyData
.Length
);
341 CSSM_FreeKey(cspHand
, NULL
, &wrappedKey
, CSSM_FALSE
);
345 #pragma mark --- Certificate External rep ---
347 SecExport::Cert::Cert(
348 CFTypeRef kcItemRef
) :
349 SecExportRep(kcItemRef
)
351 mExternType
= kSecItemTypeCertificate
;
354 SecExport::Cert::~Cert()
356 /* nothing for now */
360 * The heart of this class: cook up external representation, appending to
361 * existing CFMutableDataRef.
363 OSStatus
SecExport::Cert::exportRep(
364 SecExternalFormat format
,
365 SecItemImportExportFlags flags
,
366 const SecKeyImportExportParameters
*keyParams
, // optional
367 CFMutableDataRef outData
, // data appended here
368 const char **pemHeader
)// e.g., "X509 CERTIFICATE"
370 assert(outData
!= NULL
);
371 assert(mKcItem
!= NULL
);
374 case kSecFormatUnknown
: // default
375 case kSecFormatX509Cert
: // currently, only supported format
378 SecImpExpDbg("SecExportRep::exportRep unsupported format for cert");
379 return errSecUnsupportedFormat
;
382 CFDataRef cdata
= SecCertificateCopyData((SecCertificateRef
)mKcItem
);
384 SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error");
385 return errSecUnsupportedFormat
;
388 CFDataAppendBytes(outData
, CFDataGetBytePtr(cdata
), CFDataGetLength(cdata
));
390 *pemHeader
= PEM_STRING_X509
;
391 return errSecSuccess
;
394 #pragma mark --- SecImportRep: Representation of an external object on import ---
397 * for import, when we have the external representation.
398 * All arguments except for the CFDataRef are optional (i.e., "unknown"
401 SecImportRep::SecImportRep(
403 SecExternalItemType externType
, // may be unknown
404 SecExternalFormat externFormat
, // may be unknown
405 CSSM_ALGORITHMS keyAlg
, // may be unknown, CSSM_ALGID_NONE
406 CFArrayRef pemParamLines
/* = NULL */ ) :
409 mExternType(externType
),
410 mExternFormat(externFormat
),
412 mPemParamLines(pemParamLines
)
417 SecImportRep::~SecImportRep()
423 CFRelease(mExternal
);
426 CFRelease(mPemParamLines
);
431 * Convert to one or more SecItemRefs and/or import to keychain.
432 * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle.
434 OSStatus
SecImportRep::importRep(
435 SecKeychainRef importKeychain
, // optional
436 CSSM_CSP_HANDLE cspHand
, // required
437 SecItemImportExportFlags flags
,
438 const SecKeyImportExportParameters
*keyParams
, // optional
439 ImpPrivKeyImportState
&keyImportState
, // IN/OUT
440 CFMutableArrayRef outArray
) // optional, append here
442 /* caller must have sorted this out by now */
443 assert((mExternType
!= kSecItemTypeUnknown
) &&
444 (mExternFormat
!= kSecFormatUnknown
));
446 /* app could conceivably botch these with crafty PEM hacking */
447 if((mExternal
== NULL
) || (CFDataGetLength(mExternal
) == 0)) {
451 /* handle the easy ones first */
452 switch(mExternFormat
) {
453 case kSecFormatPKCS12
:
454 return impExpPkcs12Import(mExternal
, flags
, keyParams
,
455 keyImportState
, importKeychain
, cspHand
, outArray
);
456 case kSecFormatX509Cert
:
457 case kSecFormatPKCS7
:
459 OSStatus rx
= impExpPkcs7Import(mExternal
, flags
, keyParams
, importKeychain
,
461 if (rx
== errSecUnknownFormat
)
464 cdata
.Data
= (uint8
*)CFDataGetBytePtr(mExternal
);
465 cdata
.Length
= (CSSM_SIZE
)CFDataGetLength(mExternal
);
466 return impExpImportCertCommon(&cdata
, importKeychain
, outArray
);
470 case kSecFormatNetscapeCertSequence
:
471 return impExpNetscapeCertImport(mExternal
, flags
, keyParams
, importKeychain
,
477 if((mExternType
== kSecItemTypeCertificate
) ||
478 (mExternType
== kSecItemTypeAggregate
)) {
479 SecImpExpDbg("SecImportRep::importRep screwup");
480 return errSecUnimplemented
;
484 * All that's left: keys.
486 if((mExternType
== kSecItemTypePrivateKey
) && (keyImportState
== PIS_NoMore
)) {
487 /* multi key import against caller's wishes */
488 return errSecMultiplePrivKeys
;
491 /* optionally infer PrintName attribute */
492 switch(mExternFormat
) {
494 case kSecFormatWrappedSSH
:
495 case kSecFormatSSHv2
:
496 mPrintName
= impExpOpensshInferPrintName(mExternal
, mExternType
, mExternFormat
);
503 OSStatus ortn
= errSecSuccess
;
505 switch(mExternFormat
) {
506 case kSecFormatOpenSSL
:
508 case kSecFormatSSHv2
:
509 case kSecFormatBSAFE
:
510 case kSecFormatRawKey
:
511 ortn
= impExpImportRawKey(mExternal
, mExternFormat
, mExternType
,
512 mKeyAlg
, importKeychain
, cspHand
, flags
, keyParams
, mPrintName
, outArray
);
514 case kSecFormatWrappedPKCS8
:
515 ortn
= impExpPkcs8Import(mExternal
, importKeychain
, cspHand
, flags
,
516 keyParams
, outArray
);
518 case kSecFormatWrappedOpenSSL
:
519 ortn
= importWrappedKeyOpenssl(importKeychain
, cspHand
, flags
, keyParams
,
522 case kSecFormatWrappedSSH
:
523 ortn
= impExpWrappedOpenSSHImport(mExternal
, importKeychain
, cspHand
,
524 flags
, keyParams
, mPrintName
, outArray
);
526 case kSecFormatWrappedLSH
:
528 return errSecUnknownFormat
;
530 if((ortn
== errSecSuccess
) && (keyImportState
== PIS_AllowOne
)) {
531 /* reached our limit */
532 keyImportState
= PIS_NoMore
;