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

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;
}

问题原因及解决方案

核心问题

  1. 二进制数据传输损坏:RC4加密结果是包含不可打印控制字符(如\u001f)的二进制字符串,直接用JSON.stringify处理会对这些字符转义,导致服务器接收的字符串长度、字节内容与原始加密数据不一致。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:53:15