如何生成可通过System Keychain访问的Secure Enclave密钥对及证书连接问题
我来帮你解决这个问题,分两个核心方向来梳理方案:
生成可被System Keychain访问的Secure Enclave密钥对
要让Secure Enclave生成的密钥能被System Keychain(以及依赖它的系统服务,比如Wi-Fi/VPN)访问,关键是在密钥生成阶段就指定正确的存储位置和访问控制规则:
1. 针对macOS:显式指定System Keychain存储
首先要获取系统密钥链的引用,确保密钥存在全局的System Keychain而非App沙盒密钥链:
guard let systemKeychain = SecKeychainCopySystemDefault() else { print("Failed to get system keychain reference") return }
2. 创建允许系统进程访问的访问控制对象
访问控制是核心,必须同时开启硬件保护(绑定Secure Enclave)和允许系统进程检索访问:
let accessControl = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, // 仅解锁时可访问,不备份 [.allowHardwareProtection, .allowSystemSearch], nil )!
.allowHardwareProtection:强制密钥存储在Secure Enclave中,无法导出.allowSystemSearch:允许系统级进程(比如Wi-Fi、VPN服务)查找并使用这个密钥
3. 生成并存储密钥对
设置密钥生成参数,把公私钥都存到System Keychain:
let keyParams: [CFString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, // 推荐用ECC,更适合SE kSecAttrKeySizeInBits: 256, kSecAttrTokenID: kSecAttrTokenIDSecureEnclave, // 指定使用Secure Enclave kSecPrivateKeyAttrs: [ kSecAttrIsPermanent: true, kSecAttrKeychain: systemKeychain, kSecAttrAccessControl: accessControl, kSecAttrLabel: "AD-SE-Private-Key" // 自定义标签方便后续查找 ], kSecPublicKeyAttrs: [ kSecAttrIsPermanent: true, kSecAttrKeychain: systemKeychain, kSecAttrLabel: "AD-SE-Public-Key" ] ] var pubKey: SecKey? var privKey: SecKey? let status = SecKeyGeneratePair(keyParams as CFDictionary, &pubKey, &privKey) if status == errSecSuccess { print("Secure Enclave key pair saved to System Keychain successfully") } else { print("Key generation failed with status: \(status)") }
iOS提示:iOS上没有显式的System Keychain引用,只要不设置kSecAttrAccessGroup,密钥会自动存到用户全局密钥链,同样适用上述访问控制规则。
用现有证书连接Wi-Fi/VPN(私钥在App密钥链中)
如果已经生成了密钥在App专属密钥链里,没法直接被系统服务访问,可以试试这两个方案:
1. VPN场景:通过NetworkExtension + XPC共享密钥
创建一个VPN扩展(NetworkExtension框架),通过XPC和你的主应用通信:
- 主应用负责持有Secure Enclave私钥
- 当VPN扩展需要进行TLS签名时,通过XPC向主应用发起请求
- 主应用调用
SecKeyCreateSignature完成签名,把结果返回给扩展 - 扩展用签名结果完成EAP-TLS认证
核心签名代码示例:
func signRequest(data: Data) throws -> Data { guard let privateKey = retrievePrivateKeyFromAppKeychain() else { throw NSError(domain: "KeyError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Private key not found"]) } var error: Unmanaged<CFError>? guard let signature = SecKeyCreateSignature( privateKey, .ecdsaSignatureMessageX962SHA256, data as CFData, &error ) as Data? else { throw error!.takeRetainedValue() as Error } return signature }
2. Wi-Fi场景:优先重新生成密钥到System Keychain
系统Wi-Fi框架的EAP-TLS认证依赖System Keychain中的密钥,没法直接访问App密钥链里的SE密钥。最稳妥的方案是按照第一部分的方法重新生成密钥到System Keychain,然后用新的公钥生成CSR重新申请AD证书。如果无法重新申请,暂时没有完美的 workaround,因为Wi-Fi服务无法直接和第三方App通信获取签名。
内容的提问来源于stack exchange,提问作者MOE
相关产品推荐
相关产品推荐

