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 need to modify existing frontend (angular) code that involves uploading files to a server. Now the files need to be encrypted before being uploaded.
The current approach uses FormData to append a number of files and send them in a single request as shown below:
function uploadFiles(wrappers){
var data = new FormData();
// Add each file
for(var i = 0; i < wrappers.length; i++){
var wrapper = wrappers[i];
var file = wrapper.file;
data.append('file_' + i, file);
}
$http.post(uri, data, requestCfg).then(
/*...*
I have been using Forge in other projects, but never in this sort of context and don't really see how to encrypt files on the fly and still append them as FormData contents.
Forge provides an easy API:
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(8);
// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
The backend recieves files using Formidable and all the file hanlding is already wired. I would thus like to stick to using the existing front-end logic but simply insert the encryption logic. In that, it's not the entire formdata that must be encrypted... I haven't found a good lead yet to approach this.
Suggestions are very welcome!
Ok, found a solution and added the decrypt code as well. This adds a layer of async code.
function appendFile(aFile, idx){
// Encrypt if a key was provided for this protocol test
if(!key){
data.append('dicomfile_' + idx, file);
appendedCount++;
onFileAppended();
}
else{
var reader = new FileReader();
reader.onload = function(){
// 1. Read bytes
var arrayBuffer = reader.result;
var bytes = new Uint8Array(arrayBuffer); // byte array aka uint8
// 2. Encrypt
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(bytes));
cipher.finish();
// 3. To blob (file extends blob)
var encryptedByteCharacters = cipher.output.getBytes(); // encryptedByteCharacters is similar to an ATOB(b64) output
// var asB64 = forge.util.encode64(encryptedBytes);
// var encryptedByteCharacters = atob(asB64);
// Convert to Blob object
var blob = byteCharsToBlob(encryptedByteCharacters, "application/octet-stream", 512);
// 4. Append blob
data.append('dicomfile_' + idx, blob, file.name);
// Decrypt for the sake of testing
if(true){
var fileReader = new FileReader();
fileReader.onload = function() {
arrayBuffer = this.result;
var bytez = new Uint8Array(arrayBuffer);
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(bytez));
decipher.finish();
var decryptedByteCharacters = decipher.output.getBytes();
var truz = bytes === decryptedByteCharacters;
var blob = byteCharsToBlob(decryptedByteCharacters, "application/octet-stream", 512);
data.append('decrypted_' + idx, blob, file.name + '.decrypted');
appendedCount++;
onFileAppended();
};
fileReader.readAsArrayBuffer(blob);
}
else{
// z. Resume processing
appendedCount++;
onFileAppended();
}
}
// Read file
reader.readAsArrayBuffer(aFile);
}
}
function onFileAppended(){
// Only proceed when all files were appended and optionally encrypted (async)
if(appendedCount !== wrappers.length) return;
/* resume processing, upload or do whathever */
I would like to give the users in my website the ability to download a "lnk" file.
My idea is to generate this file with to contain an address that can be used only once.
Is there a way to generate this file in javascript?
The flow is something like -
the user presses a button
the javascript generates this file and downloads it to the user's machine
the user sends this file to another user to use this one-time-address from his machine
Is something like this is doable in javascript from the client side? or would i need to generate this file using java server side?
This is a faithful translation of mslink.sh.
I only tested my answer in Windows 8.1, but I would think that it works in older versions of Windows, too.
function create_lnk_blob(lnk_target) {
function hex_to_arr(s) {
var result = Array(s.length / 2);
for (var i = 0; i < result.length; ++i) {
result[i] = +('0x' + s.substr(2*i, 2));
}
return result;
}
function str_to_arr(s) {
var result = Array(s.length);
for (var i = 0; i < s.length; ++i) {
var c = s.charCodeAt(i);
if (c >= 128) {
throw Error("Only ASCII paths are suppored :-(");
}
result[i] = c;
}
return result;
}
function convert_CLSID_to_DATA(s) {
var idx = [[6,2], [4,2], [2,2], [0,2],
[11,2], [9,2], [16,2], [14,2],
[19,4], [24,12]];
var s = idx.map(function (ii) {
return s.substr(ii[0], ii[1]);
});
return hex_to_arr(s.join(''));
}
function gen_IDLIST(s) {
var item_size = (0x10000 + s.length + 2).toString(16).substr(1);
return hex_to_arr(item_size.replace(/(..)(..)/, '$2$1')).concat(s);
}
var HeaderSize = [0x4c, 0x00,0x00,0x00],
LinkCLSID = convert_CLSID_to_DATA("00021401-0000-0000-c000-000000000046"),
LinkFlags = [0x01,0x01,0x00,0x00], // HasLinkTargetIDList ForceNoLinkInfo
FileAttributes_Directory = [0x10,0x00,0x00,0x00],
FileAttributes_File = [0x20,0x00,0x00,0x00],
CreationTime = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
AccessTime = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
WriteTime = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
FileSize = [0x00,0x00,0x00,0x00],
IconIndex = [0x00,0x00,0x00,0x00],
ShowCommand = [0x01,0x00,0x00,0x00], //SW_SHOWNORMAL
Hotkey = [0x00,0x00], // No Hotkey
Reserved = [0x00,0x00],
Reserved2 = [0x00,0x00,0x00,0x00],
Reserved3 = [0x00,0x00,0x00,0x00],
TerminalID = [0x00,0x00],
CLSID_Computer = convert_CLSID_to_DATA("20d04fe0-3aea-1069-a2d8-08002b30309d"),
CLSID_Network = convert_CLSID_to_DATA("208d2c60-3aea-1069-a2d7-08002b30309d"),
PREFIX_LOCAL_ROOT = [0x2f],
PREFIX_FOLDER = [0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
PREFIX_FILE = [0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
PREFIX_NETWORK_ROOT = [0xc3,0x01,0x81],
PREFIX_NETWORK_PRINTER = [0xc3,0x02,0xc1],
END_OF_STRING = [0x00];
if (/.*\\+$/.test(lnk_target)) {
lnk_target = lnk_target.replace(/\\+$/g, '');
var target_is_folder = true;
}
var prefix_root, item_data, target_root, target_leaf;
if (lnk_target.substr(0, 2) === '\\\\') {
prefix_root = PREFIX_NETWORK_ROOT;
item_data = [0x1f, 0x58].concat(CLSID_Network);
target_root = lnk_target.subtr(lnk_target.lastIndexOf('\\'));
if (/\\\\.*\\.*/.test(lnk_target)) {
target_leaf = lnk_target.substr(lnk_target.lastIndexOf('\\') + 1);
}
if (target_root === '\\') {
target_root = lnk_target;
}
} else {
prefix_root = PREFIX_LOCAL_ROOT;
item_data = [0x1f, 0x50].concat(CLSID_Computer);
target_root = lnk_target.replace(/\\.*$/, '\\');
if (/.*\\.*/.test(lnk_target)) {
target_leaf = lnk_target.replace(/^.*?\\/, '');
}
}
var prefix_of_target, file_attributes;
if (!target_is_folder) {
prefix_of_target = PREFIX_FILE;
file_attributes = FileAttributes_File;
} else {
prefix_of_target = PREFIX_FOLDER;
file_attributes = FileAttributes_Directory;
}
target_root = str_to_arr(target_root);
for (var i = 1; i <= 21; ++i) {
target_root.push(0);
}
var id_list_items = gen_IDLIST(item_data);
id_list_items = id_list_items.concat(
gen_IDLIST(prefix_root.concat(target_root, END_OF_STRING)));
if (target_leaf) {
target_leaf = str_to_arr(target_leaf);
id_list_items = id_list_items.concat(
gen_IDLIST(prefix_of_target.concat(target_leaf, END_OF_STRING)));
}
var id_list = gen_IDLIST(id_list_items);
var data = [].concat(HeaderSize,
LinkCLSID,
LinkFlags,
file_attributes,
CreationTime,
AccessTime,
WriteTime,
FileSize,
IconIndex,
ShowCommand,
Hotkey,
Reserved,
Reserved2,
Reserved3,
id_list,
TerminalID);
return new Blob([new Uint8Array(data)], { type: 'application/x-ms-shortcut' });
}
var blob = create_lnk_blob('C:\\Windows\\System32\\Calc.exe');
Use it like:
var blob_to_file = create_lnk_blob('C:\\Windows\\System32\\Calc.exe');
var blob_to_folder = create_lnk_blob('C:\\Users\\Myself\\Desktop\\'); // with a trailing slash
Demo: http://jsfiddle.net/5cjgLyan/2/
This would be simple if your website allows php.
If your script is part of an html file, just write the the javascript as if you were writing it to send a static lnk file. Then, at the lnk address part, break apart the javascript into two parts, breaking into html. Then at that point, put in
<?php /*PHP code set a variable *? /* PHP code to generate proper string*/ PRINT /*PHP variable*/
?>
I think make it pure client is impossible.
Even the web rtc protocol need at least one iceServer to signal other client.
And I think the easiest way to do that is use http://peerjs.com/
you could first create a clinet token of the room owner
//room owner side
peer.on('open', function(my_peer_id) {
console.log('My peer ID is: ' + my_peer_id);
});
And send the token to any other you want (by text file, web chat ...etc)
Then other connect it use the token above
//the other one
var conn = peer.connect(other_peer_id);
After the room owner detected someone entered the room.
Disconnect from signal server, so the token will become unusable
//room owner side
peer.disconnect()
About generate and read file by client side, I recommend you read article below.
http://www.html5rocks.com/en/tutorials/file/dndfiles/ read from file
How to use filesaver.js save as file
I believe the compatibility of fileReader api and blob doesn't matter.
Since there will never be a browser which support webrtc but not support fileReader api
Please suggest any idea to decode the PKCS12 file and get the encrypted private key from it using JavaScript. I know that it can be done very easily using Java Keytool command and Java Security package. But I want it to be done by Java Script. Bellow is my actual requirement.
I have a ".p12" extention file which is one of the formats of pkcs12.
It should be decoded first and need to trace out the decoded file where exactly the encrypted Private key is placed.
Need to get that encrypted Private key and Decrypt it and send it to the receiver.
And all this Should be done only in JAVASCRIPT.
I think this might be what you are looking for:
"A native implementation of TLS (and various other cryptographic tools) in JavaScript."
https://github.com/digitalbazaar/forge#pkcs12
It sounds like this example is close:
// decode p12 from base64
var p12Der = forge.util.decode64(p12b64);
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(p12Der);
// decrypt p12
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
// look at pkcs12.safeContents
// generate p12, base64 encode
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
privateKey, certificateChain, 'password');
var p12Der = forge.asn1.ToDer(p12Asn1).getBytes();
var p12b64 = forge.util.encode64(p12Der);
Rgds....Hoonto/Matt
This will work Perfectly
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(buffer);
// decrypt p12 using the password 'password'
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);
// get bags by type
var certBags = p12.getBags({bagType: forge.pki.oids.certBag});
var pkeyBags = p12.getBags({bagType: forge.pki.oids.pkcs8ShroudedKeyBag});
// fetching certBag
var certBag = certBags[forge.pki.oids.certBag][0];
// fetching keyBag
var keybag = pkeyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0];
// generate pem from private key
var privateKeyPem = forge.pki.privateKeyToPem(keybag.key);
// generate pem from cert
var certificate = forge.pki.certificateToPem(certBag.cert);
Thanks to the examples from #Ujjawal and #hoonto I was able to get the following working well.
const decodePKCS12 = (
file // Dom File object
) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = evt => {
try {
const binary = evt && evt.target ? evt.target.result : null
if (!binary) {
reject(new Error('No file data'))
}
const p12Asn1 = asn1.fromDer(binary)
const p12 = pkcs12.pkcs12FromAsn1(p12Asn1)
const certBags = p12.getBags({bagType: pki.oids.certBag})
const pkeyBags = p12.getBags({bagType: pki.oids.pkcs8ShroudedKeyBag})
const certBag = certBags[pki.oids.certBag][0]
const keybag = pkeyBags[pki.oids.pkcs8ShroudedKeyBag][0]
const certificate = pki.certificateToPem(certBag.cert)
const privateKey = pki.privateKeyToPem(keybag.key)
resolve({certificate, privateKey})
} catch (e) {
reject(e)
}
}
reader.onerror = reject
reader.readAsBinaryString(file)
})
}