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

PHP与Go语言RC4-MD5解密输出不一致问题求助

解决PHP与Go语言RC4-MD5解密结果不一致的问题

问题的核心在于RC4算法的状态维护差异:Go语言中创建的RC4 cipher实例会持续维护内部流状态,两次调用XORKeyStream是连续使用密钥流;而PHP的openssl_decrypt每次调用rc4模式时,都会重新初始化RC4上下文,从头生成密钥流,导致两次解密结果相同。

解决方案步骤

1. 补全PHP的evpBytesToKey函数

你的当前函数仅处理了keyLen=16的场景,补全后和Go语言实现完全一致:

function evpBytesToKey(string $password, int $keyLen): string {
    $md5Len = 16;
    $cnt = (int)(($keyLen - 1) / $md5Len + 1);
    $m = md5($password, true);
    $start = $md5Len;
    for ($i = 1; $i < $cnt; $i++) {
        $d = substr($m, $start - $md5Len, $md5Len) . $password;
        $m .= md5($d, true);
        $start += $md5Len;
    }
    return substr($m, 0, $keyLen);
}

2. 实现可维护状态的RC4类

自定义RC4类来模拟Go语言中rc4.Cipher的状态持续性:

class RC4Cipher {
    private $s = [];
    private $i = 0;
    private $j = 0;

    public function __construct(string $key) {
        $keyLen = strlen($key);
        // 初始化S盒
        for ($this->i = 0; $this->i < 256; $this->i++) {
            $this->s[$this->i] = $this->i;
        }
        $j = 0;
        // 密钥调度算法(KSA)
        for ($this->i = 0; $this->i < 256; $this->i++) {
            $j = ($j + $this->s[$this->i] + ord($key[$this->i % $keyLen])) % 256;
            // 交换S[i]和S[j]
            $temp = $this->s[$this->i];
            $this->s[$this->i] = $this->s[$j];
            $this->s[$j] = $temp;
        }
        $this->i = $this->j = 0;
    }

    public function xorKeyStream(string $data): string {
        $output = '';
        $len = strlen($data);
        // 伪随机生成算法(PRGA)
        for ($k = 0; $k < $len; $k++) {
            $this->i = ($this->i + 1) % 256;
            $this->j = ($this->j + $this->s[$this->i]) % 256;
            // 交换S[i]和S[j]
            $temp = $this->s[$this->i];
            $this->s[$this->i] = $this->s[$j];
            $this->s[$j] = $temp;
            $t = ($this->s[$this->i] + $this->s[$this->j]) % 256;
            $output .= chr(ord($data[$k]) ^ $this->s[$t]);
        }
        return $output;
    }
}

3. 修改主代码使用自定义RC4类

替换openssl_decrypt为自定义类的方法,确保状态持续:

function binToHex(string $src): string {
    $hextable = '0123456789abcdef';
    $dst = '';
    $len = strlen($src);
    for ($i = 0; $i < $len; $i++) {
        $v = ord($src[$i]);
        $dst .= $hextable[$v >> 4] . $hextable[$v & 0x0f];
    }
    return $dst;
}

// 初始化参数
$iv = base64_decode('jsB78D2MXq83YZqqwcswXA==');
$key = evpBytesToKey("123456", 16);
var_dump(binToHex($key)); // 输出:e10adc3949ba59abbe56e057f20f883e

$rc4key = md5($key . $iv, true);
var_dump(binToHex($rc4key)); // 输出:fd0eb4cfd6b0174968d9979781d95670

// 创建RC4实例(维护状态)
$rc4 = new RC4Cipher($rc4key);

// 第一次解密
$text1 = base64_decode('8Q==');
$result1 = $rc4->xorKeyStream($text1);
var_dump(binToHex($result1)); // 输出:03

// 第二次解密(使用更新后的状态)
$text2 = base64_decode('8Q==');
$result2 = $rc4->xorKeyStream($text2);
var_dump(binToHex($result2)); // 输出:0e

为什么这样能解决问题

  • PHP的openssl_decrypt('rc4')每次调用都会重新初始化RC4上下文,无法复用密钥流状态;
  • 自定义的RC4Cipher类会持续维护RC4算法的内部S盒和指针状态,和Go语言中rc4.NewCipher的行为完全一致,两次解密会连续使用密钥流,从而得到和Go相同的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:55:45