使用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))") }
问题原因与修复方案
你犯了两个核心错误:
混淆了
authenticating参数的用途
AES-GCM的authenticating参数是用来传入**附加认证数据(AAD)**的,不是提前指定tag。tag是加密过程中CryptoKit自动生成的完整性校验值,不能手动传入固定值。你把外部tag当作AAD传入加密方法,导致生成的sealedBox自带的tag和你手动传入的完全不匹配,解密时触发认证失败。解密时错误复用外部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
相关产品推荐
相关产品推荐

