I am trying to create a JSON object, encrypt it using AES, and then send the data, however I am getting an error when I decrypt it.
The following code works perfectly, it encrypts, and decrypts the needed info.
var encrypted = CryptoJS.AES.encrypt(JSON.stringify(token), 'AGsrv54SDG2DA');
console.log("token is :" + encrypted.toString());
//U2FsdGVkX19QYpU9XWLESHzRB7uvFMtc1J/pCbJjXelTHyeEmU4LCsIMpYZGl9Bs157sX2f47fGeAW4EIaCGiPATxX2PdpZ YMdvPKDIaPYPnmlEJa9yZWyfXf80FNbkRM9Jo9M8GrMiAg8baK6S8GH7GdwxeZEVVkLJpFpgFBUPS3xn2sy/bMHWvOK0lPT0
var bytes = CryptoJS.AES.decrypt(encrypted.toString(), 'AGsrv54SDG2DA');
console.log('BYTES BEFORE:' + bytes);
//bytes : 7b2254797065223a22415554485f434f4445222c22554944223a226e6866476a424c30306c5866735763783332333275587171306c6932222c22434944223a22486f6d655370616365434944222c2245584154223a22546875204d617920333120323031382031373a32333a323920474d542b30303030202855544329227d
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
console.log("auth token UID for test is " + decryptedData.UID); //this works 100%
return {token : encrypted.toString()};
the return statement returns the stringed version of the encrypted data back to my web page. the token is then sent back to another cloud function which attempts to decrypt it as following:
inToken = req.body.code;
console.log("auth token given:" + inToken);
//U2FsdGVkX19QYpU9XWLESHzRB7uvFMtc1J/pCbJjXelTHyeEmU4LCsIMpYZGl9Bs157sX2f47fGeAW4EIaCGiPATxX2PdpZ YMdvPKDIaPYPnmlEJa9yZWyfXf80FNbkRM9Jo9M8GrMiAg8baK6S8GH7GdwxeZEVVkLJpFpgFBUPS3xn2sy/bMHWvOK0lPT0
var bytes = CryptoJS.AES.decrypt(inToken, 'AGsrv54SDG2DA');
console.log('BYTES AFTER:' + bytes);
//7b2254797065223a22415554485f434f4445222c22554944223a226e6866476a424c30306c5866735763783332333275587171306c6932222c22434944223a22486f6d655370616365434944222c2245584154223a22546875204d617920333120323031382031373a32333a323920474d542b30303030202855544329227d
var jsonToken = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
however this throws
Error: Malformed UTF-8 data at Object.stringify
Related
I am trying to decode a JSON Web Token using this function:
function parseJwt(token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};
This works fine in my Google Chrome console, but when I try to use it in Google Scripts it says "atob is not defined". I looked up what atob does, which is decode a 64-bit encoded string. But when I use base64Decode(String) it produces an array instead of a string. How can I reproduce atob's behavior? Or is there another way to decode a JWT?
I found out how to decode a JWT from https://developers.google.com/apps-script/reference/script/script-app#getidentitytoken
function parseJwt(token) {
let body = token.split('.')[1];
let decoded = Utilities.newBlob(Utilities.base64Decode(body)).getDataAsString();
return JSON.parse(decoded);
};
I'm Using node js to create a jwt in my backend server.
I'm using a library to sign/verify a JWT and it work fine. once one jwt.io i paste the token that i got when i sign in and i can see my data in the payload.
So the problem is that I'm trying to generate the signature from header and the payload that i got back in jwt.io
here is what i tryed to do but it did'nt work and i'm confuse a bit.
the algorith used to sign is the default one HS256.
const crypto = require("crypto");
// encode base64 the header
let jsonHeader = JSON.stringify({
alg: "HS256",
typ: "JWT",
});
let bs64header = Buffer.from(jsonHeader).toString("base64").split("=")[0];
console.log("bs64header :>>\n ", bs64header); //look the same as the token i got
// encode vase64 the payload
let jsonPayload = JSON.stringify({
id: "5eb20004ac94962628c68b91",
iat: 1589125343,
exp: 1589989343,
jti: "37743739b1476caa18ca899c7bc934e1aba63ba1",
});
let bs64payload = Buffer.from(jsonPayload).toString("base64").split("=")[0];
console.log("bs64Payload :>> \n", bs64payload); //look the same as the token i got
// TRY to generate the signature from the Base64Header and Base64Payload
// with the secret code that i used to sign the JWT
let secret = "0d528cb666023eee0d44e725fe9dfb751263d2f68f07998ae7388ff43b1b504f";
let signature = bs64header + "." + bs64payload;
let hashed = crypto
.createHash("sha256", secret)
.update(signature)
.digest("hex");
console.log("hashed :>> \n", hashed);
let bs64signature = Buffer.from(hashed).toString("base64").split("=")[0];
console.log("bs64signature>>", bs64signature); //This is where i got stuck.
// let jwt = bs64header + "." + bs64payload + "." + bs64signature;
// console.log("jwt>>", jwt);
I have modified your code a lot to make it less repetitive and easier to read. I am not entirely sure if this will work, so please comment if there are any errors.
I have tested it in runkit and have also checked what the output should be using jwt.io. The output appears to be the same, so I am pretty certain that this works.
Changes
Created a function to base64 encode objects and strings.
Created a function to make base64 strings use the URL safe character set.
Changed crypto.createHash() to crypto.createHmac(), so that a secret key can actually be used.
// base64 encode the data
function bs64encode(data) {
if (typeof data === "object") {
data = JSON.stringify(data);
}
return bs64escape(Buffer.from(data).toString("base64"));
}
// modify the base64 string to be URL safe
function bs64escape(string) {
return string.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
// base64 encode the header
let bs64header = bs64encode({
alg: "HS256",
typ: "JWT"
});
console.log("bs64header :>>\n ", bs64header);
// base64 encode the payload
let bs64payload = bs64encode({
id: "5eb20004ac94962628c68b91",
iat: 1589125343,
exp: 1589989343,
jti: "37743739b1476caa18ca899c7bc934e1aba63ba1"
});
console.log("bs64payload :>> \n", bs64payload);
// generate the signature from the header and payload
let secret = "0d528cb666023eee0d44e725fe9dfb751263d2f68f07998ae7388ff43b1b504f";
let signature = bs64header + "." + bs64payload;
let bs64signature = bs64escape(crypto
.createHmac("sha256", secret)
.update(signature)
.digest("base64"));
console.log("bs64signature>>", bs64signature);
let jwt = bs64header + "." + bs64payload + "." + bs64signature;
console.log("jwt>>", jwt);
I have been trying to communicate with the private API on kraken. The error I get suggests {"error":["EAPI:Invalid key"]} that the encryption/decryption steps are correct. I have tried creating new keys, does not help. I'm wondering if the 'format' of the signature variable is wrong, even though correct in nature.
function balance () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("API_read_only");
var key = sheet.getRange("B5").getValue()
var secret = sheet.getRange("B6").getValue()
// (API method, nonce, and POST data)
var path = "/0/private/TradeBalance"
var nonce = new Date () * 1000
var postdata = "nonce=" + nonce
//Algorithms
//Calculate the SHA256 of the nonce and the POST data
// using goolge script lib
// using more succint function from https://stackoverflow.com/questions/16216868/get-back-a-string-representation-from-computedigestalgorithm-value-byte
function SHA_256 (str) {
return Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, str).reduce(function(str,chr){
chr = (chr < 0 ? chr + 256 : chr).toString(16);
return str + (chr.length==1?'0':'') + chr;
},'');
}
var api_sha256 = SHA_256(nonce + postdata)
//Decode the API secret (the private part of the API key) from base64 // need to stringyfy
var base64 = Utilities.base64Decode(secret)
var base64s = Utilities.newBlob(base64).getDataAsString()
//Calculate the HMAC of the URI path and the SHA256, using SHA512 as the HMAC hash and the decoded API secret as the HMAC key
var hamc512_uri = Utilities.computeHmacSha256Signature(path + api_sha256,base64s)
var hamc512_uris = Utilities.newBlob(hamc512_uri).getDataAsString()
//Encode the HMAC into base64
var signature = Utilities.base64Encode(hamc512_uris)
Logger.log(signature)
//An example of the algorithm using the variables shown above is as follows:
//Base64Encode(HMAC-SHA512 of ("/0/private/TradeBalance" + SHA256("1540973848000nonce=1540973848000&asset=xxbt")) using Base64Decode("FRs+gtq09rR7OFtKj9BGhyOGS3u5vtY/EdiIBO9kD8NFtRX7w7LeJDSrX6cq1D8zmQmGkWFjksuhBvKOAWJohQ==") as the HMAC key
//The result is the API-Sign value / signature.
// connect
var url = "https://api.kraken.com" + path;
var options = {
method: 'post',
headers: {
'API-Key': key,
'API-Sign': signature
},
payload: postdata
};
var response = UrlFetchApp.fetch (url, options);
json = response.getContentText ();
Logger.log(json)
}
While I cannot spot what's wrong with your code I faced the same problem as well (thinking I have everything correct but getting a EAPI:Invalid key) with different libraries.
The approach that helped me was:
Take some posted working solution, e.g. https://stackoverflow.com/a/43081507/672008 (in Java)
Check that it really works
Fix the nonce parameter to get a stable HMAC end results
Massage my code until I get then same intermediate & end results
In the end I was successful using this library: https://www.npmjs.com/package/jssha
The code:
import jssha from 'jssha';
const secret = '...';
const nonce = 1642383717038;
const message = '';
const path = '/0/private/Balance';
const data = 'nonce=' + nonce;
const dataHash = new jssha('SHA-256', 'TEXT');
dataHash.update(nonce + data + message);
let utf8Encode = new TextEncoder();
const hmacHash = new jssha('SHA-512', 'UINT8ARRAY', { hmacKey: { value: secret, format: 'B64' } });
hmacHash.update(utf8Encode.encode(path));
hmacHash.update(dataHash.getHash('UINT8ARRAY'));
console.log('hmac', hmacHash.getHash('B64'));
I have encrypt my token and data json object like this and that's redirect to a subdomain web app with a guard angular :
// access website Wordpress/jQuery/Crypto-js 3.1.9-1
let encrypted = CryptoJS.AES.encrypt(JSON.stringify(response), SECRET_PASSPHRASE);
window.location.href = APP_HOME + "?access=" + encrypted;
The redirection work's as well but I have this error when my guard try to deycrpt the "access" param.
Error: Malformed UTF-8 data
at Object.stringify (core.js:478)
at WordArray.init.toString (core.js:215)
at AuthGuard.push../src/app/_guards/auth/auth.guard.ts.AuthGuard.decryptData (auth.guard.ts:62)
at AuthGuard.push../src/app/_guards/auth/auth.guard.ts.AuthGuard.canActivate
My function to decrypt - Angular WebApp - I try a lot of variations because I find a lot of persons to have the same bug with this error "Malformed UTF-8 data'.
/* Angular - Crypto-js 3.1.9-1 */
import * as CryptoJS from 'crypto-js';
...
decryptData(data) {
try {
const bytes = CryptoJS.AES.decrypt(data.toString(), this.encryptSecretKey);
let decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
//let aa = CryptoJS.enc.Utf8.stringify(bytes)
// let aa = bytes.toString(CryptoJS.enc.Utf8);
console.log('decryptedData ==>', decryptedData);
return JSON.stringify(decryptedData);
} catch (e) {
console.log(e);
}
}
I have created a URL Shortener web service, which returns JSON, if correct parameters (url) is passed.
Now, since I am learning Node.js, I am trying to create a Node.js wrapper for parsing the data and printing them in console (for now).
I am using http and request module for parsing the JSON data which I received from the url response.
This is my code that prints the data :
var request = require('request');
var http = require('http');
var url = process.argv[2];
var apiUrl = "http://shtr.ml/stats-api.php?url=" + url;
http.get(apiUrl,function(res){
var body = '';
res.on('data',function(chunk)
{
body += chunk;
});
res.on('end',function(){
const resp = JSON.parse(body);
console.log(JSON.stringify(resp));
if(resp.message.toString() == "Success")
{
console.log("Short URL : ",resp.short-url);
console.log("Long URL : ",resp.long-url);
console.log("Creation Date : ",resp.created);
console.log("Total Clicks : ",resp.clicks);
}
else
{
console.log("Stats Error !!");
}
});
}).on('error',function(e){
console.log("Got an error ",e);
});
Now, following is my code output :
C:\node-shtr-module>node index.js http://shtr.ml/ZVFWdk
{"message":"Success","short-url":"http://shtr.ml/ZVFWdk","long-url":"https://github.com/beingadityak/dot-net-mysql","created":"2016-09-27 22:58:06","clicks":"21"}
Short URL : NaN
Long URL : NaN
Creation Date : 2016-09-27 22:58:06
Total Clicks : 21
Why is the resp.short-url returning NaN even though the JSON contains the URL ? Please help.
As always, thanks in advance.
access it using
resp['short-url'] and resp['long-url']