Objective c 将客户端标识复制到Mac OS上的私钥链 背景

Objective c 将客户端标识复制到Mac OS上的私钥链 背景,objective-c,swift,macos,security,keychain,Objective C,Swift,Macos,Security,Keychain,我正在使用客户身份的大型多平台库中工作。要求是应用程序私下维护客户机标识,并且可以从系统存储导入客户机标识(在Mac OS默认密钥链的情况下) 所以基本上API必须包括以下内容 从默认密钥链设置客户端标识 设置客户端标识表单文件(系统keychian不应看到它) 删除客户端标识(这不应破坏其他应用程序) 如果将从默认密钥链中删除标识(例如使用密钥链访问),则我的应用程序不应受到影响 基本问题 起初,我计划将身份存储在一些私人加密文件中,但事实证明,苹果API不允许在没有密钥链的情况下导入客户

我正在使用客户身份的大型多平台库中工作。要求是应用程序私下维护客户机标识,并且可以从系统存储导入客户机标识(在Mac OS默认密钥链的情况下)

所以基本上API必须包括以下内容

  • 从默认密钥链设置客户端标识
  • 设置客户端标识表单文件(系统keychian不应看到它)
  • 删除客户端标识(这不应破坏其他应用程序)
  • 如果将从默认密钥链中删除标识(例如使用密钥链访问),则我的应用程序不应受到影响
基本问题 起初,我计划将身份存储在一些私人加密文件中,但事实证明,苹果API不允许在没有密钥链的情况下导入客户端身份

所以我决定我的库将维护私钥链。在导入身份表单文件的情况下,它非常简单,并且确实有效:

NSDictionary *options =  
@{  
    (id)kSecImportExportPassphrase:password.stringValue,  
    (id)kSecImportExportKeychain:(__bridge id)keychain,  
};  

CFArrayRef arrayResult = NULL;  
OSStatus status = SecPKCS12Import((CFDataRef)pkcs12,  
                                  (CFDictionaryRef)options,  
                                  &arrayResult);  
if (status != errSecSuccess)  
现在的问题是如何将客户身份从默认密钥复制到私有密钥链

下面是我用来创建钥匙链的代码:

- (void)setupOpenMyKeychain  
{  
    NSString *keychainPath = [self keychainPath];  
    NSString *pass = @"dasndiaisdfs"; // used only for testing  

    OSStatus status = SecKeychainCreate(keychainPath.UTF8String,  
                                        (UInt32)strlen(pass.UTF8String),  
                                        pass.UTF8String,  
                                        NO,  
                                        NULL,  
                                        &keychain);  

    if (status == errSecDuplicateKeychain)  
    {  
        status = SecKeychainOpen(keychainPath.UTF8String, &keychain);  
        if (status == errSecSuccess)  
        {  
            status = SecKeychainUnlock(keychain,  
                                       (UInt32)strlen(pass.UTF8String),  
                                       pass.UTF8String,  
                                       TRUE);  
            if (status != errSecSuccess)  
            {  
                [self showOSStatusFailureAlert: status forAction: @"Unlock failure"];  
            }  
        }  
    }  
    if (status != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: status forAction: @"Open/Create failure"];  
    }  
}  
现在,我在默认密钥链中有SecIdentityRef和SecCertificate(在CFArrayRef中),我想将其复制到我的自定义密钥链:

- (NSData *)persistantRefFor: (id)item  
{  
    CFTypeRef result = NULL;  
    NSDictionary *dic =  
    @{  
      (id)kSecValueRef:(id)item,  
      (id)kSecReturnPersistentRef:(id)kCFBooleanTrue,  
      //(id)kSecUseKeychain:(__bridge id)keychain,  
      };  

    OSStatus status = SecItemCopyMatching((CFDictionaryRef)dic,  
                                          &result);  

    if (status != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: status  
                             forAction: @"Failed to find reference"];  
        return nil;  
    }  
    return (__bridge_transfer NSData *)result;  
}  

