You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:55:41