2 * Copyright (c) 2004,2011,2013-2015 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 * SecImportExportAgg.cpp - private routines used by SecImportExport.h for
24 * aggregate (PKCS12 and PKCS7) conversion.
27 #include "SecImportExportAgg.h"
28 #include "SecExternalRep.h"
29 #include "SecImportExportUtils.h"
30 #include "SecNetscapeTemplates.h"
31 #include "Certificate.h"
32 #include <security_pkcs12/SecPkcs12.h>
33 #include <Security/SecBase.h>
34 #include <Security/SecCmsDecoder.h>
35 #include <Security/SecCmsEncoder.h>
36 #include <Security/SecCmsMessage.h>
37 #include <Security/SecCmsContentInfo.h>
38 #include <Security/SecCmsSignedData.h>
39 #include <security_asn1/SecNssCoder.h>
40 #include <security_asn1/nssUtils.h>
41 #include <security_cdsa_utils/cuCdsaUtils.h>
42 #include <security_keychain/Globals.h>
43 #include <Security/SecCertificatePriv.h>
44 #include <Security/SecIdentityPriv.h>
45 #include <Security/SecKeyPriv.h>
47 using namespace Security
;
48 using namespace KeychainCore
;
50 #pragma mark --- Aggregate Export routines ---
52 OSStatus
impExpPkcs12Export(
53 CFArrayRef exportReps
, // SecExportReps
54 SecItemImportExportFlags flags
, // kSecItemPemArmour, etc.
55 const SecKeyImportExportParameters
*keyParams
, // optional
56 CFMutableDataRef outData
) // output appended here
58 SecPkcs12CoderRef p12Coder
;
59 OSStatus ortn
= errSecSuccess
;
60 CFMutableArrayRef exportItems
; // SecKeychainItemRefs
61 CFDataRef tmpData
= NULL
;
62 CSSM_CSP_HANDLE cspHand
= CSSM_INVALID_HANDLE
;
63 CSSM_KEY
*passKey
= NULL
;
64 CFStringRef phraseStr
= NULL
;
66 if( (keyParams
== NULL
) ||
67 ( (keyParams
->passphrase
== NULL
) &&
68 !(keyParams
->flags
& kSecKeySecurePassphrase
) ) ) {
69 /* passphrase mandatory */
70 return errSecPassphraseRequired
;
72 CFIndex numReps
= CFArrayGetCount(exportReps
);
74 SecImpExpDbg("impExpPkcs12Export: no items to export");
75 return errSecItemNotFound
;
79 * Build an array of SecKeychainItemRefs.
81 * Keychain is inferred from the objects to be exported. Some may be
82 * floating certs with no keychain.
84 exportItems
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
85 SecKeychainRef kcRef
= nil
;
86 for(CFIndex dex
=0; dex
<numReps
; dex
++) {
87 SecExportRep
*exportRep
=
88 (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, dex
);
89 SecKeychainItemRef kcItemRef
= (SecKeychainItemRef
)exportRep
->kcItem();
90 CFArrayAppendValue(exportItems
, kcItemRef
);
92 SecKeychainItemCopyKeychain(kcItemRef
, &kcRef
);
93 /* ignore error - we do this 'til we get a kcRef */
97 /* Set up a PKCS12 encoder */
98 ortn
= SecPkcs12CoderCreate(&p12Coder
);
102 /* subsequent errors to errOut: */
104 ortn
= SecPkcs12SetKeychain(p12Coder
, kcRef
);
109 /* We need a CSPDL handle for possible secure passphrase acquisition */
110 ortn
= SecKeychainGetCSPHandle(kcRef
, &cspHand
);
112 SecImpExpDbg("SecKeychainGetCSPHandle error");
116 /* explicit passphrase, or get one ourself? */
117 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_String
,
118 VP_Export
, (CFTypeRef
*)&phraseStr
, &passKey
);
122 if(phraseStr
!= NULL
) {
123 ortn
= SecPkcs12SetMACPassphrase(p12Coder
, phraseStr
);
124 CFRelease(phraseStr
);
126 SecImpExpDbg("SecPkcs12SetMACPassphrase error");
131 assert(passKey
!= NULL
);
132 ortn
= SecPkcs12SetMACPassKey(p12Coder
, passKey
);
134 SecImpExpDbg("SecPkcs12SetMACPassphrase error");
139 ortn
= SecPkcs12ExportKeychainItems(p12Coder
, exportItems
);
141 SecImpExpDbg("impExpPkcs12Export: SecPkcs12ExportKeychainItems failure");
146 ortn
= SecPkcs12Encode(p12Coder
, &tmpData
);
148 SecImpExpDbg("impExpPkcs12Export: SecPkcs12Encode failure");
152 /* append encoded data to output */
153 CFDataAppendBytes(outData
, CFDataGetBytePtr(tmpData
), CFDataGetLength(tmpData
));
156 SecPkcs12CoderRelease(p12Coder
);
157 if(passKey
!= NULL
) {
158 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
165 CFRelease(exportItems
);
173 OSStatus
impExpPkcs7Export(
174 CFArrayRef exportReps
, // SecExportReps
175 SecItemImportExportFlags flags
, // kSecItemPemArmour, etc.
176 const SecKeyImportExportParameters
*keyParams
, // optional
177 CFMutableDataRef outData
) // output appended here
179 SecCmsSignedDataRef sigd
= NULL
;
180 SecCertificateRef certRef
;
182 CFIndex numCerts
= CFArrayGetCount(exportReps
);
183 SecExportRep
*exportRep
;
184 SecCmsContentInfoRef cinfo
= NULL
;
185 SecArenaPoolRef arena
= NULL
;
186 SecCmsEncoderRef ecx
;
187 CSSM_DATA output
= { 0, NULL
};
190 SecImpExpDbg("impExpPkcs7Export: no certs to export");
191 return errSecSuccess
;
194 /* create the message object */
195 SecCmsMessageRef cmsg
= SecCmsMessageCreate(NULL
);
197 SecImpExpDbg("impExpPkcs7Export: SecCmsMessageCreate failure");
198 return errSecInternalComponent
;
202 exportRep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, 0);
203 assert(exportRep
!= NULL
);
204 if(exportRep
->externType() != kSecItemTypeCertificate
) {
205 SecImpExpDbg("impExpPkcs7Export: non-cert item");
209 certRef
= (SecCertificateRef
)exportRep
->kcItem();
211 /* build chain of objects: message->signedData->data */
212 sigd
= SecCmsSignedDataCreateCertsOnly(cmsg
, certRef
, false);
214 SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataCreateCertsOnly failure");
215 ortn
= errSecInternalComponent
;
219 for (CFIndex dex
=1; dex
<numCerts
; dex
++) {
220 exportRep
= (SecExportRep
*)CFArrayGetValueAtIndex(exportReps
, dex
);
221 assert(exportRep
!= NULL
);
222 if(exportRep
->externType() != kSecItemTypeCertificate
) {
223 SecImpExpDbg("impExpPkcs7Export: non-cert item");
227 certRef
= (SecCertificateRef
)exportRep
->kcItem();
228 ortn
= SecCmsSignedDataAddCertChain(sigd
, certRef
);
230 SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataAddCertChain error");
235 cinfo
= SecCmsMessageGetContentInfo(cmsg
);
237 SecImpExpDbg("impExpPkcs7Export: SecCmsMessageGetContentInfo returned NULL");
238 ortn
= errSecInternalComponent
;
241 ortn
= SecCmsContentInfoSetContentSignedData(cmsg
, cinfo
, sigd
);
243 SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentSignedData error");
246 cinfo
= SecCmsSignedDataGetContentInfo(sigd
);
248 SecImpExpDbg("impExpPkcs7Export: SecCmsSignedDataGetContentInfo returned NULL");
249 ortn
= errSecInternalComponent
;
252 ortn
= SecCmsContentInfoSetContentData(cmsg
, cinfo
, NULL
,
253 false /* FIXME - what's this? */);
255 SecImpExpDbg("impExpPkcs7Export: SecCmsContentInfoSetContentData error");
260 ortn
= SecArenaPoolCreate(1024, &arena
);
262 SecImpExpDbg("impExpPkcs7Export: SecArenaPoolCreate error");
265 ortn
= SecCmsEncoderCreate(cmsg
,
266 NULL
, NULL
, /* DER output callback */
267 &output
, arena
, /* destination storage */
268 NULL
, NULL
, /* password callback */
269 NULL
, NULL
, /* decrypt key callback */
271 &ecx
); /* detached digests */
273 SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderCreate error");
276 ortn
= SecCmsEncoderFinish(ecx
);
278 SecImpExpDbg("impExpPkcs7Export: SecCmsEncoderFinish returned NULL");
282 /* append encoded data to output */
283 CFDataAppendBytes(outData
, output
.Data
, output
.Length
);
288 SecCmsMessageDestroy(cmsg
);
291 SecArenaPoolFree(arena
, false);
296 #pragma mark --- Aggregate Import routines ---
299 * For all of these, if a cspHand is specified instead of a keychain,
300 * the cspHand MUST be a CSPDL handle, not a raw CSP handle.
302 OSStatus
impExpPkcs12Import(
304 SecItemImportExportFlags flags
,
305 const SecKeyImportExportParameters
*keyParams
, // optional
306 ImpPrivKeyImportState
&keyImportState
, // IN/OUT
308 /* caller must supply one of these */
309 SecKeychainRef importKeychain
, // optional
310 CSSM_CSP_HANDLE cspHand
, // required
311 CFMutableArrayRef outArray
) // optional, append here
313 SecPkcs12CoderRef p12Coder
= NULL
;
318 CFMutableArrayRef privKeys
= NULL
;
319 CSSM_KEY
*passKey
= NULL
;
320 CFStringRef phraseStr
= NULL
;
323 * Optional private key attrs.
324 * Although the PKCS12 library has its own defaults for these, we'll
325 * set them explicitly to the defaults specified in our API if the
326 * caller doesn't specify any.
328 CSSM_KEYUSE keyUsage
= CSSM_KEYUSE_ANY
;
329 CSSM_KEYATTR_FLAGS keyAttrs
= CSSM_KEYATTR_SENSITIVE
| CSSM_KEYATTR_EXTRACTABLE
|
330 CSSM_KEYATTR_RETURN_REF
;
332 if( (keyParams
== NULL
) ||
333 ( (keyParams
->passphrase
== NULL
) &&
334 !(keyParams
->flags
& kSecKeySecurePassphrase
) ) ) {
335 /* passphrase mandatory */
336 return errSecPassphraseRequired
;
340 * Set up a P12 decoder.
342 ortn
= SecPkcs12CoderCreate(&p12Coder
);
344 SecImpExpDbg("SecPkcs12CoderCreate error");
347 /* subsequent errors to errOut: */
349 CSSM_CL_HANDLE clHand
= cuClStartup();
350 CSSM_CSP_HANDLE rawCspHand
= cuCspStartup(CSSM_TRUE
); // for CL
351 if((clHand
== 0) || (rawCspHand
== 0)) {
352 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
355 assert(cspHand
!= CSSM_INVALID_HANDLE
);
356 if(importKeychain
!= NULL
) {
357 ortn
= SecPkcs12SetKeychain(p12Coder
, importKeychain
);
359 SecImpExpDbg("SecPkcs12SetKeychain error");
364 if(cspHand
== CSSM_INVALID_HANDLE
) {
368 ortn
= SecPkcs12SetCspHandle(p12Coder
, cspHand
);
370 SecImpExpDbg("SecPkcs12SetCspHandle error");
375 /* explicit passphrase, or get one ourself? */
376 ortn
= impExpPassphraseCommon(keyParams
, cspHand
, SPF_String
,
377 VP_Import
, (CFTypeRef
*)&phraseStr
, &passKey
);
381 if(phraseStr
!= NULL
) {
382 ortn
= SecPkcs12SetMACPassphrase(p12Coder
, phraseStr
);
383 CFRelease(phraseStr
);
385 SecImpExpDbg("SecPkcs12SetMACPassphrase error");
390 assert(passKey
!= NULL
);
391 ortn
= SecPkcs12SetMACPassKey(p12Coder
, passKey
);
393 SecImpExpDbg("SecPkcs12SetMACPassphrase error");
398 if(keyImportState
!= PIS_NoLimit
) {
399 bool foundOneKey
= false;
401 /* allow either zero or one more private key */
402 if(keyImportState
== PIS_NoMore
) {
405 ortn
= SecPkcs12LimitPrivateKeyImport(p12Coder
, foundOneKey
);
407 SecImpExpDbg("SecPkcs12LimitPrivateKeyImport error");
412 if(keyParams
!= NULL
) {
413 if(keyParams
->keyUsage
!= 0) {
414 keyUsage
= keyParams
->keyUsage
;
416 if(keyParams
->keyAttributes
!= 0) {
417 keyAttrs
= keyParams
->keyAttributes
;
419 if(keyParams
->flags
& kSecKeyNoAccessControl
) {
420 ortn
= SecPkcs12SetAccess(p12Coder
, NULL
);
422 SecImpExpDbg("SecPkcs12SetAccess error");
426 else if(keyParams
->accessRef
!= NULL
) {
427 ortn
= SecPkcs12SetAccess(p12Coder
, keyParams
->accessRef
);
429 SecImpExpDbg("SecPkcs12SetAccess error");
433 /* else default ACL */
435 ortn
= SecPkcs12SetKeyUsage(p12Coder
, keyUsage
);
437 SecImpExpDbg("SecPkcs12SetKeyUsage error");
440 ortn
= SecPkcs12SetKeyAttrs(p12Coder
, keyAttrs
);
442 SecImpExpDbg("SecPkcs12SetKeyAttrs error");
447 ortn
= SecPkcs12Decode(p12Coder
, inData
);
449 SecImpExpDbg("SecPkcs12Decode error");
454 * About to process SecKeychainItemRefs.
455 * This whole mess is irrelevant if the caller doesn't
456 * want an array of SecKeychainItemRefs.
458 if(outArray
== NULL
) {
462 ortn
= SecPkcs12CertificateCount(p12Coder
, &numCerts
);
464 SecImpExpDbg("SecPkcs12CertificateCount error");
467 ortn
= SecPkcs12PrivateKeyCount(p12Coder
, &numKeys
);
469 SecImpExpDbg("SecPkcs12PrivateKeyCount error");
474 * Match up certs and keys to create SecIdentityRefs.
475 * First create a temporary array of the private keys
476 * found by the P12 module.
478 * FIXME we're working with a P12 module which can not
479 * vend SecKeyRefs.....this will hopefully, eventually,
482 privKeys
= CFArrayCreateMutable(NULL
, numKeys
, NULL
);
483 for(dex
=0; dex
<numKeys
; dex
++) {
484 CSSM_KEY_PTR privKey
;
485 ortn
= SecPkcs12GetCssmPrivateKey(p12Coder
,
486 dex
, &privKey
, NULL
, NULL
, NULL
);
487 CFArrayAppendValue(privKeys
, privKey
);
491 * Now go through all certs, searching for a matching private
492 * key. When we find a match we try to create an identity from the
493 * cert, which might fail for a number of reasons, currently including
494 * the fact that there is no way to create an identity with a key
495 * which does not reside on a keychain. (Such is the case here when
496 * caller has not specified a keychain.) If that works we skip the
497 * cert, delete that key from the privKeys array, and append the
498 * indentity to outArray. If no identity is found we append the
499 * cert to outArray. At the end of this loop, remaining
500 * items in privKeys (of which there will typically be none) are
501 * also appended to outArray.
503 for(dex
=0; dex
<numCerts
; dex
++) {
504 SecCertificateRef certRef
= NULL
; // created by P12
505 SecCertificateRef importedCertRef
= NULL
; // owned by Sec layer
506 SecCertificateRef itemImplRef
= NULL
; // temp, retained by us
507 CSSM_KEY_PTR pubKey
= NULL
; // mallocd by CL
508 CSSM_KEY_PTR privKey
= NULL
; // owned by P12
509 CSSM_DATA certData
; // owned by Sec layer
510 CSSM_DATA pubKeyDigest
= {0, NULL
}; // mallocd by CSP
511 CSSM_DATA privKeyDigest
= {0, NULL
}; // mallocd by CSP
512 bool foundIdentity
= false;
514 ortn
= SecPkcs12CopyCertificate(p12Coder
, dex
, &certRef
,
517 /* should never happen */
518 SecImpExpDbg("SecPkcs12CopyCertificate error");
521 /* subsequent errors in this loop to loopEnd: */
523 if(importKeychain
== NULL
) {
524 /* Skip the Identity match - just return keys and certs */
528 /* the SecPkcs12CopyCertificate function returns a floating
529 * certificate without a keychain. We must update it now that
530 * it has been added to importKeychain.
533 StorageManager::KeychainList keychains
;
534 globals().storageManager
.optionalSearchList(importKeychain
, keychains
);
536 /* Convert unified SecCertificateRef to an ItemImpl instance */
537 itemImplRef
= SecCertificateCreateItemImplInstance(certRef
);
538 SecPointer
<Certificate
> cert
= Certificate::required(itemImplRef
);
539 CFRelease(itemImplRef
);
541 importedCertRef
= cert
->findInKeychain(keychains
)->handle();
543 if (importedCertRef
) {
544 SecCertificateRef tmpRef
= SecCertificateCreateFromItemImplInstance(importedCertRef
);
545 CFRelease(importedCertRef
);
546 importedCertRef
= tmpRef
;
550 /* Get digest of this cert's public key */
551 ortn
= SecCertificateGetData(importedCertRef
, &certData
);
553 SecImpExpDbg("SecCertificateGetData error");
556 ortn
= CSSM_CL_CertGetKeyInfo(clHand
, &certData
, &pubKey
);
558 SecImpExpDbg("SecCertificateGetData error");
561 ortn
= impExpKeyDigest(rawCspHand
, pubKey
, &pubKeyDigest
);
567 * Now search for a private key with this same digest
569 numKeys
= CFArrayGetCount(privKeys
);
570 for(CFIndex privDex
=0; privDex
<numKeys
; privDex
++) {
571 privKey
= (CSSM_KEY_PTR
)CFArrayGetValueAtIndex(privKeys
, privDex
);
572 assert(privKey
!= NULL
);
573 ortn
= impExpKeyDigest(cspHand
, privKey
, &privKeyDigest
);
577 CSSM_BOOL digestMatch
= cuCompareCssmData(&pubKeyDigest
, &privKeyDigest
);
578 impExpFreeCssmMemory(cspHand
, privKeyDigest
.Data
);
581 * MATCH: try to cook up Identity.
582 * TBD: I expect some work will be needed here when
583 * Sec layer can handle floating keys. One thing
584 * that would be nice would be if we could create an identity
585 * FROM a given SecCertRef and a SecKeyRef, even if
586 * the SecKeyRef is floating.
588 * NOTE: you might think that we could do a
589 * SecIdentityCreateWithCertificate() before, or even without,
590 * doing a digest match....but that could "work" without
591 * us having imported any keys at all, if the appropriate
592 * private key were already there. Doing the digest match
593 * guarantees the uniqueness of the key item in the DB.
595 SecIdentityRef idRef
= NULL
;
596 ortn
= SecIdentityCreateWithCertificate(importKeychain
,
597 importedCertRef
, &idRef
);
598 if(ortn
== errSecSuccess
) {
602 * -- add Identity to outArray
603 * -- remove privKey from privKeys array
604 * -- skip to next cert
606 SecImpExpDbg("P12Import: generating a SecIdentityRef");
607 assert(outArray
!= NULL
);
608 CFArrayAppendValue(outArray
, idRef
);
609 CFRelease(idRef
); // array holds only ref
611 CFArrayRemoveValueAtIndex(privKeys
, privDex
);
612 foundIdentity
= true;
614 } /* ID create worked, else try next key */
616 } /* searching thru privKeys */
618 /* free resources allocated in this loop */
619 assert(certRef
!= NULL
);
620 if(!foundIdentity
) {
621 /* No private key for this cert: give to caller */
622 assert(outArray
!= NULL
);
623 CFArrayAppendValue(outArray
, certRef
);
625 CFRelease(certRef
); // outArray holds only ref
627 if (importedCertRef
) {
628 CFRelease(importedCertRef
);
629 importedCertRef
= NULL
;
632 /* technically invalid, the CL used some CSP handle we
633 * don't have access to to get this... */
634 CSSM_FreeKey(rawCspHand
, NULL
, pubKey
, CSSM_FALSE
);
635 impExpFreeCssmMemory(clHand
, pubKey
);
638 if(pubKeyDigest
.Data
!= NULL
) {
639 impExpFreeCssmMemory(rawCspHand
, pubKeyDigest
.Data
);
640 pubKeyDigest
.Data
= NULL
;
649 * One last thing: pass any remaining (non-identity) keys to caller.
650 * For now, the keys are CSSM_KEYs owned by the P12 coder object, we
651 * don't have to release them. When P12 can vend SecKeyRefs, we release the
656 The code below has no net effect, except for generating a leak. This was
657 found while investigating
658 <rdar://problem/8799913> SecItemImport() leaking
659 Code like this will need to be added when we return SecIdentityRefs in
660 the "in memory" case (destKeychain = NULL). Currently, when importing to
661 a physical keychain, the returned item array contains SecIdentityRefs,
662 whereas the "in memory" case returns SecCertificateRefs. See
663 <rdar://problem/8862809> ER: SecItemImport should return SecIdentityRefs in the "in memory" case
668 if(ortn
== errSecSuccess
) { // TBD OR keys are SecKeyRefs
669 numKeys
= CFArrayGetCount(privKeys
);
670 for(dex
=0; dex
<numKeys
; dex
++) {
672 CSSM_KEY_PTR privKey
=
673 (CSSM_KEY_PTR
)CFArrayGetValueAtIndex(privKeys
, dex
);
674 assert(privKey
!= NULL
);
675 if(ortn
== errSecSuccess
) {
676 /* only do this on complete success so far */
677 ortn
= SecKeyCreateWithCSSMKey(privKey
, &keyRef
);
679 SecImpExpDbg("SecKeyCreateWithCSSMKey error");
681 /* keep going for CFRelease */
685 /* TBD CFRelease the SecKeyRef */
686 } /* for each privKey */
687 } /* success so far */
691 SecPkcs12CoderRelease(p12Coder
);
692 if(passKey
!= NULL
) {
693 CSSM_FreeKey(cspHand
, NULL
, passKey
, CSSM_FALSE
);
696 if(privKeys
!= NULL
) {
700 cuClDetachUnload(clHand
);
702 if(rawCspHand
!= 0) {
703 cuCspDetachUnload(rawCspHand
, CSSM_TRUE
);
708 OSStatus
impExpPkcs7Import(
710 SecItemImportExportFlags flags
,
711 const SecKeyImportExportParameters
*keyParams
, // optional
712 SecKeychainRef importKeychain
, // optional
713 CFMutableArrayRef outArray
) // optional, append here
715 SecCmsDecoderRef decoderContext
;
716 SecCmsMessageRef cmsMessage
= NULL
;
717 SecCmsContentInfoRef contentInfo
;
718 SecCmsSignedDataRef signedData
;
719 int contentLevelCount
;
721 SECOidTag contentTypeTag
;
723 OSStatus ourRtn
= errSecSuccess
;
725 /* decode the message */
726 result
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
);
731 result
= SecCmsDecoderUpdate(decoderContext
, CFDataGetBytePtr(inData
),
732 CFDataGetLength(inData
));
734 /* any useful status here? */
735 SecImpExpDbg("SecCmsDecoderUpdate error");
736 ourRtn
= errSecUnknownFormat
;
737 SecCmsDecoderDestroy(decoderContext
);
741 ourRtn
= SecCmsDecoderFinish(decoderContext
, &cmsMessage
);
743 SecImpExpDbg("SecCmsDecoderFinish error");
744 ourRtn
= errSecUnknownFormat
;
748 // process the results
749 contentLevelCount
= SecCmsMessageContentLevelCount (cmsMessage
);
751 for (i
= 0; i
< contentLevelCount
; ++i
)
753 // get content information
754 contentInfo
= SecCmsMessageContentLevel (cmsMessage
, i
);
755 contentTypeTag
= SecCmsContentInfoGetContentTypeTag (contentInfo
);
757 switch (contentTypeTag
)
759 case SEC_OID_PKCS7_SIGNED_DATA
:
761 /* I guess this the only interesting field */
763 (SecCmsSignedDataRef
) SecCmsContentInfoGetContent (contentInfo
);
764 if (signedData
== NULL
) {
765 SecImpExpDbg("SecCmsContentInfoGetContent returned NULL");
766 ourRtn
= errSecUnknownFormat
;
770 // import the certificates
771 CSSM_DATA
**outCerts
= SecCmsSignedDataGetCertificateList(signedData
);
772 if(outCerts
== NULL
) {
773 SecImpExpDbg("SecCmsSignedDataGetCertificateList returned NULL");
774 ourRtn
= errSecUnknownFormat
;
778 /* Returned value is NULL-terminated array */
780 CSSM_DATA
**array
= outCerts
;
787 SecImpExpDbg("No certs found in apparently good PKCS7 blob");
791 for(unsigned dex
=0; dex
<count
; dex
++) {
792 ourRtn
= impExpImportCertCommon(outCerts
[dex
], importKeychain
,
806 SecCmsMessageDestroy(cmsMessage
);
813 * Import a netscape-cert-sequence. Suitable for low-cost guessing when neither
814 * importKeychain nor outArray is specified.
816 OSStatus
impExpNetscapeCertImport(
818 SecItemImportExportFlags flags
,
819 const SecKeyImportExportParameters
*keyParams
, // optional
820 SecKeychainRef importKeychain
, // optional
821 CFMutableArrayRef outArray
) // optional, append here
824 NetscapeCertSequence certSeq
;
827 memset(&certSeq
, 0, sizeof(certSeq
));
828 PRErrorCode perr
= coder
.decode(CFDataGetBytePtr(inData
),
829 CFDataGetLength(inData
),
830 NetscapeCertSequenceTemplate
,
833 SecImpExpDbg("impExpNetscapeCertImport: DER decode failure");
834 return errSecUnknownFormat
;
837 /* verify (contentType == netscape-cert-sequence) */
838 if(!cuCompareOid(&CSSMOID_NetscapeCertSequence
, &certSeq
.contentType
)) {
839 SecImpExpDbg("impExpNetscapeCertImport: OID mismatch");
840 return errSecUnknownFormat
;
843 /* Extract certs in CSSM_DATA form, return to caller */
844 unsigned numCerts
= nssArraySize((const void **)certSeq
.certs
);
845 for(unsigned i
=0; i
<numCerts
; i
++) {
846 CSSM_DATA
*cert
= certSeq
.certs
[i
];
847 OSStatus ortn
= impExpImportCertCommon(cert
, importKeychain
, outArray
);
852 return errSecSuccess
;
855 #pragma mark --- Utilities ---
857 OSStatus
impExpImportCertCommon(
858 const CSSM_DATA
*cdata
,
859 SecKeychainRef importKeychain
, // optional
860 CFMutableArrayRef outArray
) // optional, append here
862 OSStatus ortn
= errSecSuccess
;
863 SecCertificateRef certRef
;
866 return errSecUnsupportedFormat
;
868 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)cdata
->Data
, (CFIndex
)cdata
->Length
, kCFAllocatorNull
);
869 /* Pass kCFAllocatorNull as bytesDeallocator to assure the bytes aren't freed */
871 return errSecUnsupportedFormat
;
873 certRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
874 CFRelease(data
); /* certRef has its own copy of the data now */
876 SecImpExpDbg("impExpHandleCert error\n");
877 return errSecUnsupportedFormat
;
880 if(importKeychain
!= NULL
) {
881 ortn
= SecCertificateAddToKeychain(certRef
, importKeychain
);
883 SecImpExpDbg("SecCertificateAddToKeychain error\n");
888 if(outArray
!= NULL
) {
889 CFArrayAppendValue(outArray
, certRef
);