AES-GCM加密登录遇无效凭证:Dart与C#加密逻辑匹配问题
问题分析:Dart与C# AES-GCM加密逻辑不等价
两者核心差异集中在nonce长度和输出结构上,直接导致后端无法正确解密验证:
1. Nonce长度不匹配
- C#代码生成的是
AesGcm.NonceByteSizes.MaxSize(16字节)的nonce; - Dart代码固定生成12字节的nonce,长度不一致会直接导致解密失败。
2. 输出数据结构完全不同
- C#将nonce + 密文 + tag三个关键部分组合后序列化返回;
- Dart仅返回了密文+tag的Base64编码,缺少解密必需的nonce,且未按后端期望的格式组合数据。
修复后的Dart代码
调整nonce长度并对齐输出结构,假设C#的AesGcmCiphertext.ToString()是将nonce、密文、tag按顺序拼接后做Base64编码(这是最常见的实现方式):
static String encryptAESGCM(String plaintext, String key) { try { final keyBytes = Uint8List.fromList(utf8.encode(key)); // 对齐C#的nonce长度:16字节 final nonce = generateSecureRandomData(16); final cipher = GCMBlockCipher(AESEngine()); // tag长度保持128位(16字节),与C#的AesGcm.TagByteSizes.MaxSize一致 final params = AEADParameters(KeyParameter(keyBytes), 128, nonce, Uint8List(0)); cipher.init(true, params); final plaintextBytes = utf8.encode(plaintext); final ciphertextWithTag = cipher.process(Uint8List.fromList(plaintextBytes)); // 拆分密文和tag(tag占最后16字节) final ciphertextBytes = ciphertextWithTag.sublist(0, ciphertextWithTag.length - 16); final tagBytes = ciphertextWithTag.sublist(ciphertextWithTag.length - 16); // 按C#格式组合:nonce + 密文 + tag final combinedBytes = Uint8List.fromList([...nonce, ...ciphertextBytes, ...tagBytes]); return base64.encode(combinedBytes); } catch (error) { print("Encryption error: $error"); return ""; } } static Uint8List generateSecureRandomData(int size) { return Uint8List.fromList(List<int>.generate(size, (_) => Random.secure().nextInt(256))); }
额外验证点
- Key长度校验:确保key的UTF8编码后长度是16/24/32字节(对应AES-128/192/256),两端必须一致;
- AesCcm兼容性:C#使用
AesCcm,Dart使用GCMBlockCipher,两者都是标准GCM实现,参数对齐后可兼容; - 序列化格式确认:如果C#的
AesGcmCiphertext.ToString()是其他格式(如各部分单独Base64后用分隔符连接),需对应调整Dart的组合逻辑。比如若格式为nonceBase64:tagBase64:ciphertextBase64,则Dart需要分别对三个部分做Base64再拼接。
内容的提问来源于stack exchange,提问作者Ninty FiVE
相关产品推荐
相关产品推荐

