因此OS X Keychain有三条信息:
我显然总是知道ServiceName。有没有办法找到该ServiceName的任何已保存用户名? (一旦知道用户名,就很容易找到密码。)
我更喜欢使用一个漂亮的Cocoa包装器,例如EMKeychain来执行此操作。但EMKeychain要求UserName获取任何钥匙串项目!
+ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceNameString withUsername:(NSString *)usernameString;
如果您需要用户名来查找凭据,您希望如何充分利用钥匙串中的保存凭据?将用户名保存在.plist文件中的最佳做法是什么?
答案 0 :(得分:6)
SecKeychainFindGenericPassword
只返回一个钥匙串项。要查找特定服务的所有通用密码,您需要在钥匙串上运行查询。根据您定位的OS X版本,有几种方法可以做到这一点。
如果您需要在10.5或更低版本上运行,则需要使用SecKeychainSearchCreateFromAttributes
。这是一个相当可怕的API。下面是一个方法的粗略方法,它返回一个将用户名映射到密码的字典。
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
OSStatus status;
// Construct a query.
const char *utf8Service = [service UTF8String];
SecKeychainAttribute attr = { .tag = kSecServiceItemAttr,
.length = strlen(utf8Service),
.data = (void *)utf8Service };
SecKeychainAttribute attrList = { .count = 1, .attr = &attr };
SecKeychainSearchRef *search = NULL;
status = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attrList, &search);
if (status) {
report(status);
return nil;
}
// Enumerate results.
NSMutableDictionary *result = [NSMutableDictionary dictionary];
while (1) {
SecKeychainItemRef item = NULL;
status = SecKeychainSearchCopyNext(search, &item);
if (status)
break;
// Find 'account' attribute and password value.
UInt32 tag = kSecAccountItemAttr;
UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
SecKeychainAttributeInfo info = { .count = 1, .tag = &tag, .format = &format };
SecKeychainAttributeList *attrList = NULL;
UInt32 length = 0;
void *data = NULL;
status = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &attrList, &length, &data);
if (status) {
CFRelease(item);
continue;
}
NSAssert(attrList->count == 1 && attrList->attr[0].tag == kSecAccountItemAttr, @"SecKeychainItemCopyAttributesAndData is messing with us");
NSString *account = [[[NSString alloc] initWithBytes:attrList->attr[0].data length:attrList->attr[0].length encoding:NSUTF8StringEncoding] autorelease];
NSString *password = [[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] autorelease];
[result setObject:password forKey:account];
SecKeychainItemFreeAttributesAndData(attrList, data);
CFRelease(item);
}
CFRelease(search);
return result;
}
对于10.6及更高版本,您可以使用稍微不那么不方便的SecItemCopyMatching
API:
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
kSecClassGenericPassword, kSecClass,
(id)kCFBooleanTrue, kSecReturnData,
(id)kCFBooleanTrue, kSecReturnAttributes,
kSecMatchLimitAll, kSecMatchLimit,
service, kSecAttrService,
nil];
NSArray *itemDicts = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)q, (CFTypeRef *)&itemDicts);
if (status) {
report(status);
return nil;
}
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSDictionary *itemDict in itemDicts) {
NSData *data = [itemDict objectForKey:kSecValueData];
NSString *password = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
NSString *account = [itemDict objectForKey:kSecAttrAccount];
[result setObject:password forKey:account];
}
[itemDicts release];
return result;
}
对于10.7或更高版本,您可以使用我精彩的LKKeychain框架(PLUG!)。它不支持构建基于属性的查询,但您只需列出所有密码并过滤掉您不需要的密码。
- (NSDictionary *)genericPasswordsWithService:(NSString *)service {
LKKCKeychain *keychain = [LKKCKeychain defaultKeychain];
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (LKKCGenericPassword *item in [keychain genericPasswords]) {
if ([service isEqualToString:item.service]) {
[result setObject:item.password forKey:item.account];
}
}
return result;
}
(我没有尝试运行,甚至没有编译任何上述代码示例;抱歉任何错别字。)
答案 1 :(得分:2)
您不需要用户名。你使用EMKeychain,但这是该类所强加的人为区别; the underlying Keychain Services function不需要用户名来查找钥匙串项目。
直接使用SecKeychainFindGenericPassword
时,请传递0
和NULL
作为用户名参数。它将返回该服务上存在的 钥匙串项。
然而,这只会返回一个项目。如果用户在同一服务上有多个钥匙串项,你就不会知道,或者你得到了哪一个(文档说它返回“第一个”匹配项,没有说明它首先考虑的内容)。如果您想要该服务的任何和所有项目,您应该create a search并使用它。
答案 2 :(得分:0)
通用密码具有服务名称和用户名的唯一键。因此,要获取单个通用钥匙串条目,您需要同时提供这两个条目。但是,您可以使用SecKeychainFindGenericPassword
函数迭代给定服务的所有通用钥匙串条目。
(免责声明:我对EMKeychain中的任何事情一无所知。)