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
相关产品推荐
相关产品推荐

