【问题标题】:Apple keychain store client identity so only my application could access itApple 钥匙串商店客户身份,因此只有我的应用程序可以访问它
【发布时间】:2016-08-04 15:33:10
【问题描述】:

目标

我需要以安全方式将客户端身份存储在 OS X 应用程序上,这样只有我的应用程序才能访问它。没有提示请求权限。

问题

当我尝试存储客户身份时,问题立即出现。 这是代码示例(到目前为止我绑定了什么):

- (BOOL)saveClientIdentity:(SecIdentityRef)clientIdentity error:(NSError**) error
{
    NSDictionary *attributes = @{
        (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
        (__bridge id)kSecValueRef:(__bridge id)clientIdentity,
        (__bridge id)kSecAttrApplicationTag:[kMyKeychainAttrApplicationTag dataUsingEncoding: NSUTF8StringEncoding],
        (__bridge id)kSecAttrAccessGroup:kMyKeychainAttrAccessGroup
    };

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
    // status == -25299
    …
}

我不断收到代码 -25299 和工具说明问题:

$ security error -25299
Error: 0xFFFF9D2D -25299 The specified item already exists in the keychain.

因此它尝试覆盖全局客户端身份(我从未成功为此应用程序编写客户端身份,因此不应该发生这种冲突),我不知道该怎么做。 它必须是私有的,只有这个应用程序。

我验证了相应加载代码会发生什么。它加载了我的开发者身份,我不希望这样。

- (SecIdentityRef)clientIdentity
{
    NSDictionary *attributes =
    @{
      (__bridge id)kSecClass:(__bridge id)kSecClassIdentity,
      (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
      (__bridge id)kSecAttrApplicationTag:[kMyKeychainAttrApplicationTag dataUsingEncoding: NSUTF8StringEncoding],
      (__bridge id)kSecAttrAccessGroup:kMyKeychainAttrAccessGroup
      };

    CFTypeRef universalResult = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes, &universalResult);
    SecIdentityRef result = (SecIdentityRef)universalResult;
    if (result)
    {
        CFAutorelease(result);
    }
    if (status != noErr)
    {
        NSLog(@"Failed to load client identity: %@", NSErrorFromStatusErrorCode(status));
    }
    return result;
}

注意事项

我需要为 iOS 使用相同的代码,但这里应该没问题,因为默认情况下 iOS 钥匙串不会在应用程序之间共享。

【问题讨论】:

  • 好吧,看看错误信息。阅读。这应该很明显。您不能添加已存在的项目。有 SecItemUpdate。
  • 再读一遍。我需要特定于应用程序的钥匙链。我从来没有添加过这个钥匙链。这是我第一次使用它。此外,当我更改 kSecAttrApplicationTag 的值时,它会给出相同的结果。我不想覆盖全局客户端身份,我需要仅由我的应用程序使用的客户端身份。单独条目。

标签: objective-c macos security keychain


【解决方案1】:

我找到了很好的解决方案。 诀窍是创建自定义密钥链,然后将客户端身份存储在该密钥链中。

所以基本上有树步骤。

  1. 首先创建或打开自定义钥匙串:

    NSString *keychainpath  = self.customKeychainPath;
    unsigned char password[SHA_DIGEST_LENGTH];
    GenerateCustomKeychainPassword(password);
    OSStatus status = SecKeychainCreate(keychainpath.UTF8String,
                                        SHA_DIGEST_LENGTH,
                                        password,
                                        NO,
                                        NULL,
                                        &customKeychain);
    
    if (status == errSecDuplicateKeychain)
    {
        status = SecKeychainOpen(keychainpath.UTF8String, &customKeychain);
        if (status == errSecSuccess)
        {
            status = SecKeychainUnlock(customKeychain,
                                       SHA_DIGEST_LENGTH,
                                       password,
                                       TRUE);
    
            if (status != errSecSuccess)
            {
                NSLog(@"%s Failed to unlock custom keychain: %@",
                           __PRETTY_FUNCTION__, NSErrorFromStatusErrorCode(status));
            }
        }
    }
    else if (status != errSecSuccess)
    {
        NSLog(@"%s Failed to unlock custom keychain: %@",
                   __PRETTY_FUNCTION__, NSErrorFromStatusErrorCode(status));
    }
    
  2. 将客户身份添加到该钥匙串中

    OSStatus status = errSecSuccess;
    
    CFTypeRef  persistent_ref = NULL;
    NSDictionary *dict = @{
                           (id)kSecValueRef:(id)secItem,
                           (id)kSecReturnPersistentRef:(id)kCFBooleanTrue,
    #if !TARGET_OS_IPHONE
                           (id)kSecUseKeychain:(__bridge id)customKeychain,
    #endif
                           };
    status = SecItemAdd((CFDictionaryRef)dict, &persistent_ref);
    NSCAssert(status != errSecParam, @"Wrong contents of dictionary");
    if (status == errSecDuplicateItem)
    {
        NSLog(@"%s Item: %@ already exists", __PRETTY_FUNCTION__, secItem);
        return NULL;
    }
    return (CFDataRef)persistent_ref;
    
  3. 并从钥匙串中读取项目(persistent_ref 可以存储在用户默认值中)

    NSDictionary *dict = @{
                           (id)kSecClass:(__bridge id)itemType,//kSecClassIdentity,
                           (id)kSecReturnRef:(id)kCFBooleanTrue,
                           (id)kSecValuePersistentRef:persistantRef,
    #if !TARGET_OS_IPHONE
                           (id)kSecUseKeychain:(__bridge id)customKeychain,
    #endif
                           };
    
    OSStatus status =  SecItemCopyMatching((CFDictionaryRef)dict, &result);
    NSCAssert(status != errSecParam, @"Invalid arguments");
    
    return result;
    

【讨论】:

    【解决方案2】:

    我在 SSKeychain 上取得了很大的成功,它最近被 SAMKeychain. 弃用,它适用于 iOS 和 Mac,因此它也应该解决您的跨平台问题。

    【讨论】:

    • 不能使用第三方库,必须是Apple Keychain。
    • SAMKeychain 文档也没有提及任何关于客户身份的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多