Swift中无法从Keychain获取已存储私钥的问题
问题
把RSA私钥存入Keychain时提示存储成功,但调用获取方法时,返回状态为errSecSuccess却拿不到密钥(item为nil)。相关代码如下:
public func storeKeyToKeychain(_ key: SecKey, tag: String) -> Bool { let deleteQuery: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)! ] SecItemDelete(deleteQuery as CFDictionary) // Clean existing key before adding let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)!, kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecValueRef as String: key, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked ] let status = SecItemAdd(query as CFDictionary, nil) if status != errSecSuccess { print("Key storage failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))") } else { print("Key stored successfully.") } return status == errSecSuccess } public func getKeyFromKeychain(tag: String) -> SecKey? { let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)!, // Ensure tag is Data kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecReturnRef as String: true // Retrieve the SecKey reference ] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) if status == errSecSuccess, let key = item as! SecKey? { print("Successfully retrieved SecKey.") return key } else if status == errSecItemNotFound { print("Key not found in the Keychain.") } else { print("Keychain retrieval failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))") } return nil }
解决方案
问题核心是查询参数缺少私钥的类属性标记,Keychain无法精准匹配到存储的私钥。存储RSA私钥时,Keychain会自动关联kSecAttrKeyClassPrivate属性,但查询时必须显式指定这个属性才能正确匹配。
修改后的代码
1. 优化存储方法(添加私钥类属性,确保属性完整)
public func storeKeyToKeychain(_ key: SecKey, tag: String) -> Bool { let deleteQuery: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)! ] SecItemDelete(deleteQuery as CFDictionary) let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)!, kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // 明确指定私钥类 kSecValueRef as String: key, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked ] let status = SecItemAdd(query as CFDictionary, nil) if status != errSecSuccess { print("Key storage failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))") } else { print("Key stored successfully.") } return status == errSecSuccess }
2. 修复获取方法(添加私钥类属性)
public func getKeyFromKeychain(tag: String) -> SecKey? { let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrApplicationTag as String: tag.data(using: .utf8)!, kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // 必须添加此属性匹配私钥 kSecReturnRef as String: true ] var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) if status == errSecSuccess, let key = item as? SecKey { print("Successfully retrieved SecKey.") return key } else if status == errSecItemNotFound { print("Key not found in the Keychain.") } else { print("Keychain retrieval failed with status: \(status) (\(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString))") } return nil }
说明
- Keychain的查询是属性精确匹配,存储和查询的属性必须完全对应,否则即使返回
errSecSuccess,也可能无法返回正确的条目 - 添加
kSecAttrKeyClass: kSecAttrKeyClassPrivate后,Keychain能明确识别要查找的是RSA私钥,而非公钥或其他类型密钥
内容的提问来源于stack exchange,提问作者pankaj
相关产品推荐
相关产品推荐

