2 * Copyright (c) 2016 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 xLicense.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <Security/Security.h>
25 #include "keychain_regressions.h"
26 #include "kc-helpers.h"
27 #include "kc-item-helpers.h"
28 #include "kc-key-helpers.h"
29 #include "kc-identity-helpers.h"
31 #import <Foundation/Foundation.h>
36 #include <Security/oidscert.h>
37 #include <Security/oidsattr.h>
38 #include <Security/oidsalg.h>
39 #include <Security/x509defs.h>
40 #include <Security/cssmapi.h>
41 #include <Security/cssmapple.h>
42 #include <Security/certextensions.h>
44 #include <Security/SecKeychain.h>
45 #include <Security/SecKeychainItem.h>
46 #include <Security/SecImportExport.h>
47 #include <Security/SecIdentity.h>
48 #include <Security/SecIdentitySearch.h>
49 #include <Security/SecKey.h>
50 #include <Security/SecCertificate.h>
51 #include <Security/SecItem.h>
53 // Turn off deprecated API warnings
54 //#pragma clang diagnostic ignored "-Wdeprecated-declarations"
57 verifyPrivateKeyExtractability(BOOL extractable, NSArray *items)
59 // After importing items, check that private keys (if any) have
60 // the expected extractable attribute value.
62 CFIndex count = [items count];
63 is(count, 1, "One identity added");
65 for (id item in items)
68 SecKeyRef aKey = NULL;
69 if (SecKeyGetTypeID() == CFGetTypeID((CFTypeRef)item)) {
70 aKey = (SecKeyRef) CFRetain((CFTypeRef)item);
71 fprintf(stdout, "Verifying imported SecKey\n");
73 else if (SecIdentityGetTypeID() == CFGetTypeID((CFTypeRef)item)) {
74 status = SecIdentityCopyPrivateKey((SecIdentityRef)item, &aKey);
75 ok_status(status, "%s: SecIdentityCopyPrivateKey", testName);
78 ok(aKey, "%s: Have a key to test", testName);
82 const CSSM_KEY *cssmKey;
83 OSStatus status = SecKeyGetCSSMKey(aKey, &cssmKey);
84 ok_status(status, "%s: SecKeyGetCSSMKey", testName);
85 if (status != noErr) {
88 is(cssmKey->KeyHeader.KeyClass, CSSM_KEYCLASS_PRIVATE_KEY, "%s: key is private key", testName);
90 if (!(cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY)) {
91 fprintf(stdout, "Skipping non-private key (KeyClass=%d)\n", cssmKey->KeyHeader.KeyClass);
92 continue; // only checking private keys
94 BOOL isExtractable = (cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE) ? YES : NO;
95 is(isExtractable, extractable, "%s: key extractability matches expectations", testName);
103 setIdentityPreferenceForImportedIdentity(SecKeychainRef importKeychain, NSString *name, NSArray *items)
105 CFArrayRef importedItems = (__bridge CFArrayRef)items;
109 SecIdentityRef importedIdRef = NULL;
110 CFIndex dex, numItems = CFArrayGetCount(importedItems);
111 for(dex=0; dex<numItems; dex++)
113 CFTypeRef item = CFArrayGetValueAtIndex(importedItems, dex);
114 if(CFGetTypeID(item) == SecIdentityGetTypeID())
116 OSStatus status = noErr;
117 importedIdRef = (SecIdentityRef)item;
119 status = SecIdentitySetPreference(importedIdRef, (CFStringRef)name, (CSSM_KEYUSE)0);
120 ok_status(status, "%s: SecIdentitySetPreference", testName);
124 ok(importedIdRef, "%s: identity found?", testName);
128 fail("%s: no items passed to setIdentityPreferenceForImportedIdentity", testName);
129 pass("test numbers match");
133 static void removeIdentityPreference(bool test) {
134 // Clean up the identity preference, since it's in the default keychain
135 CFMutableDictionaryRef q = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
136 CFDictionarySetValue(q, kSecClass, kSecClassGenericPassword);
137 q = addLabel(q, CFSTR("kc-28-p12-import@apple.com"));
140 ok_status(SecItemDelete(q), "%s: SecItemDelete (identity preference)", testName);
142 // Our caller doesn't care if this works or not.
150 testP12Import(BOOL extractable, SecKeychainRef keychain, const char *p12Path, CFStringRef password, bool useDeprecatedAPI)
152 OSStatus status = paramErr;
154 NSString *file = [NSString stringWithUTF8String:p12Path];
155 NSData *p12Data = [[NSData alloc] initWithContentsOfFile:file];
156 NSArray *keyAttrs = nil;
157 CFArrayRef outItems = nil;
159 SecExternalFormat externFormat = kSecFormatPKCS12;
160 SecExternalItemType itemType = kSecItemTypeAggregate; // certificates and keys
162 // Decide which parameter structure to use.
163 SecKeyImportExportParameters keyParamsOld; // for SecKeychainItemImport, deprecated as of 10.7
164 SecItemImportExportKeyParameters keyParamsNew; // for SecItemImport, 10.7 and later
166 void *keyParamsPtr = (useDeprecatedAPI) ? (void*)&keyParamsOld : (void*)&keyParamsNew;
168 if (useDeprecatedAPI) // SecKeychainItemImport, deprecated as of 10.7
170 SecKeyImportExportParameters *keyParams = (SecKeyImportExportParameters *)keyParamsPtr;
171 memset(keyParams, 0, sizeof(SecKeyImportExportParameters));
172 keyParams->version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
173 keyParams->passphrase = password;
176 // explicitly set the key attributes, omitting the CSSM_KEYATTR_EXTRACTABLE bit
177 keyParams->keyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE;
180 else // SecItemImport, 10.7 and later (preferred interface)
182 SecItemImportExportKeyParameters *keyParams = (SecItemImportExportKeyParameters *)keyParamsPtr;
183 memset(keyParams, 0, sizeof(SecItemImportExportKeyParameters));
184 keyParams->version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
185 keyParams->passphrase = password;
188 // explicitly set the key attributes, omitting kSecAttrIsExtractable
189 keyAttrs = [[NSArray alloc] initWithObjects: (id) kSecAttrIsPermanent, kSecAttrIsSensitive, nil];
190 keyParams->keyAttributes = (__bridge_retained CFArrayRef) keyAttrs;
194 if (useDeprecatedAPI) // SecKeychainItemImport, deprecated as of 10.7
196 status = SecKeychainItemImport((CFDataRef)p12Data,
200 0, /* flags not used (yet) */
203 (CFArrayRef*)&outItems);
204 ok_status(status, "%s: SecKeychainItemImport", testName);
206 else // SecItemImport
208 status = SecItemImport((CFDataRef)p12Data,
212 0, /* flags not used (yet) */
215 (CFArrayRef*)&outItems);
216 ok_status(status, "%s: SecItemImport", testName);
219 verifyPrivateKeyExtractability(extractable, (__bridge NSArray*) outItems);
221 checkN(testName, createQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, CFSTR("test_import")), 1);
222 checkN(testName, addLabel(makeBaseQueryDictionary(keychain, kSecClassCertificate), CFSTR("test_import")), 1);
224 setIdentityPreferenceForImportedIdentity(keychain, @"kc-28-p12-import@apple.com", (__bridge NSArray*) outItems);
226 deleteItems(outItems);
228 CFReleaseNull(outItems);
233 int kc_28_p12_import(int argc, char *const *argv)
236 initializeKeychainTests(__FUNCTION__);
238 SecKeychainRef kc = getPopulatedTestKeychain();
240 removeIdentityPreference(false); // if there's still an identity preference in the keychain, we'll get prompts. Delete it pre-emptively (but don't test about it)
242 writeFile(keychainTempFile, test_import_p12, test_import_p12_len);
243 testP12Import(true, kc, keychainTempFile, CFSTR("password"), false);
244 testP12Import(true, kc, keychainTempFile, CFSTR("password"), true);
246 testP12Import(false, kc, keychainTempFile, CFSTR("password"), false);
247 testP12Import(false, kc, keychainTempFile, CFSTR("password"), true);
249 ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", testName);
252 removeIdentityPreference(true);
254 checkPrompts(0, "No prompts while importing items");