RC4 Algorithm: Unable to Encrypt / Decrypt data wh

2019-08-29 13:40发布

问题:

I need to pass encrypted (and base64 encoded) string data between a .NET 4.0 WCF application hosted in IIS (basicHttpBinding) and an internal client application system that uses a JavaScript RC4 algorithm implementation to encrypt / decrypt data.

So far I have not succeeded in sending data encrypted by the client to the server and then decrypting it on the server (or the reverse - where the client decrypts data received from a server response). We have tried a few variations of RC4 algorithms found in JavaScript files on the internet (and also AES).

I have converted the version of the RC4 algorithm the client is using to C# (JavaScript file located at: https://gist.github.com/2185197). I created an html page to test the JavaScript encrypt / decrypt functionality in a purely client-side manner. This works. Similarly, through unit testing, I have established that the c# Encrypt / Decrypt also works within the .NET WCF service. With these tests, there was no base64 Encoding / Decoding.

Using the c# RC4 algorithm, I can successful handle encryption / decryption of data sent over the wire where the encrypted data is base64 encoded AND where the client is a .NET application (using same C# algorithm implementation class as the server)

I have stepped through the client-side JavaScript (Firebug) and C# (Visual Studio) algorithm line by line, verifying if variable values match. Everything matches except for where code converts an integer value to String (via Char). The result here is inconsistent. Below is the problem line.

Below is the line of code for each implementation.

C#: var charX = Convert.ToChar(26).ToString();

JavaScript:

Var charX = String.fromCharCode(26);

Some visual differences might simply be due to the rendering abilities of Firebug and Visual Studio. [My understanding is that they both should be rendering UTF8 Encoded strings]. I have read that the JavaScript Engine and JavaScript Language differ in their encoding. As such Microsoft has implemented a fix [https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode] that I tried to implement without success. There might be some encoding that I need to implement in the C# code; but as yet have not yet identified

As per the above example for integer value 26, C# code shows a left arrow when the variable value is inspected. The JavaScript shows a white space. For the JavaScript, this is consistent with the tests done via the W3Shools (http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_fromcharcode)

Do I need to perform some sort of Encoding in the .NET application to ensure that the character handling is consistent in JavaScript v C#?

Thanks in advance.

Below is the JavaScript code:

 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]);

            var sx = s[i] + s[j];
            var ssx =  s[sx % 256];
            var fromChar1 =str.charCodeAt(y);
            var fromChar2 = (fromChar1 ^ ssx);
            var fromChar3 = String.fromCharCode(fromChar2);  //******  PROBLEM LINE *******
            //var fromChar3 = fixedFromCharCode(fromChar2);                
            res += fromChar3;
        }
        return res;
    }

    //Fix as per Microsoft
    //https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode
     function fixedFromCharCode(codePt) {
        if (codePt > 0xFFFF) {
            codePt -= 0x10000;
            return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF));
        }
        else {
            return String.fromCharCode(codePt);
        }
    }

Below is the c# code:

public class RC4
{
    public static string Encrypt(string key, string data)
    {
        var s = new List<int>();
        var j = 0;

        var x = 0;
        var res = string.Empty;

        for (var i = 0; i < 256; i++)
        {
            s.Add(i);
        }

        for (var i = 0; i < 256; i++)
        {
            var unicodeInt01 = ConvertedCharacterToItsUnicodeNumberic(key, i);

            j = (j + s[i] + unicodeInt01) % 256;

            x = s[i];
            s[i] = s[j];
            s[j] = x;
        }

        var f = 0;
        j = 0;
        for (var y = 0; y < data.Length; y++)
        {
            f = (f + 1) % 256;
            j = (j + s[f]) % 256;
            x = s[f];
            s[f] = s[j];
            s[j] = x;

            var unicodInt02 = ConvertedCharacterToItsUnicodeNumberic(data, y);
            var convStringOperationApplied = unicodInt02 ^ s[(s[f] + s[j]) % 256];

            var charX = Convert.ToChar(convStringOperationApplied);   //******  PROBLEM LINE *******
            var val = new string(charX, 1);
            res += val;

        }

        return res;
    }

    private static int ConvertedCharacterToItsUnicodeNumberic(string key, int i)
    {
        return key.ElementAt(i % key.Length);
    }

    public static string Decrypt(string key, string data)
    {
        return Encrypt(key, data);
    }
}

回答1:

I think you just need to do URL encoding on the value that you're sending over to your code. I ran it without any changes and noticed that FF and IE were sending two different values. so I used encodeURICompontent before sending the value along and it worked perfectly. No changes to your RC4 algorithms

<script>
    $(function () {
        var value = encodeURIComponent(rc4('9F32B12B2D34FD5FB6B9F372DE67D5C38FC8BF862DB3486C52E5211589B50AB0', 'Welcome to ASP.NET MVC!'));
        $('#encrypted').val(value);
        $.get('/Home/Test?value=' + value, function (d) {
            $('#decrypted').val(d);
        });
    });
</script>
<textarea id="encrypted">
</textarea>

<textarea id="decrypted">
</textarea>

This is what I have on the server side (ASP.NET MVC):

public string Test(string value)
{
    string test = RC4.Decrypt("9F32B12B2D34FD5FB6B9F372DE67D5C38FC8BF862DB3486C52E5211589B50AB0", value);

    return test;
}