-(OSStatus)copyItemWithPersistantRef:(NSData *)persistantReference  
{  
    SecKeychainItemRef item = NULL;  
    OSStatus result = SecKeychainItemCopyFromPersistentReference((__bridge CFDataRef)persistantReference, &item);  
    if (result != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: result forAction: @"Get item from reference"];  
        return result;  
    }  
    CFAutorelease(item);  

    SecKeychainRef soruceKeychain = NULL;  
    result = SecKeychainItemCopyKeychain(item, &soruceKeychain);  
    if (result == errSecSuccess)  
    {  
        if (soruceKeychain == keychain)  
        {  
            CFRelease(soruceKeychain);  
            // item is already in desired keychain  
            return result;  
        }  
    } else {  
        [self showOSStatusFailureAlert: result forAction: @"Fetching source keychain"];  
        return result;  
    }  

    SecAccessRef access = NULL;  
    result = SecKeychainCopyAccess(keychain, &access);  
    if (result != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: result forAction: @"Get Access object"];  
        // return result;  
    } else {  
        CFAutorelease(access);  
    }  

    SecKeychainItemRef itemCopy = NULL;  
    result = SecKeychainItemCreateCopy(item, keychain, access, &itemCopy);  
    if (result != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: result forAction: @"Create copy in private keychain"];  
        return result;  
    }  
    CFAutorelease(itemCopy);  

    NSLog(@"Copied item %@", itemCopy);  
    NSData *copyPersistantRef = [self persistantRefFor: (__bridge id)itemCopy];  
    NSLog(@"Old persisntatn reference %@\n"  
           "New persisntatn reference %@\n"  
           "%@", persistantReference, copyPersistantRef,  
          [persistantReference isEqualToData: copyPersistantRef]?@"SAME":@"DIFFRENT");  

    return result;  
}  

- (IBAction)copyIdentityToPrivateKeychain:(id)sender  
{  
    if (!identity)  
    {  
        return;  
    }  
    NSData *perRef = [self persistantRefFor: (__bridge id)identity];  

    OSStatus status = [self copyItemWithPersistantRef: perRef];  
    if (status != errSecSuccess)  
    {  
        [self showOSStatusFailureAlert: status forAction: @"Copy Identity has failed"];  
    }  
}  
尝试将SecCertificateRef复制到我的私钥链时。发生了以下事情:

  • SecKeychainCopyAccess失败,出现“错误:0xFFFFFC-4函数或操作未实现。”-代码继续执行,因为当到达SecKeychainItemCreateCopy时,明显为空值是可以的(文档提示应该可以工作)
  • SecKeychainItemCreateCopy通过Ok
  • NSData*copyPersistantRef=[self-peristantRefor:(_桥id)itemCopy]失败。我无法获取对复制项目的永久引用。没有此引用,我无法在应用程序启动时加载正确的证书
  • 在尝试复制SecIdentityRef时情况更糟

  • SecKeychainItemCopyKeychain上的代码失败,错误为:0xFFFF9D28-25304指定的项不再有效。它可能已从钥匙链中删除。
  • 当我强制跳过此错误以到达SecKeychainItemCreateCopy时,它失败,并出现相同的错误:
    错误:0xFFFF9D28-25304指定的项不再有效。它可能已从钥匙链中删除。
  • 所以,根据文档,底线是它应该工作,但它不工作。可能我做了一些不正确的事情,但我无法找到问题所在

    任何反馈都将不胜感激

    我也问过同样的问题

    需要支持OSX10.10


    这是一个用于快速测试的工具。

    使用
    SecPKCS12Import
    ,而不是
    secitemport
    。将
    kSecFormatPKCS12
    作为inputFormat传递,
    kSecItemTypeAggregate
    作为itemType传递(以要求标识或
    kSecItemTypeUnknown
    ,它将尝试为您检测该类型),并将importKeychain设置为
    NULL
    ,这将阻止导入到keychain