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

如何解决Dart AES.GCM加密字符串在Swift解密时的认证失败问题?

AES-GCM跨端解密认证失败问题解决

问题根源

Dart加密逻辑和Swift解密逻辑的数据结构不匹配,且关联数据(Associated Data)的处理不一致:

  1. Dart代码在加密后额外拼接了空的macValue(关联数据)到输出末尾,导致Swift解析SealedBox时结构错误
  2. Swift解密时未传入加密时使用的关联数据(即使是空字符串,GCM也会校验该部分)
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:13:11