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

PHP与Flutter(Dart) AES-128加密不匹配问题排查及疑问

问题分析与解决方案

一、PHP端加密结果过短的原因

AES-128-CTR属于流加密模式,它的输出密文长度和明文长度完全一致。你的明文是"1234"(4字节),加密后密文也是4字节,经过Base64编码后自然就是短字符串xpL7dg==(4字节的Base64编码正好是8个字符,末尾的==是标准填充)。

你误以为会得到16位字符串,是混淆了CBC、ECB这类块加密模式的特性——块模式下明文长度不是块大小(16字节)的整数倍时会自动填充,密文长度会是块大小的整数倍;但CTR模式不需要填充,密文长度和明文严格对应。

二、两端加密结果不匹配的核心问题

你两端的加密核心参数完全不统一,具体差异如下:

  1. 加密模式不一致:PHP用的是AES-128-CTR,Flutter用的是AESMode.cbc,两种模式的加密逻辑完全不同,结果必然无法互通。
  2. 密钥处理逻辑不同:
    • PHP直接使用原始字符串作为AES密钥(注意AES-128要求密钥必须是16字节,若你的原始密钥长度不符,OpenSSL会自动补0/截断,这是潜在问题)。
    • Flutter对原始密钥做SHA256哈希后取前16字节作为密钥,和PHP的密钥完全不是同一个值。
  3. IV处理逻辑不同:
    • PHP直接使用原始字符串作为IV(CTR模式下IV长度要求等于块大小16字节,你的IV正好符合要求)。
    • Flutter对原始IV做SHA256哈希后取前16字节作为IV,和PHP的IV完全不同。
  4. 隐藏错误:Flutter代码中把SHA256哈希的字符串转成UTF8作为密钥/IV,这会改变哈希的原始字节值,属于错误处理方式。

三、修正方案(统一参数实现互通)

要实现两端互通,必须让**加密模式、密钥、IV、填充方式(块模式下)**完全一致。以下提供两种可选方案:

方案1:统一使用AES-128-CTR模式

PHP端修正代码

$ciphering = "AES-128-CTR";
$options = 0;
// 对齐Flutter的密钥处理逻辑:SHA256哈希后取前16字节原始值
$original_key = "XXXXXXXXXXXX";
$encryption_key = substr(hash('sha256', $original_key, true), 0, 16);
// IV同样对齐Flutter的处理逻辑
$original_iv = '1234567891011121';
$decryption_iv = substr(hash('sha256', $original_iv, true), 0, 16);
$encryption = openssl_encrypt("1234", $ciphering, $encryption_key, $options, $decryption_iv);
echo $encryption;

Flutter端修正代码

import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';

void main() {
  final mykey = "XXXXXXXXXXXX";
  final myiv = "1234567891011121";
  // 密钥处理:取SHA256哈希后的原始字节前16位
  final keyUtf8 = utf8.encode(mykey);
  final keyBytes = sha256.convert(keyUtf8).bytes.sublist(0, 16);
  // IV处理:取SHA256哈希后的原始字节前16位
  final ivUtf8 = utf8.encode(myiv);
  final ivBytes = sha256.convert(ivUtf8).bytes.sublist(0, 16);
  final _iv = IV(ivBytes);
  // 改用CTR模式
  final encrypter = Encrypter(AES(Key(keyBytes), mode: AESMode.ctr));
  final encryptedPass = encrypter.encrypt("1234", iv: _iv);
  print(encryptedPass.base64);
}

方案2:统一使用AES-128-CBC模式

PHP端修正代码

$ciphering = "AES-128-CBC";
$options = OPENSSL_RAW_DATA; // 输出原始密文后自行Base64编码,对齐Flutter逻辑
$original_key = "XXXXXXXXXXXX";
$encryption_key = substr(hash('sha256', $original_key, true), 0, 16);
$original_iv = '1234567891011121';
$decryption_iv = substr(hash('sha256', $original_iv, true), 0, 16);
$encrypted_raw = openssl_encrypt("1234", $ciphering, $encryption_key, $options, $decryption_iv);
$encryption = base64_encode($encrypted_raw);
echo $encryption;

Flutter端修正代码

import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';

void main() {
  final mykey = "XXXXXXXXXXXX";
  final myiv = "1234567891011121";
  // 修正密钥和IV的处理:取哈希后的原始字节,而非字符串转UTF8
  final keyUtf8 = utf8.encode(mykey);
  final keyBytes = sha256.convert(keyUtf8).bytes.sublist(0, 16);
  final ivUtf8 = utf8.encode(myiv);
  final ivBytes = sha256.convert(ivUtf8).bytes.sublist(0, 16);
  final _iv = IV(ivBytes);
  final encrypter = Encrypter(AES(Key(keyBytes), mode: AESMode.cbc));
  final encryptedPass = encrypter.encrypt("1234", iv: _iv);
  print(encryptedPass.base64);
}

关键注意事项

  • AES-128的密钥必须是16字节,AES-256是32字节,一定要确保密钥长度符合要求,否则两端加密库的补全/截断逻辑会不一致。
  • CTR模式不需要填充,CBC模式默认使用PKCS7填充,两端要保持填充方式统一。
  • 处理哈希后的密钥/IV时,必须使用原始字节数组,不要把哈希字符串转成UTF8(哈希字符串是十六进制字符,转UTF8会改变原始字节值)。

内容的提问来源于stack exchange,提问作者chris herd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 07:40:26