2011-12-09 54 views
4

所以OS X鑰匙鏈有三條信息:僅使用ServiceName獲取存儲在Keychain中的用戶名?或者:你應該在哪裏存儲用戶名?

  • 服務名稱(我的應用程序的名稱)
  • 用戶名
  • 密碼

我明明總是知道服務名稱。有沒有辦法找到該服務名稱的任何已保存的用戶名? (一旦你知道用戶名,找到密碼很容易。)

我寧願使用一個很好的可可包裝,如EMKeychain來做到這一點。但EMKeychain需要用戶名來獲取任何鑰匙串項目!

+ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceNameString withUsername:(NSString *)usernameString;

你怎麼有望充分利用保存憑據的鑰匙扣,如果你需要的用戶名找到憑據?將用戶名保存在.plist文件或什麼的最佳做法是?

回答

0

通用密碼具有服務名稱和用戶名的唯一鍵。因此,要獲取單個通用鑰匙串條目,您需要提供兩者。但是,您可以使用SecKeychainFindGenericPassword函數遍歷給定服務的所有通用鑰匙串條目。

(免責聲明:我不知道在EMKeychain做這個事情)

+0

這是不正確的。用戶名不是必需的。 –

+0

另外,您究竟如何使用'SecKeychainFindGenericPassword'函數遍歷給定服務的所有通用鑰匙串條目?該函數的文檔說它返回第一個匹配的條目。 –

2

你不需要用戶名。你使用EMKeychain,但這是該類強加的人爲區別; the underlying Keychain Services function不需要用戶名來查找鑰匙串項目。

直接使用SecKeychainFindGenericPassword時,通過0NULL作爲用戶名參數。它將返回a該服務上存在的鑰匙串項目。

但是,那將只返回一個項目。如果用戶在同一服務上有多個鑰匙串項目,則不會知道該項目或您得到的是哪一個(文檔說它返回「第一個」匹配項目,沒有說明它首先考慮的內容)。如果您需要該服務的任何和所有項目,您應該使用create a search並使用它。

+0

> *它將返回該服務中存在的鑰匙串項目。* 此鑰匙串項目以何種形式存儲?在'SecKeychainFindGenericPassword'中,你得到'passwordData - 返回時,一個指向緩衝區的指針,指向保存密碼數據的緩衝區和'itemRef - 返回時,指向通用密碼的項目對象的指針......但沒有類似的用戶名字段?如何在使用該方法時獲取用戶名? – cksubs

+0

呃,我想'SecKeychainItemRef * itemRef'是我需要使用的。但是,我如何從中取出用戶名/密碼/服務? – cksubs

+0

@cksubs http://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/Reference/reference.html#//apple_ref/c/func/SecKeychainItemCopyAttributesAndData –

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; 
} 

(我沒有試運行,甚至編譯任何上述示例代碼的;遺憾的拼寫錯誤。)

+0

看起來像LKKeychain已被重命名爲[LKSecurity ](https://github.com/lorentey/LKSecurity) – bgentry

相關問題