在 Mac OS X 上列出钥匙串中的条目

Posted

技术标签:

【中文标题】在 Mac OS X 上列出钥匙串中的条目【英文标题】:Listing entries in a keychain on Mac OS X 【发布时间】:2011-12-27 14:21:45 【问题描述】:

过去几天我一直在玩 Cocoa,我想知道如何列出我创建的钥匙串的所有名称/帐户对? Mac OS X 附带的小钥匙链访问应用程序可以做到这一点,所以我想一定有可能吗? SecItemCopyMatching 是我要找的吗?但是,如何指定要搜索的钥匙串?在这种情况下,服务名称是什么?

...我是唯一一个认为 Cocoa 中的 Keychain API 绝对可怕的人吗?在过去的几个小时左右,我一直在上下阅读文档,但我仍然无处可去:-/

【问题讨论】:

我碰巧同意 KeyChain API 非常糟糕,但公平地说,它 不是 Cocoa API,而是一个很好的旧 Carbon API。 OSStatus 返回码是赠品。 【参考方案1】:

您使用SecItemCopyMatching 遍历钥匙串中的项目,并使用SecKeychainFindInternetPassword 或SecKeychainFindGenericPassword 访问密码。

迭代钥匙串

// iterates over keychain and pass every item found by the query to PrintAccount.
static void IterateOverKeychain() 
    // create query
    CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
    CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
    CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);

    // get search results
    CFArrayRef result = nil;
    OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&result);
    assert(status == 0);

    // do something with the result
    CFRange range = CFRangeMake(0, CFArrayGetCount(result));
    CFArrayApplyFunction(result, range, PrintAccount, nil);


// prints the password for a item from the keychain.
static void PrintAccount(const void *value, void *context) 
    CFDictionaryRef dict = value;
    CFStringRef acct = CFDictionaryGetValue(dict, kSecAttrAccount);
    NSLog(@"%@", acct);

打印密码

static void PrintPassword() 
    const char *acct = "foo.bar@googlemail.com";
    UInt32 acctLen = (UInt32)strlen(acct);

    const char *srvr = "calendar.google.com";
    UInt32 srvrLen = (UInt32)strlen(srvr);

    UInt32 pwLen = 0;
    void *pw = 0;

    SecKeychainFindInternetPassword(nil, srvrLen, srvr, 0, nil, acctLen, acct, 0, nil, 0, kSecProtocolTypeAny, kSecAuthenticationTypeAny, &pwLen, &pw, nil);

    CFStringRef pwString = CFStringCreateWithBytes(kCFAllocatorDefault, pw, pwLen, kCFStringEncodingUTF8, NO);
    NSLog(@"%s %@", acct, pwString);

【讨论】:

【参考方案2】:

好吧,我设法枚举了钥匙串条目,但是密码字段为空。我想如果需要授权,程序会像往常一样自动询问钥匙串密码吗?

NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
                       (id)kSecClassInternetPassword, kSecClass,
                       (id)kCFBooleanTrue, kSecReturnData,
                       (id)kCFBooleanTrue, kSecReturnAttributes,
                       kSecMatchLimitAll, kSecMatchLimit,
                       nil];

NSArray *itemDicts = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&itemDicts);
if (status)
    [MessageBox Show:(NSString*)SecCopyErrorMessageString(status, NULL)];

NSMutableArray *arr = [[NSMutableArray alloc] init];

for (NSDictionary *itemDict in itemDicts) 
    NSData *data    = [itemDict objectForKey:(id)kSecValueData];
    NSString *pwd   = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
    NSString *acc   = [itemDict objectForKey:(id)kSecAttrAccount];
    NSString *name  = [itemDict objectForKey:(id)kSecAttrLabel];

    if(acc != nil) 
        NSArray *values = [NSArray arrayWithObjects: (id)name, (id)acc, (id)pwd, nil];

        [arr addObject:(id)values];
    

[itemDicts release];

NSInteger c     = arr.count;
NSString *cnt   = [NSString stringWithFormat:@"%d", c]; 
[MessageBox Show: [arr objectAtIndex:10]];

【讨论】:

我半天的抨击结果是 kSecMatchLimit > 1 与 kSecReturnData 不兼容。您可以在opensource.apple.com 找到 SecItem.cpp 的源代码,您会发现这条评论://如果我们返回所有匹配项,那么我们不支持将密码作为数据获取(这可能需要对每个密码进行身份验证) @bitmusher 已经好几年了,但仍然没有包含在文档中......可能值得单独回答而不是评论。

以上是关于在 Mac OS X 上列出钥匙串中的条目的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式在 OS X 钥匙串中存储对称密钥

Mac怎么取消使用您存储在钥匙串中的机密信息

Mac怎么取消使用您存储在钥匙串中的机密信息

在 icloud 钥匙串中保存密码

如何查看 iPhone 模拟器的钥匙串

mac下查看.mobileprovision文件及钥匙串中证书.cer文件