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

PHP中特定长度输入的AES-192加解密失败问题求助

PHP自定义AES-192加解密异常问题排查与解决

我在PHP中使用自定义AES实现:

$Cipher = new AESCipher(AES::AES192);

问题

两个相似输入表现不一致:

  • CvwCvw-2-2@{}#%~[]=,-_!(23字节):加解密完全正常
  • CTPView-2-2@{}#%~[]=,-_!(24字节):加密成功,但解密失败

现象观察

AES采用16字节块加密,剩余字节会填充补全为完整块:

  • 23字节输入:填充9字节以凑齐32字节(2个16字节块)
  • 24字节输入:填充8字节以凑齐32字节
    仅填充量不同,但结果却出现明显差异

已尝试的无效操作

  • 切换加密算法为AES-256
  • 使用base64_encode/decode处理密文
  • 使用rawurlencode/rawurldecode处理密文
  • 修剪输入内容、移除隐藏字符
  • 验证加密密码在INI文件中的存储与读取逻辑

相关代码

加密代码

<?php
   include_once('/var/www/.scripts/AES.class.php');
   include_once('/var/www/.scripts/AESCipher.class.php');

   $password = "___getting from user____";
   $Cipher = new AESCipher(AES::AES192);
   $cryptext = @$Cipher->encrypt($password, $username);
   $cryptext = rawurlencode($cryptext);
   print $cryptext;

解密代码

<?php
    include_once('/var/www/.scripts/AES.class.php');
    include_once('/var/www/.scripts/AESCipher.class.php');

    $Cipher = new AESCipher(AES::AES192);
    $ini_array = parse_ini_file("example.ini");
    $password = $ini_array['password'];
    $username = $ini_array['username'];

    $cryptext = rawurldecode($password);
    $decrypt_pass = @$Cipher->decrypt($cryptext, "db_name");
    
    print $decrypt_pass;
    $this->connection = pg_connect("user=$username password=$decrypt_pass dbname=db_name") or die("Could not connect to postgresql");   

问题根源与解决建议

1. 修复填充逻辑(核心问题)

问题大概率出在自定义AES实现的填充/去填充逻辑上。标准PKCS#7填充规则明确:如果输入长度刚好是块大小的整数倍,需要额外填充一个完整块的填充字节(而非与剩余长度相等的字节数)。

比如24字节输入(刚好是1.5个16字节块),按标准应该填充16字节的0x10,而非8字节的0x08。如果你的实现未遵循此规则,解密时会错误地截断原始输入的最后8字节,导致解密失败。

替换成标准PKCS#7填充/去填充实现:

// 填充函数
function pkcs7_pad($data, $block_size) {
    $pad_length = $block_size - (strlen($data) % $block_size);
    // 若输入已是块大小整数倍,填充一个完整块
    if ($pad_length == 0) {
        $pad_length = $block_size;
    }
    return $data . str_repeat(chr($pad_length), $pad_length);
}

// 去填充函数
function pkcs7_unpad($data) {
    $pad_length = ord(substr($data, -1));
    // 校验填充长度合法性
    if ($pad_length < 1 || $pad_length > 16) {
        return false;
    }
    return substr($data, 0, -$pad_length);
}

2. 修正加密解密的参数一致性

注意到加密时第二个参数是$username,但解密时硬编码为"db_name":

  • 加密:$Cipher->encrypt($password, $username);
  • 解密:$Cipher->decrypt($cryptext, "db_name");

如果该参数是密钥推导的一部分(盐或密钥生成因子),参数不一致会导致加密解密密钥完全不同,必然解密失败。修改解密代码,使用读取到的$username作为参数:

$decrypt_pass = $Cipher->decrypt($cryptext, $username);

3. 移除错误抑制符@

代码中的@会屏蔽所有错误信息,导致无法定位具体问题。去掉@并开启PHP错误提示,能直接看到填充错误、密钥不匹配等具体异常。

4. 验证密文完整性

为避免密文在存储/读取过程中被篡改,可在加密后生成密文的哈希值,与密文一起存储,解密前先校验哈希:

// 加密时生成哈希
$hash = hash('sha256', $cryptext);
// 将$cryptext和$hash一同写入INI文件

// 解密时校验
$stored_hash = $ini_array['password_hash'];
if (hash('sha256', $cryptext) !== $stored_hash) {
    die("密文已被篡改");
}

内容的提问来源于stack exchange,提问作者Balaji Sekar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 01:20:18