将RSA公钥存储到iOS钥匙串中使用kSecAttrAccessible

4

我知道可以使用下面的代码将RSA密钥存储到密钥链中:

+ (void)savePublicKeyToKeychain:(NSData *)key tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
    NSData *tag = [SecKeyWrapper getKeyTag:tagString];

    NSDictionary *saveDict = @{
            (__bridge id) kSecClass : (__bridge id) kSecClassKey,
            (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
            (__bridge id) kSecAttrApplicationTag : tag,
            (__bridge id) kSecAttrKeyClass : (__bridge id) kSecAttrKeyClassPublic,
            (__bridge id) kSecValueData : key
    };
    [self saveKeyToKeychain:saveDict tag:tagString deleteExisting:deleteExisting];
}

+ (void)saveKeyToKeychain:(NSDictionary *)saveDict tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
    OSStatus sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, NULL);
    if (sanityCheck != errSecSuccess) {
        if (sanityCheck == errSecDuplicateItem && deleteExisting) {
            // delete the duplicate and save again
            SecItemDelete((__bridge CFDictionaryRef) saveDict);
            sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, NULL);
        }
        if (sanityCheck != errSecSuccess) {
            NSLog(@"Problem saving the key to keychain, OSStatus == %d.", (int) sanityCheck);
        }
    }
    // remove from cache
    [keyCache removeObjectForKey:tagString];
}

我可以正确保存和检索这个内容。如果我在保存时尝试设置kSecAttrAccessible值:

+ (void)savePublicKeyToKeychain:(NSData *)key tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
    NSData *tag = [SecKeyWrapper getKeyTag:tagString];

    NSDictionary *saveDict = @{
            (__bridge id) kSecClass : (__bridge id) kSecClassKey,
            (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
            (__bridge id) kSecAttrApplicationTag : tag,
            (__bridge id) kSecAttrKeyClass : (__bridge id) kSecAttrKeyClassPublic,
            (__bridge id) kSecAttrAccessible: (__bridge id) kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
            (__bridge id) kSecValueData : key
    };
    [self saveKeyToKeychain:saveDict tag:tagString deleteExisting:deleteExisting];
}

在存储后尝试检索,结果却是垃圾。这是因为当我保存后立即检索时,插入值和检索值不同。

有人有代码示例或知道如何设置密钥链项目的可访问性,特别是针对RSA密钥吗?


我曾经遇到过类似的问题。结果发现我缺少了kSecAttrKeyClassPublic属性,即使在其他调用中(例如获取密钥引用)只有应用程序标签就足够了。但是对于SecItemAdd,每个属性都很重要。 - Alexey
1个回答

3

为了更好地理解,我们需要知道用于存储钥匙串数据的属性必须与用于检索钥匙串数据的属性完全相同。如果缺少其中一个属性,即使它似乎仅用于存储(例如kSecAttrAccessible),您也会得到错误的数据。您甚至不会收到errSecItemNotFound的提示。它会返回垃圾数据。


这很有道理,你得到的垃圾数据可能是因为它获取了不正确的可访问属性而导致解密错误。 - Joe
@Joe: 实际上,是关键位是垃圾,而不是数据。如果没有任何属性匹配,则该方法应返回 errSecItemNotFound,而不是使用错误的密钥伪装成功。这里没有涉及数据,只涉及密钥的存储和检索。对我来说,应该添加所有属性集的密钥。然后,它应该用与任何匹配的属性检索(如搜索),而不需要插入时使用的所有属性。当前的行为鼓励开发人员使用尽可能少的属性存储密钥,以便检索更容易。 - mikeho

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