0

I'm writing a client-side code in JS , which should be compatible with a third-party c# service which I cannot modify at all.

The service supplies a base64 unicode encoded string (a JSON), for the sake of the (simplified) example this is the c# code:

var myString = Encoding.Unicode.GetBytes("{\"counter\":0}");
var encodedString = Convert.ToBase64String(myString);

which results in : "ewAiAGMAbwB1AG4AdABlAHIAIgA6ADAAfQA="

now I need to update this encoded JSON at the client-side, and return a valid base64 string which will be decoded by this C# code:

byte[] bytes = Convert.FromBase64String(encodedString);
string decodedString = Encoding.Unicode.GetString(bytes);

All the solutions/examples I've found online resulted in the decoding functions to output a invalid result. for example, when using the basic window.btoa("{\"counter\":1}") resulted in eyJjb3VudGVyIjoxfQ== which in turn , when decoding on the c# app, will be cause a format exception at best, or result in total gibberish.

any Ideas?

  • 2
    When using `window.btoa` it uses UTF8 encoding, when you try to decode it as a Unicode string, it will ofcourse end up as gibberish. The C# conversion code is fun, but if you don't supply it Unicode input, it will ofcourse crash when doing `Encoding.Unicode.GetString`. What happens when you change `Unicode` to `UTF8` in the C# code? – Maximilian Gerhardt May 23 '16 at 22:12
  • For clarity, Unicode under Windows (and C#) means UTF16-LE. You'll have to figure out how to get a string encoded as UTF-16 in javascript. – roeland May 24 '16 at 02:08
  • Well if I change it to `Encoding.UTF8` then the decoding works fine. But if I change it to `Encoding.UTF16` it crashed. The problem is I have no control over the C# code, I need to figure it out on the JS side – – Michael Merjanov May 24 '16 at 06:34

3 Answers3

2

As @roeland mentioned the "trick" is to encode the string to UTF16-LE first, and then encode the UTF16-LE string to base64.

The way I've solved it is by first converting the string to a Byte Array using:

function str2ByteArr(str) {
    var bytes = [];

    for (var i = 0; i < str.length; ++i) {
        bytes.push(str.charCodeAt(i));
        bytes.push(0);
    }
    return bytes;
}

the result for "{\"counter\":0}" will be :

[123, 0, 34, 0, 99, 0, 111, 0, 117, 0, 110, 0, 116, 0, 101, 0, 114, 0, 34, 0, 58, 0, 48, 0, 125, 0]

and then converting the array to base64:

function arrayBufferToBase64(buffer) {
    var binary = '';
    var bytes = new Uint8Array(buffer);
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
}

and to sum it all up :

var byteArr = str2ByteArr(str);
return arrayBufferToBase64(byteArr);

the result is :

"ewAiAGMAbwB1AG4AdABlAHIAIgA6ADAAfQA="

another way I've found to convert string to byte Arrays for 'utf-8','utf-16be' and 'utf-16le' encoding is by using Text.Encoder where you can choose the encoding explicitly like so:

var textEncoder = new TextEncoder('utf-16le')
textEncoder.encode("{\"counter\":0}")

but this is not supported by explorer.

0

The accepted answer assumes that the high byte is always 0. This is not always the case and in fact encoding 私はGoogle翻訳を使用しました does not work. The trick is to use the technique found here: How to convert a String to Bytearray when converting the string to a UTF16 Little Endian array:

bytes.push(charCode & 0xFF);  // low byte
bytes.push((charCode & 0xFF00) >>> 8); // high byte

Full solution:

function base64Encode(str)
{
    var bytes = [];
    for (var i = 0; i < str.length; ++i)
    {
        var charCode = str.charCodeAt(i);
        bytes.push(charCode & 0xFF);
        bytes.push((charCode & 0xFF00) >>> 8);
    }

    var len = bytes.length;
    var buffer = "";
    for (var i = 0; i < len; i++)
    {
        buffer += String.fromCharCode(bytes[i]);
    }

    return window.btoa(buffer);
}
Matt R
  • 106
  • 1
  • 2
-1

Below is the Java Script Code for decode :

var Base64 = { _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode: function (e) { var t = ""; var n, r, i, s, o, u, a; var f = 0; e = Base64._utf8_encode(e); while (f < e.length) { n = e.charCodeAt(f++); r = e.charCodeAt(f++); i = e.charCodeAt(f++); s = n >> 2; o = (n & 3) << 4 | r >> 4; u = (r & 15) << 2 | i >> 6; a = i & 63; if (isNaN(r)) { u = a = 64 } else if (isNaN(i)) { a = 64 } t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a) } return t }, decode: function (e) { var t = ""; var n, r, i; var s, o, u, a; var f = 0; e = e.replace(/++[++^A-Za-z0-9+/=]/g, ""); while (f < e.length) { s = this._keyStr.indexOf(e.charAt(f++)); o = this._keyStr.indexOf(e.charAt(f++)); u = this._keyStr.indexOf(e.charAt(f++)); a = this._keyStr.indexOf(e.charAt(f++)); n = s << 2 | o >> 4; r = (o & 15) << 4 | u >> 2; i = (u & 3) << 6 | a; t = t + String.fromCharCode(n); if (u != 64) { t = t + String.fromCharCode(r) } if (a != 64) { t = t + String.fromCharCode(i) } } t = Base64._utf8_decode(t); return t }, _utf8_encode: function (e) { e = e.replace(/\r\n/g, "n"); var t = ""; for (var n = 0; n < e.length; n++) { var r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r) } else if (r > 127 && r < 2048) { t += String.fromCharCode(r >> 6 | 192); t += String.fromCharCode(r & 63 | 128) } else { t += String.fromCharCode(r >> 12 | 224); t += String.fromCharCode(r >> 6 & 63 | 128); t += String.fromCharCode(r & 63 | 128) } } return t }, _utf8_decode: function (e) { var t = ""; var n = 0; var r = c1 = c2 = 0; while (n < e.length) { r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r); n++ } else if (r > 191 && r < 224) { c2 = e.charCodeAt(n + 1); t += String.fromCharCode((r & 31) << 6 | c2 & 63); n += 2 } else { c2 = e.charCodeAt(n + 1); c3 = e.charCodeAt(n + 2); t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63); n += 3 } } return t } }


var id = 7;

var encodeString = Base64.encode(id.toString());

console.log(encodeString);

This encodeString pass to Your controller.

http://localhost:60362/ProjectManagement/Project/ProjectSettings/MTA=

C# code :

 public static string base64Encode(string sData)
        {
            try
            {
                byte[] encData_byte = new byte[sData.Length];

                encData_byte = System.Text.Encoding.UTF8.GetBytes(sData);

                string encodedData = Convert.ToBase64String(encData_byte);

                return encodedData;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Method To Decrypt Password
        /// </summary>
        /// <param name="sData"></param>
        /// <returns></returns>
        public static string base64Decode(string sData)
        {
            try
            {
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();

                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                byte[] todecode_byte = Convert.FromBase64String(sData);

                int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);

                char[] decoded_char = new char[charCount];

                utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);

                string result = new String(decoded_char);

                return result;

            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

Final code:

Decryption the value :

int id = (int)Convert.ToInt64(Cryptography.base64Decode(orders));
Dusan Radovanovic
  • 1,599
  • 12
  • 21