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

使用CryptoKit结合nonce和tag时出现认证失败问题

问题:CryptoKit解密抛出CryptoKitError.authenticationFailure错误

解密过程(倒数第二行代码)抛出以下错误:

Fatal error: 'try!' expression unexpectedly raised an error: CryptoKit.CryptoKitError.authenticationFailure

Swift代码如下:

func encryptDecryptWithNonceTag(){
    let secret = "my-xxx-bit-secret-my-secret-my-s"
    let mySymKey = SymmetricKey(data: secret.data(using: .utf8)!)
    let plain = "Say hello to my little friend!"
    let nonce = try! AES.GCM.Nonce(data: Data(base64Encoded: "fv1nixTVoYpSvpdA")!)
    let tag =  Data(base64Encoded: "e1eIgoB4+lA/j3KDHhY4BQ==")!
    
    //ENCRYPT
    let sealedBox = try! AES.GCM.seal(plain.data(using: .utf8)!, using: mySymKey, nonce: nonce, authenticating: tag)
    let ciphertext = sealedBox.ciphertext.base64EncodedString()
    print("ciphertext: \(ciphertext)")
    
    
    //DECRYPT: Recreate sealedbox with nonce and tag and then decrypt
    let sealedBoxRecreated = try! AES.GCM.SealedBox(nonce: nonce,
                                                    ciphertext: Data(base64Encoded: ciphertext)!,
                                                    tag: tag)
    let decrypted = try! AES.GCM.open(sealedBoxRecreated, using: mySymKey)
    print("decryptedtext:\(String(decoding: decrypted, as: UTF8.self))")
}

问题原因与修复方案

你犯了两个核心错误:

  1. 混淆了authenticating参数的用途
    AES-GCM的authenticating参数是用来传入**附加认证数据(AAD)**的,不是提前指定tag。tag是加密过程中CryptoKit自动生成的完整性校验值,不能手动传入固定值。你把外部tag当作AAD传入加密方法,导致生成的sealedBox自带的tag和你手动传入的完全不匹配,解密时触发认证失败。

  2. 解密时错误复用外部tag
    加密生成的sealedBox本身已经包含了正确的tag,你应该使用这个自动生成的tag,而非一开始传入的外部tag来重建sealedBox。

修复后的代码

func encryptDecryptWithNonceTag(){
    let secret = "my-xxx-bit-secret-my-secret-my-s"
    guard let secretData = secret.data(using: .utf8) else {
        print("Secret转Data失败")
        return
    }
    let mySymKey = SymmetricKey(data: secretData)
    let plain = "Say hello to my little friend!"
    guard let plainData = plain.data(using: .utf8) else {
        print("明文转Data失败")
        return
    }
    // 测试场景固定nonce,生产环境建议用随机生成:AES.GCM.Nonce()
    guard let nonceData = Data(base64Encoded: "fv1nixTVoYpSvpdA"),
          let nonce = try? AES.GCM.Nonce(data: nonceData) else {
        print("无效nonce")
        return
    }
    
    // 加密:移除错误的authenticating参数,如需AAD请传入实际附加数据
    guard let sealedBox = try? AES.GCM.seal(plainData, using: mySymKey, nonce: nonce) else {
        print("加密失败")
        return
    }
    let ciphertext = sealedBox.ciphertext.base64EncodedString()
    let generatedTag = sealedBox.tag.base64EncodedString()
    print("ciphertext: \(ciphertext)")
    print("自动生成的tag: \(generatedTag)")
    
    // 解密:使用加密生成的tag重建sealedBox
    guard let ciphertextData = Data(base64Encoded: ciphertext),
          let tagData = Data(base64Encoded: generatedTag),
          let sealedBoxRecreated = try? AES.GCM.SealedBox(nonce: nonce,
                                                          ciphertext: ciphertextData,
                                                          tag: tagData) else {
        print("sealedBox组件无效")
        return
    }
    
    guard let decryptedData = try? AES.GCM.open(sealedBoxRecreated, using: mySymKey),
          let decryptedText = String(data: decryptedData, encoding: .utf8) else {
        print("解密失败")
        return
    }
    print("decryptedtext:\(decryptedText)")
}

额外提示

  • 绝对不要用try!,操作失败会直接崩溃,应该用try?结合可选绑定或do-catch处理错误。
  • AES-GCM的nonce必须每次加密都随机生成,固定nonce会严重降低安全性。
  • 如果需要使用附加认证数据(AAD),加密和解密时必须传入相同的AAD值,示例:
    // 加密时传入AAD
    let aad = "my-aad-data".data(using: .utf8)!
    let sealedBox = try AES.GCM.seal(plainData, using: mySymKey, nonce: nonce, authenticating: aad)
    // 解密时同步传入AAD
    let decryptedData = try AES.GCM.open(sealedBoxRecreated, using: mySymKey, authenticating: aad)
    

内容的提问来源于stack exchange,提问作者nirav dinmali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:02:41