如何解决Dart AES.GCM加密字符串在Swift解密时的认证失败问题?
AES-GCM跨端解密认证失败问题解决
问题根源
Dart加密逻辑和Swift解密逻辑的数据结构不匹配,且关联数据(Associated Data)的处理不一致:
- Dart代码在加密后额外拼接了空的
macValue(关联数据)到输出末尾,导致Swift解析SealedBox时结构错误 - Swift解密时未传入加密时使用的关联数据(即使是空字符串,GCM也会校验该部分)
- Dart的
encrypted.bytes本身已经包含密文+16字节认证标签,无需额外处理
修正后的Dart加密代码
移除多余的空关联数据拼接,保持输出为IV + 密文+标签的结构:
String encryptString(Uint8List key) { try { final encryptionKey = Key(key); final iv = IV.fromLength(16); // 保持16字节IV,和Swift端一致 final associatedData = utf8.encode(""); // 空关联数据 final encrypter = Encrypter(AES(encryptionKey, mode: AESMode.gcm)); final encrypted = encrypter.encrypt( this, iv: iv, associatedData: associatedData, ); // 仅拼接IV和加密后的字节(密文+标签) final concatenatedBytes = Uint8List.fromList([...iv.bytes, ...encrypted.bytes]); final base64Encoded = base64.encode(concatenatedBytes); return base64Encoded; } catch (e) { print('Error while encrypting: $e'); return ''; } }
修正后的Swift解密代码
明确拆分IV、密文、标签,并传入对应的空关联数据:
func decrypt(withKey key: SymmetricKey) throws -> String { guard let encryptedData = Data(base64Encoded: self) else { throw EncryptionError.invalidData } // 拆分IV(前16字节)、剩余数据(密文+标签) let ivLength = 16 guard encryptedData.count >= ivLength + 16 else { // 标签至少16字节 throw EncryptionError.invalidData } let ivData = encryptedData.prefix(ivLength) let ciphertextAndTag = encryptedData.suffix(from: ivLength) do { let sealedBox = try AES.GCM.SealedBox( nonce: AES.GCM.Nonce(data: ivData), ciphertext: ciphertextAndTag.dropLast(16), tag: ciphertextAndTag.suffix(16) ) // 传入加密时使用的空关联数据 let decryptedData = try AES.GCM.open(sealedBox, using: key, associatedData: Data()) guard let decryptedString = String(data: decryptedData, encoding: .utf8) else { throw EncryptionError.decryptionFailed } return decryptedString } catch { print(error.localizedDescription) throw EncryptionError.authenticationFailed // 更精准的错误定义 } }
简化版Swift代码(若Dart输出格式严格)
如果确认Dart输出是IV(16字节) + 密文 + 标签(16字节),也可以直接使用SealedBox(combined:),但必须传入关联数据:
func decrypt(withKey key: SymmetricKey) throws -> String { guard let encryptedData = Data(base64Encoded: self) else { throw EncryptionError.invalidData } do { let sealedBox = try AES.GCM.SealedBox(combined: encryptedData) // 必须传入空的关联数据,和加密时一致 let decryptedData = try AES.GCM.open(sealedBox, using: key, associatedData: Data()) guard let decryptedString = String(data: decryptedData, encoding: .utf8) else { throw EncryptionError.decryptionFailed } return decryptedString } catch { print(error.localizedDescription) throw EncryptionError.authenticationFailed } }
内容的提问来源于stack exchange,提问作者Pratheesh Bennet
相关产品推荐
相关产品推荐

