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 "SecCFRelease.h"
34 #include <Security/cssmerr.h>
35 #include <security_cdsa_utils/cuDbUtils.h> // cuAddCrlToDb()
36 #include <security_asn1/nssUtils.h>
37 #include <security_cdsa_utilities/KeySchema.h> /* private API */
38 #include <security_keychain/SecImportExportCrypto.h> /* private API */
41 * Store the results of a successful decode in app-specified
42 * keychain per mImportFlags. Also assign public key hash attributes to any
45 void P12Coder::storeDecodeResults()
47 assert(mKeychain
!= NULL
);
48 assert(mDlDbHand
.DLHandle
!= 0);
49 if(mImportFlags
& kSecImportKeys
) {
50 setPrivateKeyHashes();
52 if(mImportFlags
& kSecImportCertificates
) {
53 for(unsigned dex
=0; dex
<numCerts(); dex
++) {
54 P12CertBag
*certBag
= mCerts
[dex
];
55 SecCertificateRef secCert
= certBag
->getSecCert();
56 OSStatus ortn
= SecCertificateAddToKeychain(secCert
, mKeychain
);
59 case errSecSuccess
: // normal
60 p12DecodeLog("cert added to keychain");
62 case errSecDuplicateItem
: // dup cert, OK< skip
63 p12DecodeLog("skipping dup cert");
66 p12ErrorLog("SecCertificateAddToKeychain failure\n");
67 MacOSError::throwMe(ortn
);
72 if(mImportFlags
& kSecImportCRLs
) {
73 for(unsigned dex
=0; dex
<numCrls(); dex
++) {
74 P12CrlBag
*crlBag
= mCrls
[dex
];
75 CSSM_RETURN crtn
= cuAddCrlToDb(mDlDbHand
,
78 NULL
); // no URI known
80 case CSSM_OK
: // normal
81 p12DecodeLog("CRL added to keychain");
83 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
: // dup, ignore
84 p12DecodeLog("skipping dup CRL");
87 p12LogCssmError("Error adding CRL to keychain", crtn
);
88 CssmError::throwMe(crtn
);
93 /* If all of that succeeded, post notification for imported keys */
94 if(mImportFlags
& kSecImportKeys
) {
100 * Assign appropriate public key hash attribute to each
103 void P12Coder::setPrivateKeyHashes()
107 for(unsigned dex
=0; dex
<numKeys(); dex
++) {
108 P12KeyBag
*keyBag
= mKeys
[dex
];
110 CSSM_DATA newLabel
= {0, NULL
};
111 CFStringRef friendlyName
= keyBag
->friendlyName();
113 CSSM_RETURN crtn
= p12SetPubKeyHash(mCspHand
,
116 p12StringToUtf8(friendlyName
, mCoder
),
121 CFRelease(friendlyName
);
125 /* update key's label in case we have to delete on error */
126 keyBag
->setLabel(newLabel
);
127 p12DecodeLog("set pub key hash for private key");
129 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
131 * Special case: update keyBag's CSSM_KEY and proceed without error
133 p12DecodeLog("ignoring dup private key");
134 assert(newKey
!= NULL
);
135 keyBag
->setKey(newKey
);
136 keyBag
->dupKey(true);
137 /* update key's label in case we have to delete on error */
138 keyBag
->setLabel(newLabel
);
141 p12ErrorLog("p12SetPubKeyHash failure\n");
142 CssmError::throwMe(crtn
);
148 * Post keychain notification for imported keys.
150 void P12Coder::notifyKeyImport()
152 if(mKeychain
== NULL
) {
153 /* Can't notify if user only gave us DLDB */
156 for(unsigned dex
=0; dex
<numKeys(); dex
++) {
157 P12KeyBag
*keyBag
= mKeys
[dex
];
158 if(keyBag
->dupKey()) {
159 /* no notification for keys we merely looked up */
162 CssmData
&labelData
= CssmData::overlay(keyBag
->label());
163 OSStatus ortn
= impExpKeyNotify(mKeychain
, labelData
, *keyBag
->key());
165 p12ErrorLog("notifyKeyImport: impExpKeyNotify returned %ld\n", (unsigned long)ortn
);
166 MacOSError::throwMe(ortn
);
172 * Given a P12KeyBag, find a matching P12CertBag. Keys and certs
173 * "match" if their localKeyIds match. Returns NULL if not found.
175 P12CertBag
*P12Coder::findCertForKey(
178 assert(keyBag
!= NULL
);
179 CSSM_DATA
&keyKeyId
= keyBag
->localKeyIdCssm();
181 for(unsigned dex
=0; dex
<numCerts(); dex
++) {
182 P12CertBag
*certBag
= mCerts
[dex
];
183 CSSM_DATA
&certKeyId
= certBag
->localKeyIdCssm();
184 if(nssCompareCssmData(&keyKeyId
, &certKeyId
)) {
185 p12DecodeLog("findCertForKey SUCCESS");
189 p12DecodeLog("findCertForKey FAILURE");
194 * Export items specified as SecKeychainItemRefs.
196 void P12Coder::exportKeychainItems(
199 assert(items
!= NULL
);
200 CFIndex numItems
= CFArrayGetCount(items
);
201 for(CFIndex dex
=0; dex
<numItems
; dex
++) {
202 const void *item
= CFArrayGetValueAtIndex(items
, dex
);
204 p12ErrorLog("exportKeychainItems: NULL item\n");
205 MacOSError::throwMe(errSecParam
);
207 CFTypeID itemType
= CFGetTypeID(item
);
208 if(itemType
== SecCertificateGetTypeID()) {
209 addSecCert((SecCertificateRef
)item
);
211 else if(itemType
== SecKeyGetTypeID()) {
212 addSecKey((SecKeyRef
)item
);
215 p12ErrorLog("exportKeychainItems: unknown item\n");
216 MacOSError::throwMe(errSecParam
);
222 * Gross kludge to work around the fact that SecKeyRefs have no attributes which
223 * are visible at the Sec layer. Not only are the attribute names we happen
224 * to know about (Label, PrintName) not publically visible anywhere in the
225 * system, but the *format* of the attr names for SecKeyRefs differs from
226 * the format of all other SecKeychainItems (NAME_AS_STRING for SecKeys,
227 * NAME_AS_INTEGER for everything else).
229 * So. We use the privately accessible schema definition table for
230 * keys to map from the attr name strings we happen to know about to a
231 * totally private name-as-int index which we can then use in the
232 * SecKeychainItemCopyAttributesAndData mechanism.
234 * This will go away if SecKeyRef defines its actual attrs as strings, AND
235 * the SecKeychainSearch mechanism knows to specify attr names for SecKeyRefs
236 * as strings rather than integers.
238 static OSStatus
attrNameToInt(
242 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*attrList
=
243 KeySchema::KeySchemaAttributeList
;
244 unsigned numAttrs
= KeySchema::KeySchemaAttributeCount
;
245 for(unsigned dex
=0; dex
<numAttrs
; dex
++) {
246 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*info
= &attrList
[dex
];
247 if(!strcmp(name
, info
->AttributeName
)) {
248 *attrInt
= info
->AttributeId
;
249 return errSecSuccess
;
255 void P12Coder::addSecKey(
258 /* get the cert's attrs (not data) */
261 * Convert the attr name strings we happen to know about to
262 * unknowable name-as-int values.
265 OSStatus ortn
= attrNameToInt(P12_KEY_ATTR_PRINT_NAME
, &printNameTag
);
267 p12ErrorLog("addSecKey: problem looking up key attr name\n");
268 MacOSError::throwMe(ortn
);
271 ortn
= attrNameToInt(P12_KEY_ATTR_LABEL_AND_HASH
, &labelHashTag
);
273 p12ErrorLog("addSecKey: problem looking up key attr name\n");
274 MacOSError::throwMe(ortn
);
278 tags
[0] = printNameTag
;
279 tags
[1] = labelHashTag
;
281 /* I don't know what the format field is for */
282 SecKeychainAttributeInfo attrInfo
;
285 attrInfo
.format
= NULL
; // ???
287 /* FIXME header says this is an IN/OUT param, but it's not */
288 SecKeychainAttributeList
*attrList
= NULL
;
290 ortn
= SecKeychainItemCopyAttributesAndData(
291 (SecKeychainItemRef
)keyRef
,
295 NULL
, // don't need the data
298 p12ErrorLog("addSecKey: SecKeychainItemCopyAttributesAndData "
300 MacOSError::throwMe(ortn
);
303 /* Snag the attrs, convert to something useful */
304 CFStringRef friendName
= NULL
;
305 CFDataRef localKeyId
= NULL
;
306 for(unsigned i
=0; i
<attrList
->count
; i
++) {
307 SecKeychainAttribute
*attr
= &attrList
->attr
[i
];
308 if(attr
->tag
== printNameTag
) {
309 CFReleaseNull(friendName
);
310 friendName
= CFStringCreateWithBytes(NULL
,
311 (UInt8
*)attr
->data
, attr
->length
,
312 kCFStringEncodingUTF8
, false);
314 else if(attr
->tag
== labelHashTag
) {
315 CFReleaseNull(localKeyId
);
316 localKeyId
= CFDataCreate(NULL
, (UInt8
*)attr
->data
, attr
->length
);
319 p12ErrorLog("addSecKey: unexpected attr tag\n");
320 MacOSError::throwMe(errSecParam
);
326 * Infer the CSP associated with this key.
327 * FIXME: this should be an attribute of the SecKeyRef itself,
328 * not inferred from the keychain it happens to be living on
329 * (SecKeyRefs should not have to be attached to Keychains at
332 SecKeychainRef kcRef
;
333 ortn
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)keyRef
, &kcRef
);
335 p12ErrorLog("addSecKey: SecKeychainItemCopyKeychain returned %d\n", (int)ortn
);
336 MacOSError::throwMe(ortn
);
338 CSSM_CSP_HANDLE cspHand
;
339 ortn
= SecKeychainGetCSPHandle(kcRef
, &cspHand
);
341 p12ErrorLog("addSecKey: SecKeychainGetCSPHandle returned %d\n", (int)ortn
);
342 MacOSError::throwMe(ortn
);
346 /* and the CSSM_KEY itself */
347 const CSSM_KEY
*cssmKey
;
348 ortn
= SecKeyGetCSSMKey(keyRef
, &cssmKey
);
350 p12ErrorLog("addSecKey: SecKeyGetCSSMKey returned %d\n", (int)ortn
);
351 MacOSError::throwMe(ortn
);
354 /* Cook up a key bag and save it */
355 P12KeyBag
*keyBag
= new P12KeyBag(cssmKey
,
357 friendName
, localKeyId
,
362 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
364 CFRelease(friendName
);
367 CFRelease(localKeyId
);
371 void P12Coder::addSecCert(
372 SecCertificateRef certRef
)
374 /* get the cert's attrs and data */
375 /* I don't know what the format field is for */
376 SecKeychainAttributeInfo attrInfo
;
378 UInt32 tags
[2] = {kSecLabelItemAttr
, kSecPublicKeyHashItemAttr
};
380 attrInfo
.format
= NULL
; // ???
382 /* FIXME header says this is an IN/OUT param, but it's not */
383 SecKeychainAttributeList
*attrList
= NULL
;
387 OSStatus ortn
= SecKeychainItemCopyAttributesAndData(
388 (SecKeychainItemRef
)certRef
,
395 p12ErrorLog("addSecCert: SecKeychainItemCopyAttributesAndData "
397 MacOSError::throwMe(ortn
);
400 /* Snag the attrs, convert to something useful */
401 CFStringRef friendName
= NULL
;
402 CFDataRef localKeyId
= NULL
;
403 for(unsigned i
=0; i
<attrList
->count
; i
++) {
404 SecKeychainAttribute
*attr
= &attrList
->attr
[i
];
406 case kSecPublicKeyHashItemAttr
:
407 CFReleaseNull(localKeyId
);
408 localKeyId
= CFDataCreate(NULL
, (UInt8
*)attr
->data
, attr
->length
);
410 case kSecLabelItemAttr
:
411 CFReleaseNull(friendName
);
412 /* FIXME: always in UTF8? */
413 friendName
= CFStringCreateWithBytes(NULL
,
414 (UInt8
*)attr
->data
, attr
->length
, kCFStringEncodingUTF8
,
418 SecKeychainItemFreeAttributesAndData(attrList
, certData
);
419 CFReleaseNull(friendName
);
420 CFReleaseNull(localKeyId
);
421 p12ErrorLog("addSecCert: unexpected attr tag\n");
422 MacOSError::throwMe(errSecParam
);
427 /* Cook up a cert bag and save it */
428 CSSM_DATA cData
= {certLen
, (uint8
*)certData
};
429 P12CertBag
*certBag
= new P12CertBag(CT_X509
, cData
, friendName
,
430 localKeyId
, NULL
, mCoder
);
432 SecKeychainItemFreeAttributesAndData(attrList
, certData
);
434 CFRelease(friendName
);
437 CFRelease(localKeyId
);
442 * Delete anything stored in a keychain during decode, called on
444 * Currently the only thing we have to deal with is private keys,
445 * since certs and CRLs don't get stored until the end of a successful
448 void P12Coder::deleteDecodedItems()
450 if(!(mImportFlags
& kSecImportKeys
)) {
451 /* no keys stored, done */
454 if(mDlDbHand
.DLHandle
== 0) {
455 /* no keychain, done */
459 unsigned nKeys
= numKeys();
460 for(unsigned dex
=0; dex
<nKeys
; dex
++) {
461 P12KeyBag
*keyBag
= mKeys
[dex
];
462 p12DeleteKey(mDlDbHand
, keyBag
->label());