我是 iOS 开发新手,最近我正在尝试构建一个应用程序,它将在安全元素内创建一个密钥,然后 - 我会用它唱歌。 在开发过程中,我遇到了一个问题:如果存在标志 .biometryAny 或 .biometryCurrentSet
,则密钥生成失败身份验证本身被触发,但函数仍然抛出错误。
我的设置 - Xcode iPhone15 模拟器,FaceID 注册并且它的动画正在工作。
我尝试删除该标志,同时保留手动授权,这种方法有效,但我仍然希望拥有最大的安全性。
这个有效:
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 {
info.something 文件已更新,并且包含隐私 FaceID 字段。
错误是在这部分触发的:
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
}
错误本身:
创建密钥时出错:错误域=NSOSStatusErrorDomain代码=-25293“密钥生成失败,错误-25293”UserInfo={numberOfErrorsDeep=0,NSDescription=密钥生成失败,错误-25293}
我发现一个问题。该问题是由于模拟器的限制造成的。当我在手机上运行该应用程序时,一切都开始工作了
PS:上面的代码将产生双重 FaceID 检查:)
这就是最终的正确版本:
func generatePrivateKey() -> SecKey? {
// 1. Create Keys Access Control
guard let accessControl =
SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .biometryCurrentSet],
nil)
else {
fatalError("cannot set access control")
}