You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:05:04