SecItemUpdate()不断返回errSecParam,我不知道为什么

SecItemUpdate()不断返回errSecParam,我不知道为什么

问题描述:

这是负责将值添加到加密存储中的整个功能.它在XCode 6的iPhone6模拟器上运行.

Here is the entire function responsible for adding the value into the encrypted store. This is running on the iPhone6 Simulator in XCode 6.

func addOrUpdateGenericPassword(password: String, withService service: String, andAccount account: String, withAccessibility accessibility: SecurityAccessibility) -> ReturnStatus
{
    var sacObject: SecAccessControlRef
    var accessFlag: CFStringRef = kSecAttrAccessibleWhenUnlocked

    switch accessibility
    {
    case SecurityAccessibility.AccessibleAfterFirstUnlock:
        accessFlag = kSecAttrAccessibleAfterFirstUnlock
    case SecurityAccessibility.AccessibleAfterFirstUnlockThisDeviceOnly:
        accessFlag = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
    case SecurityAccessibility.AccessibleWhenUnlocked:
        accessFlag = kSecAttrAccessibleWhenUnlocked
    case SecurityAccessibility.AccessibleWhenUnlockedThisDeviceOnly:
        accessFlag = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
    }

    if touchIDEnabled
    {
        sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessFlag, .USerPresence, nil).takeRetainedValue()
    }
    else
    {
        sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessFlag, nil, nil).takeRetainedValue()
    }

    var dataFromString: NSData = password.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    var secKeys: [AnyObject] = [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData, kSecAttrAccessControl]
    var secValues: [AnyObject] = [kSecClassGenericPassword, service, account, dataFromString, sacObject]
    var update = NSDictionary(objects: secValues, forKeys: secKeys)

    // attempt to get the value first to determine if it exists
    let (currentValue, rStatus) = getGenericPasswordWith(service, andAccount: account)
    if rStatus == ReturnStatus.errSecSuccess
    {
        // Item already exists, so do an Update
        // We need to build a query for the existing value here instead of using the one we built for updating
        var query = buildQueryValueForLookupWith(service, andAccount: account)
        var status = SecItemUpdate(query, update)
        return getReturnStatusCodeForOSStatusCode(status)
    }
    else if rStatus == ReturnStatus.errSecItemNotFound
    {
        // Item does not exist so add it
        var status = SecItemAdd(update, nil)
        return getReturnStatusCodeForOSStatusCode(status)
    }
    else
    {
        // some other error occured and we dont know what is going on!
        println("Unknown errr occured when checking if item already exists.  Status = \(returnStatusCodeToString(rStatus))")
        return ReturnStatus.errSecUnknownError
    }
}
func buildQueryValueForLookupWith(service: String, andAccount account: String) -> NSDictionary
{
    var secKeys: NSArray = [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData]
    var secValues: [AnyObject] = [kSecClassGenericPassword, service, account, true]
    var query = NSDictionary(objects: secValues, forKeys: secKeys)

    return query
}

该代码在我编写的KeychainToolkit类中.这是出现错误时如何调用的示例:

That code is in the KeychainToolkit class I wrote. Here is an example of how I am calling it when I get the error:

var status = keychain.addOrUpdateGenericPassword(value!, withService: "healthBIT.lastSync", andAccount: key, withAccessibility: KeychainToolkit.SecurityAccessibility.AccessibleWhenUnlocked)

在这种情况下,值已经在钥匙串中,并且上面的代码正确地检测到该值,并使用SecItemUpdate()来更新其值.但是,无论我做什么,它总是返回errSecParam,并且我无法弄清楚我的参数哪里错了.

In this case the value is already in the keychain, and the above code properly detects that and uses SecItemUpdate() to just update its value. However it always returns errSecParam no matter what I do and I can't figure out where my params are wrong.

我也尝试过完全删除具有相同结果的新iOS8 kSecAttrAccessControl部分.

I have also tried it with completely removing the new iOS8 kSecAttrAccessControl part, with the same results.

我猜您不希望SecItemUpdate的查询字典中包含kSecReturnData.无法使用该API将数据返回给您.

I'm guessing you do not want kSecReturnData in the query dictionary for SecItemUpdate. There is no way to return the data to you with that API.