2 * Copyright (c) 2003-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@
25 * pkcs12Keychain.h - P12Coder keychain-related functions.
28 #include "pkcs12Coder.h"
29 #include "pkcs12Templates.h"
30 #include "pkcs12Utils.h"
31 #include "pkcs12Debug.h"
32 #include "pkcs12Crypto.h"
33 #include <Security/cssmerr.h>
34 #include <security_cdsa_utils/cuDbUtils.h> // cuAddCrlToDb()
35 #include <security_asn1/nssUtils.h>
36 #include <security_cdsa_utilities/KeySchema.h> /* private API */
37 #include <security_keychain/SecImportExportCrypto.h> /* private API */
40 * Store the results of a successful decode in app-specified
41 * keychain per mImportFlags. Also assign public key hash attributes to any
44 void P12Coder::storeDecodeResults()
46 assert(mKeychain
!= NULL
);
47 assert(mDlDbHand
.DLHandle
!= 0);
48 if(mImportFlags
& kSecImportKeys
) {
49 setPrivateKeyHashes();
51 if(mImportFlags
& kSecImportCertificates
) {
52 for(unsigned dex
=0; dex
<numCerts(); dex
++) {
53 P12CertBag
*certBag
= mCerts
[dex
];
54 SecCertificateRef secCert
= certBag
->getSecCert();
55 OSStatus ortn
= SecCertificateAddToKeychain(secCert
, mKeychain
);
58 case errSecSuccess
: // normal
59 p12DecodeLog("cert added to keychain");
61 case errSecDuplicateItem
: // dup cert, OK< skip
62 p12DecodeLog("skipping dup cert");
65 p12ErrorLog("SecCertificateAddToKeychain failure\n");
66 MacOSError::throwMe(ortn
);
71 if(mImportFlags
& kSecImportCRLs
) {
72 for(unsigned dex
=0; dex
<numCrls(); dex
++) {
73 P12CrlBag
*crlBag
= mCrls
[dex
];
74 CSSM_RETURN crtn
= cuAddCrlToDb(mDlDbHand
,
77 NULL
); // no URI known
79 case CSSM_OK
: // normal
80 p12DecodeLog("CRL added to keychain");
82 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
: // dup, ignore
83 p12DecodeLog("skipping dup CRL");
86 p12LogCssmError("Error adding CRL to keychain", crtn
);
87 CssmError::throwMe(crtn
);
92 /* If all of that succeeded, post notification for imported keys */
93 if(mImportFlags
& kSecImportKeys
) {
99 * Assign appropriate public key hash attribute to each
102 void P12Coder::setPrivateKeyHashes()
106 for(unsigned dex
=0; dex
<numKeys(); dex
++) {
107 P12KeyBag
*keyBag
= mKeys
[dex
];
109 CSSM_DATA newLabel
= {0, NULL
};
110 CFStringRef friendlyName
= keyBag
->friendlyName();
112 CSSM_RETURN crtn
= p12SetPubKeyHash(mCspHand
,
115 p12StringToUtf8(friendlyName
, mCoder
),
120 CFRelease(friendlyName
);
124 /* update key's label in case we have to delete on error */
125 keyBag
->setLabel(newLabel
);
126 p12DecodeLog("set pub key hash for private key");
128 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
130 * Special case: update keyBag's CSSM_KEY and proceed without error
132 p12DecodeLog("ignoring dup private key");
133 assert(newKey
!= NULL
);
134 keyBag
->setKey(newKey
);
135 keyBag
->dupKey(true);
136 /* update key's label in case we have to delete on error */
137 keyBag
->setLabel(newLabel
);
140 p12ErrorLog("p12SetPubKeyHash failure\n");
141 CssmError::throwMe(crtn
);
147 * Post keychain notification for imported keys.
149 void P12Coder::notifyKeyImport()
151 if(mKeychain
== NULL
) {
152 /* Can't notify if user only gave us DLDB */
155 for(unsigned dex
=0; dex
<numKeys(); dex
++) {
156 P12KeyBag
*keyBag
= mKeys
[dex
];
157 if(keyBag
->dupKey()) {
158 /* no notification for keys we merely looked up */
161 CssmData
&labelData
= CssmData::overlay(keyBag
->label());
162 OSStatus ortn
= impExpKeyNotify(mKeychain
, labelData
, *keyBag
->key());
164 p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn
);
165 MacOSError::throwMe(ortn
);
171 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
172 * "match" if their localKeyIds match. Returns NULL if not found.
174 P12CertBag
*P12Coder::findCertForKey(
177 assert(keyBag
!= NULL
);
178 CSSM_DATA
&keyKeyId
= keyBag
->localKeyIdCssm();
180 for(unsigned dex
=0; dex
<numCerts(); dex
++) {
181 P12CertBag
*certBag
= mCerts
[dex
];
182 CSSM_DATA
&certKeyId
= certBag
->localKeyIdCssm();
183 if(nssCompareCssmData(&keyKeyId
, &certKeyId
)) {
184 p12DecodeLog("findCertForKey SUCCESS");
188 p12DecodeLog("findCertForKey FAILURE");
193 * Export items specified as SecKeychainItemRefs.
195 void P12Coder::exportKeychainItems(
198 assert(items
!= NULL
);
199 CFIndex numItems
= CFArrayGetCount(items
);
200 for(CFIndex dex
=0; dex
<numItems
; dex
++) {
201 const void *item
= CFArrayGetValueAtIndex(items
, dex
);
203 p12ErrorLog("exportKeychainItems: NULL item\n");
204 MacOSError::throwMe(errSecParam
);
206 CFTypeID itemType
= CFGetTypeID(item
);
207 if(itemType
== SecCertificateGetTypeID()) {
208 addSecCert((SecCertificateRef
)item
);
210 else if(itemType
== SecKeyGetTypeID()) {
211 addSecKey((SecKeyRef
)item
);
214 p12ErrorLog("exportKeychainItems: unknown item\n");
215 MacOSError::throwMe(errSecParam
);
221 * Gross kludge to work around the fact that SecKeyRefs have no attributes which
222 * are visible at the Sec layer. Not only are the attribute names we happen
223 * to know about (Label, PrintName) not publically visible anywhere in the
224 * system, but the *format* of the attr names for SecKeyRefs differs from
225 * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys,
226 * NAME_AS_INTEGER for everything else).
228 * So. We use the privately accessible schema definition table for
229 * keys to map from the attr name strings we happen to know about to a
230 * totally private name-as-int index which we can then use in the
231 * SecKeychainItemCopyAttributesAndData mechanism.
233 * This will go away if SecKeyRef defines its actual attrs as strings, AND
234 * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs
235 * as strings rather than integers.
237 static OSStatus
attrNameToInt(
241 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*attrList
=
242 KeySchema::KeySchemaAttributeList
;
243 unsigned numAttrs
= KeySchema::KeySchemaAttributeCount
;
244 for(unsigned dex
=0; dex
<numAttrs
; dex
++) {
245 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*info
= &attrList
[dex
];
246 if(!strcmp(name
, info
->AttributeName
)) {
247 *attrInt
= info
->AttributeId
;
248 return errSecSuccess
;
254 void P12Coder::addSecKey(
257 /* get the cert's attrs (not data) */
260 * Convert the attr name strings we happen to know about to
261 * unknowable name-as-int values.
264 OSStatus ortn
= attrNameToInt(P12_KEY_ATTR_PRINT_NAME
, &printNameTag
);
266 p12ErrorLog("addSecKey: problem looking up key attr name\n");
267 MacOSError::throwMe(ortn
);
270 ortn
= attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH
, &labelHashTag
);
272 p12ErrorLog("addSecKey: problem looking up key attr name\n");
273 MacOSError::throwMe(ortn
);
277 tags
[0] = printNameTag
;
278 tags
[1] = labelHashTag
;
280 /* I don't know what the format field is for */
281 SecKeychainAttributeInfo attrInfo
;
284 attrInfo
.format
= NULL
; // ???
286 /* FIXME header says this is an IN/OUT param, but it's not */
287 SecKeychainAttributeList
*attrList
= NULL
;
289 ortn
= SecKeychainItemCopyAttributesAndData(
290 (SecKeychainItemRef
)keyRef
,
294 NULL
, // don't need the data
297 p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData "
299 MacOSError::throwMe(ortn
);
302 /* Snag the attrs, convert to something useful */
303 CFStringRef friendName
= NULL
;
304 CFDataRef localKeyId
= NULL
;
305 for(unsigned i
=0; i
<attrList
->count
; i
++) {
306 SecKeychainAttribute
*attr
= &attrList
->attr
[i
];
307 if(attr
->tag
== printNameTag
) {
308 friendName
= CFStringCreateWithBytes(NULL
,
309 (UInt8
*)attr
->data
, attr
->length
,
310 kCFStringEncodingUTF8
, false);
312 else if(attr
->tag
== labelHashTag
) {
313 localKeyId
= CFDataCreate(NULL
, (UInt8
*)attr
->data
, attr
->length
);
316 p12ErrorLog("addSecKey: unexpected attr tag\n");
317 MacOSError::throwMe(errSecParam
);
323 * Infer the CSP associated with this key.
324 * FIXME: this should be an attribute of the SecKeyRef itself,
325 * not inferred from the keychain it happens to be living on
326 * (SecKeyRefs should not have to be attached to Keychains at
329 SecKeychainRef kcRef
;
330 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)keyRef
, &kcRef
);
332 p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn
);
333 MacOSError::throwMe(ortn
);
335 CSSM_CSP_HANDLE cspHand
;
336 ortn
= SecKeychainGetCSPHandle(kcRef
, &cspHand
);
338 p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn
);
339 MacOSError::throwMe(ortn
);
343 /* and the CSSM_KEY itself */
344 const CSSM_KEY
*cssmKey
;
345 ortn
= SecKeyGetCSSMKey(keyRef
, &cssmKey
);
347 p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn
);
348 MacOSError::throwMe(ortn
);
351 /* Cook up a key bag and save it */
352 P12KeyBag
*keyBag
= new P12KeyBag(cssmKey
,
354 friendName
, localKeyId
,
359 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
361 CFRelease(friendName
);
364 CFRelease(localKeyId
);
368 void P12Coder::addSecCert(
369 SecCertificateRef certRef
)
371 /* get the cert's attrs and data */
372 /* I don't know what the format field is for */
373 SecKeychainAttributeInfo attrInfo
;
375 UInt32 tags
[2] = {kSecLabelItemAttr
, kSecPublicKeyHashItemAttr
};
377 attrInfo
.format
= NULL
; // ???
379 /* FIXME header says this is an IN/OUT param, but it's not */
380 SecKeychainAttributeList
*attrList
= NULL
;
384 OSStatus ortn
= SecKeychainItemCopyAttributesAndData(
385 (SecKeychainItemRef
)certRef
,
392 p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData "
394 MacOSError::throwMe(ortn
);
397 /* Snag the attrs, convert to something useful */
398 CFStringRef friendName
= NULL
;
399 CFDataRef localKeyId
= NULL
;
400 for(unsigned i
=0; i
<attrList
->count
; i
++) {
401 SecKeychainAttribute
*attr
= &attrList
->attr
[i
];
403 case kSecPublicKeyHashItemAttr
:
404 localKeyId
= CFDataCreate(NULL
, (UInt8
*)attr
->data
, attr
->length
);
406 case kSecLabelItemAttr
:
407 /* FIXME: always in UTF8? */
408 friendName
= CFStringCreateWithBytes(NULL
,
409 (UInt8
*)attr
->data
, attr
->length
, kCFStringEncodingUTF8
,
413 p12ErrorLog("addSecCert: unexpected attr tag\n");
414 MacOSError::throwMe(errSecParam
);
419 /* Cook up a cert bag and save it */
420 CSSM_DATA cData
= {certLen
, (uint8
*)certData
};
421 P12CertBag
*certBag
= new P12CertBag(CT_X509
, cData
, friendName
,
422 localKeyId
, NULL
, mCoder
);
424 SecKeychainItemFreeAttributesAndData(attrList
, certData
);
426 CFRelease(friendName
);
429 CFRelease(localKeyId
);
434 * Delete anything stored in a keychain during decode, called on
436 * Currently the only thing we have to deal with is private keys,
437 * since certs and CRLs don't get stored until the end of a successful
440 void P12Coder::deleteDecodedItems()
442 if(!(mImportFlags
& kSecImportKeys
)) {
443 /* no keys stored, done */
446 if(mDlDbHand
.DLHandle
== 0) {
447 /* no keychain, done */
451 unsigned nKeys
= numKeys();
452 for(unsigned dex
=0; dex
<nKeys
; dex
++) {
453 P12KeyBag
*keyBag
= mKeys
[dex
];
454 p12DeleteKey(mDlDbHand
, keyBag
->label());