iOS开发中Secure Enclave添加生物识别标志时密钥生成失败求助
问题:Secure Enclave添加生物识别标志生成密钥失败(错误码-25293)
我是iOS开发新手,近期开发一款需在Secure Enclave中创建密钥并签名数据的应用。遇到以下问题:
- 当在
SecAccessControlCreateWithFlags中添加.biometryAny或.biometryCurrentSet标志时,密钥生成失败,返回错误码-25293 - 虽然触发了生物识别认证流程,但认证完成后仍抛出错误
- 开发环境为Xcode iPhone15模拟器,已录入FaceID且动画正常
- 移除生物识别标志后可正常生成密钥,但希望保留最高安全性
- Info.plist已添加FaceID隐私权限(
NSFaceIDUsageDescription)
可正常运行的代码
import UIKit import Security import LocalAuthentication import CommonCrypto func sha256(data: Data) -> Data { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) } return Data(hash) } class ViewController: UIViewController { let inputTextField = UITextField() let encryptButton = UIButton() let outputLabel = UILabel() override func viewDidLoad() { super.viewDidLoad() setupUI() } func setupUI() { // Input TextField setup inputTextField.placeholder = "Enter a random number" inputTextField.borderStyle = .roundedRect inputTextField.translatesAutoresizingMaskIntoConstraints = false view.addSubview(inputTextField) // Encrypt Button setup encryptButton.setTitle("Encrypt", for: .normal) encryptButton.backgroundColor = .blue encryptButton.translatesAutoresizingMaskIntoConstraints = false encryptButton.addTarget(self, action: #selector(encryptAction), for: .touchUpInside) view.addSubview(encryptButton) // Output Label setup outputLabel.text = "Encrypted output will appear here" outputLabel.numberOfLines = 0 outputLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(outputLabel) NSLayoutConstraint.activate([ inputTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor), inputTextField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), inputTextField.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8), encryptButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), encryptButton.topAnchor.constraint(equalTo: inputTextField.bottomAnchor, constant: 20), outputLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), outputLabel.topAnchor.constraint(equalTo: encryptButton.bottomAnchor, constant: 20), outputLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8) ]) } func authenticateUser(completion: @escaping (Bool, Error?) -> Void) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { let reason = "Biometric authentication is needed to access your secure data." context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in DispatchQueue.main.async { completion(success, authenticationError) } } } else { // Biometry is not available or not enrolled. DispatchQueue.main.async { completion(false, error) } } } @objc func encryptAction() { authenticateUser { [weak self] (success, error) in guard success else { self?.outputLabel.text = "Authentication failed: \(error?.localizedDescription ?? \"Unknown error\")" return } guard let randomNumber = self?.inputTextField.text, !randomNumber.isEmpty, let dataToSign = randomNumber.data(using: .utf8), let privateKey = self?.generatePrivateKey() else { self?.outputLabel.text = "Error: Could not generate private key." return } if let signature = self?.signData(privateKey: privateKey, data: dataToSign) { self?.outputLabel.text = "Signature: \(signature.base64EncodedString())" } else { self?.outputLabel.text = "Error: Could not sign data." } } } func generatePrivateKey() -> SecKey? { // 1. Create Keys Access Control guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil) else { fatalError("cannot set access control") } // 2. Create Key Attributes guard let tag = "com.example.keys.mykey".data(using: .utf8) else { fatalError("cannot set tag") } let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits as String: 256, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: tag, kSecAttrAccessControl as String: accessControl ] ] // 3. Generate Key Pairs var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Error creating a key: \(error)") } return nil } return privateKey } func signData(privateKey: SecKey, data: Data) -> Data? { let digest = sha256(data: data) var error: Unmanaged<CFError>? guard let signature = SecKeyCreateSignature(privateKey, .ecdsaSignatureMessageX962SHA256, digest as CFData, &error) as Data? else { print(error!.takeRetainedValue() as Error) return nil } return signature } }
报错的代码片段
guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage, .biometryCurrentSet], nil) else {
触发错误的代码位置
var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Error creating a key: \(error)") } return nil }
错误信息
Error creating a key: Error Domain=NSOSStatusErrorDomain Code=-25293 "Key generation failed, error -25293" UserInfo={numberOfErrorsDeep=0, NSDescription=Key generation failed, error -25293}
解决方案
1. 核心原因:模拟器不支持带生物识别约束的Secure Enclave密钥
iPhone模拟器的FaceID仅为模拟交互,无法真正与Secure Enclave的生物验证机制绑定。带.biometryAny/.biometryCurrentSet标志的密钥生成只能在真实iOS设备上运行,这是错误码-25293的主要诱因。
2. 真实设备适配代码优化
在真实设备上实现该功能,需要将已通过生物识别的LAContext传入密钥生成方法,否则会触发权限校验失败:
修改密钥生成方法
func generatePrivateKey(with context: LAContext) -> SecKey? { // 1. Create Keys Access Control guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage, .biometryCurrentSet], nil) else { fatalError("cannot set access control") } // 2. Create Key Attributes guard let tag = "com.example.keys.mykey".data(using: .utf8) else { fatalError("cannot set tag") } let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeySizeInBits as String: 256, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: tag, kSecAttrAccessControl as String: accessControl ] ] // 3. 传入已认证的LAContext生成密钥 var error: Unmanaged<CFError>? let parameters: [String: Any] = [kSecUseAuthenticationContext as String: context] guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, parameters as CFDictionary, &error) else { if let error = error?.takeRetainedValue() { print("Error creating a key: \(error)") } return nil } return privateKey }
修改认证与密钥调用逻辑
@objc func encryptAction() { let context = LAContext() authenticateUser(with: context) { [weak self] (success, error) in guard success else { self?.outputLabel.text = "Authentication failed: \(error?.localizedDescription ?? \"Unknown error\")" return } guard let randomNumber = self?.inputTextField.text, !randomNumber.isEmpty, let dataToSign = randomNumber.data(using: .utf8), let privateKey = self?.generatePrivateKey(with: context) else { self?.outputLabel.text = "Error: Could not generate private key." return } if let signature = self?.signData(privateKey: privateKey, data: dataToSign) { self?.outputLabel.text = "Signature: \(signature.base64EncodedString())" } else { self?.outputLabel.text = "Error: Could not sign data." } } } // 调整认证方法,传入LAContext func authenticateUser(with context: LAContext, completion: @escaping (Bool, Error?) -> Void) { var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { let reason = "Biometric authentication is needed to access your secure data." context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError in DispatchQueue.main.async { completion(success, authenticationError) } } } else { DispatchQueue.main.async { completion(false, error) } } }
3. 模拟器调试临时方案
如果需要在模拟器上调试其他逻辑,可通过条件编译跳过生物识别约束:
let accessControlFlags: SecAccessControlCreateFlags #if targetEnvironment(simulator) accessControlFlags = [.privateKeyUsage] #else accessControlFlags = [.privateKeyUsage, .biometryCurrentSet] #endif guard let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, accessControlFlags, nil) else { fatalError("cannot set access control") }
内容的提问来源于stack exchange,提问作者Vitaly
相关产品推荐
相关产品推荐

