iOS钥匙串:当更新kSecAttrAccessible时,SecItemUpdate返回-50(paramErr)

6

我需要更新钥匙串条目的kSecAttrAccessible。我不需要更新实际数据,只需要更新可访问性属性。

首先,我尝试查找项目,以确保我的查询字典良好:

sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)(queryPrivateKey), (void *)&privateKeyRef);

这行代码成功地找到了我在查找的项目(返回码为0)。

然后,我使用相同的查询更新了kSecAttrAccessible属性:

if (sanityCheck == noErr && privateKeyRef != nil) {
    // found it, update accessibility
    NSMutableDictionary *updatedAttributes = [[NSMutableDictionary alloc] init];
    updatedAttributes[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAlways;
    OSStatus updateItemStatus = SecItemUpdate((__bridge CFDictionaryRef)queryPrivateKey, (__bridge CFDictionaryRef)updatedAttributes);
}

此时,updateItemStatus为-50(paramErr)。
我查看了这个线程:是否可以更新Keychain项目的kSecAttrAccessible值?然而我的问题是不同的。即使我将kSecValueData添加到我的updatedAttributes中,它也返回-50。此外,文档还指出,我们只需要为iOS 4及更早版本添加kSecValueData。我支持iOS 7及以上版本,所以这不应该是我的问题。
有人能指出我在这里缺少什么吗?非常感谢。
1个回答

6

通过SecItemCopyMatching查询成功找到钥匙串项目并不意味着相同的查询可以用于更新钥匙串项目。

我正在使用以下查询查找项目:

[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
[queryPrivateKey setObject:[EncryptionHelper privateKeyTag:JWT_KEYPAIR_TAG] forKey:(__bridge id<NSCopying>)(kSecAttrApplicationTag)];

sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)(queryPrivateKey), (void *)&privateKeyef);

然而,为了使用该查询来更新项目,我首先必须执行以下操作:

[queryPrivateKey removeObjectForKey:(__bridge id)kSecReturnRef];

然后我可以更新:
OSStatus updateItemStatus = SecItemUpdate((__bridge CFDictionaryRef)queryPrivateKey,(__bridge CFDictionaryRef)updatedAttributes);

显然,在SecItemUpdate的查询字典中,kSecReturnRef不是可接受的键。我找不到SecItemUpdate查询的可接受键列表,从苹果文档中也没有找到。从SecItemUpdate的文档中看来,只有这些键是可接受的,但这似乎不是正确的列表,因为我期望像kSecClass等键也在列表中。如果有人有更新的文档链接,请分享一下,目前只能通过尝试和错误来确定哪些键适用于SecItemUpdate
在找到项目后,更新kSecAttrAccessible时还有另一个复杂性:出于安全原因,当手机处于锁定状态时,您无法从更高的安全设置(如kSecAttrAccessibleWhenUnlocked)更新为较低的设置(如kSecAttrAccessibleAlways),因此迁移必须在手机解锁时进行。迁移的好时机是在应用程序恢复到前台时,因为设备在应用程序在前台时必须处于解锁状态。

我和@SeaJelly遇到了完全相同的问题。我正在使用苹果的Keychain Wrapper,它使用默认的kSecAttrAccessible。我想将其更新为kSecAttrAccessibleWhenUnlocked。但出于某种原因,我似乎无法更新该属性。你能成功地做到这一点吗? - jonypz
抱歉,我的意思是 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly。但是您正在删除密钥 kSecReturnRef。难道您不应该删除 kSecAttrAccessible 并将其设置回所需的设置吗? - jonypz
1
@jonypz 对于第一个参数,您不需要在查询中使用 kSecAttrAccessible 进行查找。我的查询中没有 kSecAttrAccessible。我也没有 kSecReturnRef - 我删除了它,否则查找将失败。然后,使用正确的查询,我将 kSecAttrAccessible 放入第二个参数(更新的属性参数),这样就可以对其进行更新。基本上您只需要在第二个参数中使用 kSecAttrAccessible 而不是第一个参数。 - SeaJelly
明白了。我会尝试去做,然后告诉你进展如何。谢谢。 - jonypz
SecItemUpdate() 不返回任何内容,它接受一个关于要更新的查询和一个描述如何更新它的更新描述,并且唯一的输出值是状态代码。因此,指定任何 kSecReturn... 都没有意义,因为该值如何返回? - Mecki
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接