TripleDESCryptoServiceProvider for C# and Cryptojs gives different results - javascript

Why do I get different result in my encryption on TriplesDes using c# and JavaScript cryptojs? Please see my code below.
c#
public static string EncryptTxt()
{
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
using (var tdes = new TripleDESCryptoServiceProvider())
{
var msg = 'jvofs:JCV XXXXX:201911141547:12345678';
var key = 'jjvofs';
var keyOffset = 10;
System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
byte[] Results;
byte[] newKeyx = new byte[24];
byte[] keybyte = sha.ComputeHash(Encoding.UTF8.GetBytes(key));
Array.Copy(keybyte, keyOffset, newKeyx, 0, newKeyx.Length);
TDESAlgorithm.Key = newKeyx;
TDESAlgorithm.Mode = CipherMode.ECB;
TDESAlgorithm.Padding = PaddingMode.PKCS7;
byte[] DataToEncrypt = UTF8.GetBytes(msg);
try
{
ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
}
var a = Convert.ToBase64String(DataToEncrypt);
var b = Convert.ToBase64String(newKeyx);
var c = Convert.ToBase64String(Results);
return Convert.ToBase64String(Results);
}
}
JavaScript using cryptojs
txtEncrypter = () => {
const msg = 'jvofs:JCV XXXXX:201911141547:12345678';
const key = 'jjvofs';
const keyOffset = 10;
const keybyte: any = this.wordArrayToByteArray(crypto.SHA512(key), 100);
// For message
const dataToEncrypt = crypto.enc.Utf8.parse(msg);
const dte = this.wordArrayToByteArray(dataToEncrypt, 100);
const dataToEncryptx = this._arrayBufferToBase64(dte);
const dataToEncryptxx = crypto.enc.Utf8.parse(dataToEncryptx);
// For key
let newKeyx = keybyte.slice(keyOffset, 34);
const newKeyxB4Splice = newKeyx;
const newKeyxB4Splicex = this._arrayBufferToBase64(newKeyx);
newKeyx = crypto.enc.Utf8.parse(newKeyx);
const options = {
mode: crypto.mode.ECB,
padding: crypto.pad.Pkcs7
};
const encrypted = crypto.TripleDES.encrypt(dataToEncrypt, newKeyx, options);
const base64String = encrypted.toString();
console.log(base64String);
}
wordArrayToByteArray(wordArray, length) {
if (wordArray.hasOwnProperty('sigBytes') && wordArray.hasOwnProperty('words')) {
length = wordArray.sigBytes;
wordArray = wordArray.words;
}
const result = [];
let bytes: any;
let i = 0;
while (length > 0) {
bytes = this.wordToByteArray(wordArray[i], Math.min(4, length));
length -= bytes.length;
result.push(bytes);
i++;
}
return [].concat.apply([], result);
}
wordToByteArray(word: any, length: any) {
const ba = [], xFF = 0xFF;
if (length > 0) {
// tslint:disable-next-line:no-bitwise
ba.push(word >>> 24);
}
if (length > 1) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 16) & xFF);
}
if (length > 2) {
// tslint:disable-next-line:no-bitwise
ba.push((word >>> 8) & xFF);
}
if (length > 3) {
// tslint:disable-next-line:no-bitwise
ba.push(word & xFF);
}
return ba;
}
byteArrayToWordArray(ba) {
const wa = [];
let i = 0;
for (i = 0; i < ba.length; i++) {
// tslint:disable-next-line:no-bitwise
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i);
}
return crypto.lib.WordArray.create(wa);
}
toUTF8Array(str) {
const utf8 = [];
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode < 0x80) { utf8.push(charcode); } else if (charcode < 0x800) {
// tslint:disable-next-line:no-bitwise
utf8.push(0xc0 | (charcode >> 6),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else if (charcode < 0xd800 || charcode >= 0xe000) {
// tslint:disable-next-line: no-bitwise
utf8.push(0xe0 | (charcode >> 12),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
} else {
i++;
// UTF-16 encodes 0x10000-0x10FFFF by
// subtracting 0x10000 and splitting the
// 20 bits of 0x0-0xFFFFF into two halves
// tslint:disable-next-line:no-bitwise
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
// tslint:disable-next-line:no-bitwise
| (str.charCodeAt(i) & 0x3ff));
// tslint:disable-next-line:no-bitwise
utf8.push(0xf0 | (charcode >>18),
// tslint:disable-next-line:no-bitwise
0x80 | ((charcode>>12) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | ((charcode>>6) & 0x3f),
// tslint:disable-next-line: no-bitwise
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
_arrayBufferToBase64( buffer ) {
let binary = '';
const bytes = new Uint8Array( buffer );
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
When parsing the data for message and key, both c# and JavaScript are the same:
Message:
[C#] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
[cryptojs] anZvZnM6SkNWIFhYWFhYOjIwMTkxMTE0MTU0NzoxMjM0NTY3OA==
Key:
[C#] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
[cryptojs] smbkkmDrCBdRev7S4hLaWE16Nvym+9gW
But as soon as the "crypto.TripleDES.encrypt(....)" runs, I get different result for c# and javascript:
[C#] 1pjvBOB81iAOqsskZ+cM080yDU37XBoCwMhbYULwva/Nql5vbEMiPQ==
[cryptojs] ncCcCYNy3jVsB/95SaC2N1rH5Q+hX04WvScMvwmtkPkrnL7Ki1bmPg==

After the key has been determined from the byte-array of the hash as a sub-array (incl. index 10 to excl. index 34), this sub-array must be converted back into a WordArray with equivalent content, i.e. the line:
newKeyx = crypto.enc.Utf8.parse(newKeyx);
must be replaced by:
newKeyx = byteArrayToWordArray(newKeyx);
With this change, the NodeJS-code returns the same result as the C#-code.
The conversion WordArray <-> byte-array (and thus all functions required for these conversions) aren't really necessary, because as an alternative the key can also be derived using only the CryptoJS-encoders:
...
const key = 'jjvofs';
const keyOffset = 10;
const keyLength = 24;
const keyHash = crypto.enc.Hex.stringify(crypto.SHA512(key));
const newKey = crypto.enc.Hex.parse(keyHash.slice(keyOffset * 2, (keyOffset + keyLength) * 2));
...
By the way: The ECB-mode is insecure and instead of TripleDES the more performant AES should be used.

Related

Reverse engineer RSA algorithm used by HUAWEI Router in Java/Kotlin

I'm trying control my huawei router using its web api, but some things are RSA encrypted. I have the public key, but I am struggling to find the algorithm they use to encrypt things.
The code they use is below:
function doRSAEncrypt(encstring) {
if (encstring == '') {
return '';
}
if (typeof (g_moduleswitch.encrypt_enabled) == 'undefined' || g_moduleswitch.encrypt_enabled != 1) {
return encstring;
}
if (g_encPublickey.e == '') {
if (true == g_scarm_login) {
var pubkeyArray = getPubkey();
g_encPublickey.e = pubkeyArray[1];
g_encPublickey.n = pubkeyArray[0];
} else {
getEncpubkey();
}
}
var rsa = new RSAKey();
rsa.setPublic(g_encPublickey.n, g_encPublickey.e);
encstring = base64_encode(encstring);
var num = encstring.length / 245;
var restotal = '';
for (i = 0; i < num; i++) {
var encdata = encstring.substr(i * 245, 245);
var res = rsa.encrypt(encdata);
restotal += res;
}
if (restotal.length % 256 != 0) {
restotal = doRSAEncrypt(encstring);
}
return restotal;
}
function parseBigInt(str, r) {
return new BigInteger(str, r);
}
// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s, n) {
if (n < s.length + 11) { // TODO: fix for utf-8 alert("Message too long for RSA");
return null;
}
var ba = new Array();
var i = s.length - 1;
while (i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if (c < 128) { // encode using utf-8
ba[--n] = c;
} else if ((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
} else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while (n > 2) { // random non-zero pad
x[0] = 0;
while (x[0] == 0)
rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}
// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
// Set the public key fields N and e from hex strings
function RSASetPublic(N, E) {
if (N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N, 16);
this.e = parseInt(E, 16);
} else alert("Invalid RSA public key");
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);
if (m == null)
return null;
var c = this.doPublic(m);
if (c == null)
return null;
var h = c.toString(16);
if ((h.length & 1) == 0)
return h;
else
return "0" + h;
}
// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
function RSAEncryptB64(text) {
var h = this.encrypt(text);
if (h)
return hex2b64(h);
else
return null;
}
// protectedRSAKey.prototype.doPublic = RSADoPublic;
// publicRSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
function base64_encode(input) {
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
}
function _utf8_encode(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
For modulus d5eeead43ba5133e06cce6703b713db54331141d2707b8701a532173904b4e3bfca4bf73cdb7c56a640319299a083c780fa39d0fdc50aca6e0ea5d39c605cf90b88b33ed71126eea437fcd383576b11276df99425807e4c43bde60fcef38a11a6cbfb327377240b42dcf9e3d3abc1f37e62ca7efebfa247e879adeea9a395ed889916e91fd83199539dd9063f6fc306b106245b630f13ffea18eae7a486316b2c27b551214fd202993581276dfc407047f3f2da3e44161590b4cf5e12eab81633396b0eb17e487b3a12dd4a2a87726b487309801e0984ca222706127018e917ddbad3e9d9a7107e3cb54a9dad49ae7ba77252547758c2a3cf8c2c56e17b13591
and exponent 010001 (both in hex) the string test123test yields 59a5a4fe4056612c9892ea2d5108c1e348b2fb70a85a1f0c7b112bfddf058f969e8fc5e797875f63b75eb59160c1df77c7d23dbe481905226d2001f32b4eee59ec795bd113c3606096f8cbdab8ada6d6df00f1621635b7dec60ba1814208a39a3aba2ea527bda2ea522f6b65273525e7fe03478a154904debb11b7523c50432cda45a3638a6b65f768acbcc3ef1ea5c11235e39343042ea570bf220bbad05973e5c6d58af3e64c0ad183b946252c801567fa11029bdc667d2144413a5f943813ef8daf2318079204ec615e68a871e29556cd43495e3ea0a84adbe6b2dbd7e4a8d3be78bdf64a61db1a8c1dd9683499ce9241dbd6ea5bee0325944d01a17d5199 using their encryption.
I tried various RSA modes from Bouncycastle in Java like
RSA/ECB/OAEPWITHMD5ANDMGF1PADDING
RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING
RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING
RSA/ECB/OAEPWITHSHA-384ANDMGF1PADDING
RSA/ECB/OAEPWITHSHA-512ANDMGF1PADDING
RSA/ECB/OAEPWithSHA-1AndMGF1Padding
RSA/ECB/PKCS1Padding
but none of them yielded the same result as the Huawei encrypt function did
import java.io.ByteArrayOutputStream
import java.math.BigInteger
import java.security.SecureRandom
import java.util.*
import kotlin.math.ceil
import kotlin.math.min
class RSAEncrypt(modulo: String, exponent: String) {
private val modulo = BigInteger(modulo, 16)
private val exponent = BigInteger(exponent, 16)
fun doRSAEncrypt(encstring: String): String {
val base64String = Base64.getEncoder().encodeToString(encstring.toByteArray())
val num = base64String.length / 245.0
var restotal = ""
for (i in 0 until ceil(num).toInt()) {
val encryptedData = base64String.substring(i * 245, min(245, base64String.length))
val res = this.encrypt(encryptedData);
restotal += res;
}
if (restotal.length % 256 !== 0) {
restotal = doRSAEncrypt(base64String)
}
return restotal
}
private fun pkcs1pad2(s: String, n: Int): BigInteger? {
if (n < s.length + 11) { // TODO: fix for utf-8
return null
}
var n = n
val ba = MutableList(n) { 0 }
var i = s.length - 1;
while (i >= 0 && n > 0) {
val c = Character.codePointAt(s, i--)
if (c < 128) { // encode using utf-8
ba[--n] = c;
} else if ((c > 127) && (c < 2048)) {
ba[--n] = (c and 63) or 128;
ba[--n] = (c shr 6) or 192;
} else {
ba[--n] = (c and 63) or 128;
ba[--n] = ((c shr 6) and 63) or 128;
ba[--n] = (c shr 12) or 224;
}
}
ba[--n] = 0;
val rng = SecureRandom()
val x = ByteArray(n) { 0 }
while (n > 2) {
x[0] = 0;
while (x[0].toInt() == 0) {
rng.nextBytes(x)
}
ba[--n] = x[0].toInt()
}
ba[--n] = 2;
ba[--n] = 0;
val baos = ByteArrayOutputStream()
for (item in ba) {
if (item < -128 || item > 127) {
throw Exception("You code is shit")
}
baos.write(item)
}
return BigInteger(baos.toByteArray())
}
private fun doPublic(x: BigInteger): BigInteger {
return x.modPow(this.exponent, this.modulo)
}
private fun encrypt(text: String): String? {
val maxLength = (this.modulo.bitLength() + 7) shr 3
val m = pkcs1pad2(text, maxLength) ?: return null
val c = this.doPublic(m)
var h = c.toString(16);
val length = h.length;
// fix zero before result
for (i in 0 until maxLength * 2 - length) {
h = "0$h";
}
return h
}
}
Implemented in Kotlin

Convert large byteArray to wordArray

I need to convert a large array (Uint8Array(224337596)) inside my code. Apparently the size is to big and makes the browser crash.
Is there any workaround to maybe to this in chunks?
var encrypted = convertUint8ArrayToWordArray(mergedArray)
function convertUint8ArrayToWordArray(u8Array) {
var words = [], i = 0, len = u8Array.length;
while (i < len) {
words.push(
(u8Array[i++] << 24) |
(u8Array[i++] << 16) |
(u8Array[i++] << 8) |
(u8Array[i++])
);
}
return {
sigBytes: words.length * 4,
words: words
};
}
If your browser window freezes, than you can process the array asynchronously in batches. I've put the example into snippet
function pause() {
return new Promise(r => setTimeout(r, 0))
}
async function convertUint8ArrayToWordArray(u8Array) {
var words = [], i = 0, len = u8Array.length;
while (i < len) {
words.push(
(u8Array[i++] << 24) |
(u8Array[i++] << 16) |
(u8Array[i++] << 8) |
(u8Array[i++])
);
if (i % 100000 == 0) {
await pause();
}
}
return {
sigBytes: words.length * 4,
words: words
};
}
const bigArray = new Uint8Array(224337596);
for (let idx = 0; idx < bigArray.length; ++idx) {
bigArray[idx] = Math.floor(Math.random() * 256);
}
convertUint8ArrayToWordArray(bigArray).then((res) => {
console.log(res.words[0])
});

Is function computeHmacSha256Signature(value, key) broken?

I am trying to sign the value eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6InRzIiwibm9uY2UiOiI4MTZkMWVmNi0zYjNlLTQ1MmEtOWM5Yi0xNDYyZWIzNWZlNzUiLCJpc3MiOiIwYzE3MjYwNDEwMjhkMTI5ZGI3YjU4NzUzYzU2OTYwYyIsImV4cCI6MTU4ODUyOTE3OCwibmJmIjoxNTg4NTI4ODE4LCJpYXQiOjE1ODg1Mjg4MTgsImp0aSI6IjE2ZGExZGZiLTkyYjQtNDI0ZS04ZTU5LWIyNzZmYmQ3MWVkYSIsInJlZ2lvbiI6Im55IiwibWV0aG9kIjoiR0VUIiwicGF0aCI6Ii9hdXRoZW50aWNhdGUvY29ubmVjdC9kZXZpY2UiLCJob3N0IjoiaHR0cHM6Ly9hcGkuYmxvb21iZXJnLmNvbSIsImNsaWVudF9pZCI6IjBjMTcyNjA0MTAyOGQxMjlkYjdiNTg3NTNjNTY5NjBjIn0 with key a1b2c3a4b5c6. The key is hexadecimal.
This should generate signature 8Wspda1l2Z3-hLwvMI_5Q8AQic59oclZAav7kWVtGHw. This signature is provided from my service provider as a sample.
I tried the following:
var signature = Utilities.computeHmacSha256Signature("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6InRzIiwibm9uY2UiOiI4MTZkMWVmNi0zYjNlLTQ1MmEtOWM5Yi0xNDYyZWIzNWZlNzUiLCJpc3MiOiIwYzE3MjYwNDEwMjhkMTI5ZGI3YjU4NzUzYzU2OTYwYyIsImV4cCI6MTU4ODUyOTE3OCwibmJmIjoxNTg4NTI4ODE4LCJpYXQiOjE1ODg1Mjg4MTgsImp0aSI6IjE2ZGExZGZiLTkyYjQtNDI0ZS04ZTU5LWIyNzZmYmQ3MWVkYSIsInJlZ2lvbiI6Im55IiwibWV0aG9kIjoiR0VUIiwicGF0aCI6Ii9hdXRoZW50aWNhdGUvY29ubmVjdC9kZXZpY2UiLCJob3N0IjoiaHR0cHM6Ly9hcGkuYmxvb21iZXJnLmNvbSIsImNsaWVudF9pZCI6IjBjMTcyNjA0MTAyOGQxMjlkYjdiNTg3NTNjNTY5NjBjIn0", "a1b2c3a4b5c6");
var encodedSignature = Utilities.base64EncodeWebSafe(signature).replace(/=+$/, '');
but I got different result: vnshuAi4GtWTZ3VxmPgLzJ--V18mm-r4cBUNA0FIvTs.
Since computeHmacSha256Signature(value, key) also allows input to be pair of byte[] (in addition to string pair as an input), I tried the following:
function toUTF8Array(str) {
var utf8 = [];
for (var i = 0; i < str.length; i++) {
var charcode = str.charCodeAt(i);
if (charcode < 0x80) utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6),
0x80 | (charcode & 0x3f));
}
else if (charcode < 0xd800 || charcode >= 0xe000) {
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
// surrogate pair
else {
i++;
// UTF-16 encodes 0x10000-0x10FFFF by
// subtracting 0x10000 and splitting the
// 20 bits of 0x0-0xFFFFF into two halves
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
| (str.charCodeAt(i) & 0x3ff));
utf8.push(0xf0 | (charcode >>18),
0x80 | ((charcode>>12) & 0x3f),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
function resecret(s) {
var bin_secret = "";
var duo = ""
var new_secret_array = [];
var j = 0;
var len=s.length;
var element = 0;
for (var i = 0; i < len; i++) {
duo = s.charAt(i) + s.charAt(i+1);
element = parseInt(duo, 16);
// element = "0x"+duo;
bin_secret = bin_secret + element;
new_secret_array[j] = element;
j++;
i++;
}
return new_secret_array;
}
function test() {
var value="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6InRzIiwibm9uY2UiOiI4MTZkMWVmNi0zYjNlLTQ1MmEtOWM5Yi0xNDYyZWIzNWZlNzUiLCJpc3MiOiIwYzE3MjYwNDEwMjhkMTI5ZGI3YjU4NzUzYzU2OTYwYyIsImV4cCI6MTU4ODUyOTE3OCwibmJmIjoxNTg4NTI4ODE4LCJpYXQiOjE1ODg1Mjg4MTgsImp0aSI6IjE2ZGExZGZiLTkyYjQtNDI0ZS04ZTU5LWIyNzZmYmQ3MWVkYSIsInJlZ2lvbiI6Im55IiwibWV0aG9kIjoiR0VUIiwicGF0aCI6Ii9hdXRoZW50aWNhdGUvY29ubmVjdC9kZXZpY2UiLCJob3N0IjoiaHR0cHM6Ly9hcGkuYmxvb21iZXJnLmNvbSIsImNsaWVudF9pZCI6IjBjMTcyNjA0MTAyOGQxMjlkYjdiNTg3NTNjNTY5NjBjIn0";
var key = "a1b2c3a4b5c6";
var signature = Utilities.computeHmacSha256Signature(toUTF8Array(value),resecret(key));
}
This time, I get an error:
Cannot convert Array to (class)[].
What am I doing wrong here? Has anyone able to run computeHmacSha256Signature(value, key) using byte array?
How do I get the signature 8Wspda1l2Z3-hLwvMI_5Q8AQic59oclZAav7kWVtGHw?

Encode and decode skipping the characters

I am trying to store some strings into the database using their encoded format. But when retrieving back the string is malformed.
Here is my code example where you easily can see that String passed to encode is not the same as after decode. Why is this happening?
Is there any other library which can help me encoding and decoding? Any suggestion on the same will be helpful.
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(/rn/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 str = "background:url(/drona-courses/player_assets/skin_0/DRONA_default_skinRightCorner.png) ;"
var encoded = Base64.encode(str);
//console.log(encoded);
var decoded = Base64.decode(encoded);
console.log(str,"......Input");
console.log(decoded,".....Output");
Base64 Encoding in common browsers
In JavaScript there are two functions respectively for decoding and encoding base64 strings:
atob()
btoa()
The atob() function decodes a string of data which has been encoded using base-64 encoding. Conversely, the btoa() function creates a base-64 encoded ASCII string from a "string" of binary data.
Use atob and btoa.
const foo = "bar"
const encodedFoo = btoa(foo)
const decodedFoo = atob(encodedFoo)
console.log(encodedFoo)
console.log(decodedFoo)
You can read more about it here.

Javascript Base64 encoding UTF8 string fails in webkit/safari

I'm trying to base64 encode a utf8 string containing Thai characters. I'm using the browser's built in btoa function. It works for ascii text, however Thai is causing it to throw a INVALID_CHARACTER_ERR: DOM Exception 5 exception.
Here's a sample that fails (the character that looks like an "n" is Thai)
btoa('aก')
What do I need to do to base64 encode non-ascii strings?
var Base64 = {
encode: function(s) {
return btoa(unescape(encodeURIComponent(s)));
},
decode: function(s) {
return decodeURIComponent(escape(atob(s)));
}
};
Unfortunately btoa/atob aren't specified in any standard, but the implementations in firefox and webkit both fail on multibyte characters so even if they were now specified those builtin functions would not be able to support multibyte characters (as the input and output strings would necessarily change).
It would seem your only option would be to roll your own base64 encode+decode routines
check this workaround
http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
I know this is old, but I was recently looking for a UTF8-to-Base64 encoder as well. I found a handy little script at http://www.webtoolkit.info/javascript-base64.html, and a performance improved version at http://jsbase64.codeplex.com/.
Here is the script:
var B64 = {
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
lookup: null,
ie: /MSIE /.test(navigator.userAgent),
ieo: /MSIE [67]/.test(navigator.userAgent),
encode: function (s) {
var buffer = B64.toUtf8(s),
position = -1,
len = buffer.length,
nan0, nan1, nan2, enc = [, , , ];
if (B64.ie) {
var result = [];
while (++position < len) {
nan0 = buffer[position];
nan1 = buffer[++position];
enc[0] = nan0 >> 2;
enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
if (isNaN(nan1))
enc[2] = enc[3] = 64;
else {
nan2 = buffer[++position];
enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
}
result.push(B64.alphabet.charAt(enc[0]), B64.alphabet.charAt(enc[1]), B64.alphabet.charAt(enc[2]), B64.alphabet.charAt(enc[3]));
}
return result.join('');
} else {
var result = '';
while (++position < len) {
nan0 = buffer[position];
nan1 = buffer[++position];
enc[0] = nan0 >> 2;
enc[1] = ((nan0 & 3) << 4) | (nan1 >> 4);
if (isNaN(nan1))
enc[2] = enc[3] = 64;
else {
nan2 = buffer[++position];
enc[2] = ((nan1 & 15) << 2) | (nan2 >> 6);
enc[3] = (isNaN(nan2)) ? 64 : nan2 & 63;
}
result += B64.alphabet[enc[0]] + B64.alphabet[enc[1]] + B64.alphabet[enc[2]] + B64.alphabet[enc[3]];
}
return result;
}
},
decode: function (s) {
if (s.length % 4)
throw new Error("InvalidCharacterError: 'B64.decode' failed: The string to be decoded is not correctly encoded.");
var buffer = B64.fromUtf8(s),
position = 0,
len = buffer.length;
if (B64.ieo) {
var result = [];
while (position < len) {
if (buffer[position] < 128)
result.push(String.fromCharCode(buffer[position++]));
else if (buffer[position] > 191 && buffer[position] < 224)
result.push(String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63)));
else
result.push(String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63)));
}
return result.join('');
} else {
var result = '';
while (position < len) {
if (buffer[position] < 128)
result += String.fromCharCode(buffer[position++]);
else if (buffer[position] > 191 && buffer[position] < 224)
result += String.fromCharCode(((buffer[position++] & 31) << 6) | (buffer[position++] & 63));
else
result += String.fromCharCode(((buffer[position++] & 15) << 12) | ((buffer[position++] & 63) << 6) | (buffer[position++] & 63));
}
return result;
}
},
toUtf8: function (s) {
var position = -1,
len = s.length,
chr, buffer = [];
if (/^[\x00-\x7f]*$/.test(s)) while (++position < len)
buffer.push(s.charCodeAt(position));
else while (++position < len) {
chr = s.charCodeAt(position);
if (chr < 128)
buffer.push(chr);
else if (chr < 2048)
buffer.push((chr >> 6) | 192, (chr & 63) | 128);
else
buffer.push((chr >> 12) | 224, ((chr >> 6) & 63) | 128, (chr & 63) | 128);
}
return buffer;
},
fromUtf8: function (s) {
var position = -1,
len, buffer = [],
enc = [, , , ];
if (!B64.lookup) {
len = B64.alphabet.length;
B64.lookup = {};
while (++position < len)
B64.lookup[B64.alphabet.charAt(position)] = position;
position = -1;
}
len = s.length;
while (++position < len) {
enc[0] = B64.lookup[s.charAt(position)];
enc[1] = B64.lookup[s.charAt(++position)];
buffer.push((enc[0] << 2) | (enc[1] >> 4));
enc[2] = B64.lookup[s.charAt(++position)];
if (enc[2] == 64)
break;
buffer.push(((enc[1] & 15) << 4) | (enc[2] >> 2));
enc[3] = B64.lookup[s.charAt(++position)];
if (enc[3] == 64)
break;
buffer.push(((enc[2] & 3) << 6) | enc[3]);
}
return buffer;
}
};
Disclaimer: I haven't tested this with Thai characters specifically, but assume it will work.
Sav

Categories