JavaScript与PHP间RC4加解密字符不一致问题求助
RC4加密后JS客户端与PHP服务器字符不一致问题解决
问题概述
在Chrome扩展的JavaScript客户端使用RC4算法加密数据(密钥为test),加密结果包含特殊字符,通过WebSocket发送到PHP服务器后,解密时出现字符数量不符的情况,导致无法正确解密。
两端控制台输出
JavaScript客户端控制台
text before encrypting: {"data":"yo, server"} background.js:958 encryptedText: ÕCpK_ô72O1×½ËûÂ$ó background.js:907 sent data: "ÕCpK_\u001fô72O1×½ËûÂ$ó"
PHP服务器控制台
"ÕCpK_\u001fô72O1×½ËûÂ$ó""\u00d5\u00adCp\u0094K_\u001f\u00f472O1\u00d7\u0082\u00bd\u00cb\u00fb\u00c2$\u00f3"
原始代码
JavaScript客户端代码
// Send data function sendWebSocketData(data) { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify(data)) console.log('sent data: ' + JSON.stringify(data)) } else { console.log('DEBUG: WebSocket connection is not open.') } } function rc4(key, str) { var s = [], j = 0, x, res = ''; for (var i = 0; i < 256; i++) { s[i] = i; } for (i = 0; i < 256; i++) { j = (j + s[i] + key.charCodeAt(i % key.length)) % 256; x = s[i]; s[i] = s[j]; s[j] = x; } i = 0; j = 0; for (var y = 0; y < str.length; y++) { i = (i + 1) % 256; j = (j + s[i]) % 256; x = s[i]; s[i] = s[j]; s[j] = x; res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]); } return res; } let text = { data: 'yo, server' } console.log('text before encrypting: ' + JSON.stringify(text)) encryptedText = rc4('test', JSON.stringify(text)) console.log('encryptedText: ' + encryptedText) sendWebSocketData(encryptedText)
PHP服务器代码
public function onMessage(ConnectionInterface $from, $msg) { // Process the received message (handle JSON data) rc4('test', $msg); echo $msg; $decoded = json_decode($msg, true); echo json_encode($decoded); } function rc4($key, $str) { $s = array(); for ($i = 0; $i < 256; $i++) { $s[$i] = $i; } $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $s[$i] + ord($key[$i % strlen($key)])) % 256; $x = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $x; } $i = 0; $j = 0; $res = ''; for ($y = 0; $y < strlen($str); $y++) { $i = ($i + 1) % 256; $j = ($j + $s[$i]) % 256; $x = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $x; $res .= $str[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]); } return $res; }
问题原因及解决方案
核心问题
- 二进制数据传输损坏:RC4加密结果是包含不可打印控制字符(如
\u001f)的二进制字符串,直接用JSON.stringify处理会对这些字符转义,导致服务器接收的字符串长度、字节内容与原始加密数据不一致。 - PHP RC4函数逻辑错误:异或操作时直接使用字符串字符
$str[$y],未转换为ASCII值,PHP中字符串字符的异或会受编码影响,导致计算错误。
修复步骤
1. 修改JavaScript客户端:用Base64编码传输加密数据
将加密后的二进制字符串转为Base64,避免传输过程中字符被转义或损坏:
function sendWebSocketData(data) { if (ws && ws.readyState === WebSocket.OPEN) { // 直接发送Base64编码后的字符串,无需JSON.stringify ws.send(data) console.log('sent data: ' + data) } else { console.log('DEBUG: WebSocket connection is not open.') } } // 加密后转Base64 encryptedText = rc4('test', JSON.stringify(text)) // 将二进制字符串转为Base64 const base64Encrypted = btoa(unescape(encodeURIComponent(encryptedText))) console.log('base64 encrypted data: ' + base64Encrypted) sendWebSocketData(base64Encrypted)
2. 修改PHP服务器:解码Base64并修正RC4函数
接收Base64数据后先解码,同时修正RC4函数的异或逻辑:
public function onMessage(ConnectionInterface $from, $msg) { // 解码Base64数据 $encryptedData = base64_decode($msg); // 解密数据 $decrypted = rc4('test', $encryptedData); // 解析原始JSON $decoded = json_decode($decrypted, true); echo 'Decrypted data: ' . $decrypted . PHP_EOL; echo 'Parsed JSON: ' . json_encode($decoded) . PHP_EOL; } function rc4($key, $str) { $s = array(); for ($i = 0; $i < 256; $i++) { $s[$i] = $i; } $j = 0; $keyLength = strlen($key); for ($i = 0; $i < 256; $i++) { $j = ($j + $s[$i] + ord($key[$i % $keyLength])) % 256; $x = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $x; } $i = 0; $j = 0; $res = ''; $strLength = strlen($str); for ($y = 0; $y < $strLength; $y++) { $i = ($i + 1) % 256; $j = ($j + $s[$i]) % 256; $x = $s[$i]; $s[$i] = $s[$j]; $s[$j] = $x; // 先获取字符的ASCII值再异或,结果转成字符 $res .= chr(ord($str[$y]) ^ $s[($s[$i] + $s[$j]) % 256]); } return $res; }
验证说明
修改后,JS端将加密数据转为Base64发送,PHP端解码后得到原始加密二进制数据,再通过修正后的RC4函数解密,即可得到正确的原始JSON字符串,解决字符数量不符和解密失败的问题。
内容的提问来源于stack exchange,提问作者kyle walker
相关产品推荐
相关产品推荐

