使用iOS Secure Enclave时遇到解密失败,导致部分用户在生产环境中解密失败,影响大部分手机和系统功能。
错误信息是:
解密:解密数据失败:可选(错误域=NSOSStatusErrorDomain代码=-50“ECIES:无法aes-gcm解密数据(err -69)”UserInfo = {numberOfErrorsDeep = 0,NSDescription = ECIES:无法aes-gcm解密数据(错误-69)}),数据长度:113
我的代码:
static func getPrivateKey(keyName: String) -> (key: SecKey?, errorMsg: String?) {
var key: SecKey?
key = loadKey(name: keyName)
if let key = key { return (key, nil) }
let keyInfo = makeAndStoreKey(keyName: keyName)
if let key = keyInfo.key { return (key, nil) }
return (nil, keyInfo.errorMsg)
}
static func makeAndStoreKey(keyName: String, requiresBiometry: Bool = false) -> (key: SecKey?, errorMsg: String?) {
removeKey(name: keyName)
let flags: SecAccessControlCreateFlags
if #available(iOS 11.3, *) {
flags = requiresBiometry ?
[.privateKeyUsage, .biometryCurrentSet] : .privateKeyUsage
} else {
flags = requiresBiometry ?
[.privateKeyUsage, .touchIDCurrentSet] : .privateKeyUsage
}
let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
flags,
nil)
guard let tag = keyName.data(using: .utf8), let access = access else {
return (nil, Crypto.Err.accessOrTagFail.errorDescription)
}
let attributes: [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecAttrKeySizeInBits: 256,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs: [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: tag,
kSecAttrAccessControl: access
]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
return (nil, "SecKeyCreateRandomKeyFail: \((error?.takeRetainedValue() as? Error)?.localizedDescription ?? "")")
}
return (privateKey, nil)
}
static func loadKey(name: String) -> SecKey? {
guard let tag = name.data(using: .utf8) else { return nil }
let query: [CFString: Any] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: tag,
kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecReturnRef: true
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess, let item = item else { return nil }
return item as! SecKey
}
static func removeKey(name: String) {
guard let tag = name.data(using: .utf8) else { return }
let query: [CFString: Any] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: tag
]
SecItemDelete(query as CFDictionary)
}
static func encrypt(data: Data, keyName: String) -> Data? {
var tmpData: Data?
var errMsg: String?
defer {
if tmpData == nil {
self.updateDeviceDecryptFail()
self.errorCallBack?(.masterKey(state: .deviceEncryptErr(errMsg: errMsg)))
}
}
guard #available(iOS 11.0, *) else { return nil }
let keyInfo = getPrivateKey(keyName: keyName)
guard let key = keyInfo.key else {
errMsg = "encrypt: \(Crypto.Err.getPrivateKeyFail.errorDescription ?? "") \(keyInfo.errorMsg ?? "")"
return nil
}
guard let publicKey = SecKeyCopyPublicKey(key) else {
errMsg = "encrypt: \(Crypto.Err.getPublicKeyFail.errorDescription ?? "")"
return nil
}
let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM
guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
errMsg = "encrypt: \(Crypto.Err.isAlgorithmSupportedFail.errorDescription ?? "")"
return nil
}
var error: Unmanaged<CFError>?
let cipherData = SecKeyCreateEncryptedData(publicKey,
algorithm,
data as CFData,
&error) as Data?
guard let cipherData = cipherData else {
errMsg = "encrypt: \(Crypto.Err.encryptedDataFail(error: (error?.takeRetainedValue() as? Error)).errorDescription ?? "")"
return nil
}
tmpData = cipherData
return cipherData
}
static func decrypt(encryptedData: Data, keyName: String) -> Data? {
var tmpData: Data?
var errMsg: String?
defer { if tmpData == nil { self.errorCallBack?(.masterKey(state: .deviceDecryptErr(errMsg: errMsg))) } }
guard #available(iOS 11.0, *) else { return nil }
let keyInfo = getPrivateKey(keyName: keyName)
guard let key = keyInfo.key else {
errMsg = "decrypt: \(Crypto.Err.getPrivateKeyFail.errorDescription ?? "") \(keyInfo.errorMsg ?? "")"
return nil
}
let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM
guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else {
errMsg = "decrypt: \(Crypto.Err.isAlgorithmSupportedFail.errorDescription ?? "")"
return nil
}
var error: Unmanaged<CFError>?
let data = SecKeyCreateDecryptedData(key,
algorithm,
encryptedData as CFData,
&error) as Data?
guard let data = data else {
errMsg = "decrypt: \(Crypto.Err.decryptedDataFail(error: (error?.takeRetainedValue() as? Error)).errorDescription ?? ""), DataLength: \(encryptedData.count)"
return nil
}
tmpData = data
return data
}
我在网上找了很久,但没有用。请帮忙或尝试给出一些如何实现这一目标的想法。
我只在一台设备上突然遇到同样的问题。我们有解决方案吗?