Swift中如何利用API返回的Tag实现AES GCM解密?
AES-GCM 结合Tag值解密问题解决
我从API响应中获取了带Tag的AES-GCM加密数据,尝试用以下代码结合API返回的Tag解密,需要解决如何通过AES-GCM的Tag值完成数据解密的问题。
用户提供的解密代码:
func aesDecrypt(phrase:String, key: String, iv: String) throws -> String { if let data: Array<UInt8> = (Data(base64Encoded: phrase)?.bytes) { let key: Array<UInt8> = (key.data(using: .utf8)?.bytes)! let iv: Array<UInt8> = (iv.data(using: .utf8)?.bytes)! print("IVVVVVVVV:\(iv)") let tag: Array<UInt8> = (self.tagStr.data(using: .utf8)?.bytes)! let decGCM = GCM(iv: iv, authenticationTag: tag , mode: .detached) let decrypted = try AES(key: key, blockMode: decGCM, padding: .noPadding).decrypt(data) print("DECRYPTED:\(decrypted)") let decryptedData = Data(decrypted) guard let value = String.init(data: decryptedData, encoding: .utf8) else { return "error" } return value } return "" }
使用示例:
let decryptData = try self.aesDecrypt(phrase: responseData, key: key, iv: iv)
核心问题及修正方案
1. Tag的编码处理错误
API返回的Tag通常是Base64或十六进制编码的二进制数据,不是UTF-8文本。直接转UTF-8会导致Tag字节数组错误,验证失败。根据API返回的Tag编码格式调整:
- 如果是Base64编码:
guard let tagData = Data(base64Encoded: self.tagStr), !tagData.isEmpty else { throw NSError(domain: "DecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Tag格式无效"]) } let tagBytes = tagData.bytes - 如果是十六进制编码,需要先实现十六进制转Data的方法,再提取字节数组。
2. 强制解包引发崩溃风险
代码中多处使用!强制解包,一旦参数格式错误会直接崩溃,替换为可选绑定+错误抛出:
- 验证加密数据的Base64格式有效性
- 验证密钥长度符合AES标准(16/24/32字节对应AES-128/192/256)
- 验证IV长度符合GCM推荐的12字节长度
3. 一致性校验
确保解密逻辑和API的加密逻辑完全匹配:
- IV的长度和编码方式
- 密钥的长度和编码方式
- Tag的长度(GCM默认16字节)
- 是否使用了附加认证数据(AAD):如果加密时传入了AAD,解密时必须传入相同的AAD,否则Tag验证失败
修正后的完整代码
func aesDecrypt(phrase: String, key: String, iv: String) throws -> String { // 解析Base64格式的加密数据 guard let phraseData = Data(base64Encoded: phrase), !phraseData.isEmpty else { throw NSError(domain: "DecryptError", code: 2, userInfo: [NSLocalizedDescriptionKey: "加密数据格式无效"]) } let encryptedBytes = phraseData.bytes // 验证密钥有效性:长度必须是16/24/32字节 guard let keyData = key.data(using: .utf8), [16, 24, 32].contains(keyData.count) else { throw NSError(domain: "DecryptError", code: 3, userInfo: [NSLocalizedDescriptionKey: "密钥格式或长度错误(需16/24/32字节)"]) } let keyBytes = keyData.bytes // 验证IV有效性:GCM推荐使用12字节长度的IV guard let ivData = iv.data(using: .utf8), ivData.count == 12 else { throw NSError(domain: "DecryptError", code: 4, userInfo: [NSLocalizedDescriptionKey: "IV格式或长度错误(推荐12字节)"]) } let ivBytes = ivData.bytes // 解析Base64格式的Tag(根据API实际编码调整) guard let tagData = Data(base64Encoded: self.tagStr), !tagData.isEmpty else { throw NSError(domain: "DecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Tag格式无效"]) } let tagBytes = tagData.bytes // 初始化GCM分离模式(密文与Tag分开存储) let gcmMode = GCM(iv: ivBytes, authenticationTag: tagBytes, mode: .detached) let aes = AES(key: keyBytes, blockMode: gcmMode, padding: .noPadding) // 执行解密 let decryptedBytes = try aes.decrypt(encryptedBytes) let decryptedData = Data(decryptedBytes) // 转换为UTF-8字符串 guard let result = String(data: decryptedData, encoding: .utf8) else { throw NSError(domain: "DecryptError", code: 5, userInfo: [NSLocalizedDescriptionKey: "解密后数据无法转成UTF-8字符串"]) } return result }
内容的提问来源于stack exchange,提问作者Mukesh
相关产品推荐
相关产品推荐

