what is the better library to use gnupg encryption using nodejs
I have a binary public key,
Need to encrypt json payload,
Send it as a formdata (multipart-form) to another API.
I tried looking at openpgp.js, tried reading the key and encrypting, but no luck.
Appreciate any help
a binary public key was given to me along this article to encrypt and send a json payload.
https://www.ibm.com/support/knowledgecenter/SS4T5W/Watson_Talent_Data_Management_Administrator_Guide/gnupg_to_import_pgp_keys.html
I need to do this in nodejs..
I've used openpgp, form-data, stream for posting this as a formdata to api.
below is the code I've tried.
// encrypting the json payload. Is there anything I'm doing wrong here? receiving end is always failing to decrypt, always the response is a bad request
const pgpEncrypt = async (payload) => {
var encryptedKey = await fs.readFileSync("pub.bpg");
const keys = (await openpgp.key.read(encryptedKey)).keys;
const data = await openpgp.encrypt({
message: openpgp.message.fromText(JSON.stringify(payload, null, 0)),
publicKeys: keys,
armor: false,
});
return data.message.packets.write();
};
const formData = new FormData();
let fileContent = await pgpEncrypt(customPayload);
let stream = bufferToStream(Buffer.from(fileContent));
formData.append("file", stream);
const { Readable } = require("stream");
function bufferToStream(binary) {
const readableInstanceStream = new Readable({
read() {
this.push(binary);
this.push(null);
},
});
return readableInstanceStream;
}
axios.post(url, formData, {
headers: {
...formData.getHeaders(),
Authorization: `Bearer ${accessToken}`,
},
})
);
Related
I am lost with using HMAC SHA256 for api authentication. This is my first time using it and I'm not sure what I am missing although I suspect it has to do with the timestamp. Can someone please help me identify what it is I am missing?
Everytime I try and make an API call I get a response stating
data: { success: false, error: 'Not logged in: Invalid signature' }
Here are the requirements for making the API call including the HMAC SHA256.
Here is the code I am using currently:
const axios = require('axios');
var forge = require('node-forge');
require('dotenv').config()
// get timestamp
var time = new Date().getTime();
// generate and return hash
function generateHash(plainText,secretKey)
{
var hmac = forge.hmac.create();
hmac.start('sha256', secretKey);
hmac.update(plainText);
var hashText = hmac.digest().toHex();
return hashText
}
// set axios config
var config = {
url:"https://ftx.us/api/wallet/all_balances",
method:"GET",
headers :{
"FTXUS-KEY":process.env.FTX_API_KEY,
"FTXUS-TS":time,
"FTXUS-SIGN":generateHash(`${new Date()}${"GET"}${"/wallet/all_balances"}`,process.env.FTX_API_SECRET)
}
}
axios(config)
.then(response => {
console.log(response.data)
}).catch(function (error) {
console.log(error);
})
I had to go through the same issue, so here goes my code.
import * as crypto from "crypto";
import fetch from "node-fetch";
// a function to call FTX (US)
async function callFtxAPIAsync(secrets, method, requestPath, body) {
const timestamp = Date.now();
const signaturePayload = timestamp + method.toUpperCase() + "/api" + requestPath + (method.toUpperCase() == "POST" ? JSON.stringify(body) : "");
const signature = crypto.createHmac('sha256', secrets.secret)
.update(signaturePayload)
.digest('hex');
const response = await fetch("https://ftx.us/api" + requestPath, {
method: method,
body: body != null ? JSON.stringify(body) : "",
headers: {
'FTXUS-KEY': secrets.key,
'FTXUS-TS': timestamp.toString(),
'FTXUS-SIGN': signature,
"Content-Type": "application/json",
"Accepts": "application/json"
}
});
return await response.json();
}
then call a post endpoint as for example:
let resultQuote = await callFtxAPIAsync(secrets, "post", "/otc/quotes",
{
"fromCoin": "USD",
"toCoin": "ETH",
"size": usd
});
or a get one:
let resultQuote = await callFtxAPIAsync(secrets, "get", "/otc/quotes/1234");
I hope it helps 😄
You need to add the full URL path, except the domain, in your case /api is missing. Try this:
"FTXUS-SIGN":generateHash(`${new Date()}${"GET"}${"/api/wallet/all_balances"}`,process.env.FTX_API_SECRET)
I am reading the apple business chat api docs and I'm at the section "Receiving Large Interactive Data Payloads". The last step is to decipher an attachment then send to the Business Chat Api.
The Download & data step Documentation
--- And the decipher instructions DOCUMENTATION --
Then, using a cypher library, decrypt the file by using the AES/CTR/NoPadding algorithm with an all-zero, 16-byte initialization vector (IV) with the key value in the Attachment dictionary for the downloaded attachment.
So here is my interpretation of this documentation as they leave me little to work with.
// The single-use, 256-bit AES key represented as a hex-encoded string.
const algorithm = 'aes-256-ctr';
// remove the 00 prefix from the hex-encoded string,
// then decode the string into its original value.
const key = Buffer.from(decryptKey.substr(2), 'hex');
// Use the decoded key value to decrypt the downloaded attachment file.
// THE FULL IMPLEMENTATION
const iv = Buffer.alloc(16, 0);
const key = Buffer.from(decryptKey.substr(2), 'hex');
const decipher = crypto.createDecipheriv(algorithm, key, iv);
decipher.setAutoPadding(false)
let decrypted = decipher.update(data, '', 'hex');
decrypted += decipher.final('hex');
console.log("decrypted:", decrypted);
// Finally send to Apple Business Chat Api
POST https://mspgw.push.apple.com/v1/decodePayload
accept: */*
accept-encoding: gzip, deflate
authorization: Bearer signed-web-token
source-id: business-id
bid: some-bid
{ attachment data }
// Here is a piece of the incoming data
��F�ڼ���/��G����+���)�\M���x�tk��Y(���-�-G�ȍ$t��� )
// After decipher
d3ffade249263d1252ee0dcfa6accd0beff31c607889ff0d31d893adde5063616a15591e181fb698350fb955f
Im not sure if I am doing deciphering correctly as when I send the decrypted code to Apples API
POST https://mspgw.push.apple.com/v1/decodePayload
it is always code response 400
I have contacted Apple for assistance on this issue. I will update this doc as soon as I get a response back from them.
Below is a diagram of the steps needed to take. I stuck at the last 2 steps.
Here is the update for solving the deciphering issues using the apple business chat api with NodeJS. Main issue was converting deciphered data to a buffer before sending to Apple to be decoded.
const decryptKeyFromInteractiveRef = "03f30ff3d3d03dc3".toUpperCase()
async function main(decryptKeyFromInteractiveRef) {
const url = await preDownloadUrl();
const data = await downloadPayload(url);
const decipheredData = await decipherInteractiveRef(data);
const decodedData = await appleDecode(decipheredData);
console.log("Finally your data", decodedData);
async function appleDecode(decipheredData) {
var config = {
method: 'post',
url: 'https://mspgw.push.apple.com/v1/decodePayload',
headers: {
"Authorization": Authorization,
"bid": "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.icloud.apps.messages.business.extension",
"source-id": BIZ_ID,
"accept": "*/*",
"accept-encoding": "gzip, deflate",
'Content-Type': 'application/octet-stream'
},
data: decipheredData
};
const { data } = await axios(config);
const path = Path.resolve(__dirname, 'images', 'data.json')
fs.writeFileSync(path, JSON.stringify(data))
}
async function decipherInteractiveRef() {
const iv = Buffer.alloc(16); // buffer alloc fills with zeros
const key = Buffer.from(decryptKey.slice(2), 'hex',);
const decipher = crypto.createDecipheriv("aes-256-ctr", key, iv);
decipher.setAutoPadding(false); // No Padding
let decrypted = decipher.update(data); // if input is a buffer dont choose a encoding
return decrypted;
}
async function preDownloadUrl() {
//Using the fields in the received interactiveDataRef key,
// retrieve the URL to the payload by calling the /preDownload endpoint.
//interactiveDataRef key
const signatureHex = "81101cc048b6b588c895f01c12715421f9d0a25329".toUpperCase()
const signature = Buffer.from(signatureHex, 'hex').toString('base64')
var configInteractiveRef = {
method: 'get',
url: 'https://mspgw.push.apple.com/v1/preDownload',
headers: {
'Authorization': Authorization,
'source-id': BIZ_ID,
'MMCS-Url': 'https://p56-content.icloud.com/MZ02db38070edccb2ce8c972efdcdd25437439745cad6f15473bb7880d436377702752e134be8bd3b4d695567a5d574142.C01USN00',
'MMCS-Signature': signature,
'MMCS-Owner': 'MZ02db38070edccb2ce8c972efdcdd25437439745cad6f15473bb7880d436377702752e134be8bd3b4d695567a5d574142.C01USN00'
}
};
const response = await axios(configInteractiveRef)
return response.data["download-url"];
}
// download big payload from apple
async function downloadPayload(url) {
const { data } = await axios.get(url, { responseType: 'arraybuffer' });
return data
}}
I want use Azure's STT REST API for my react-native app with recorded audio.
But I've try to search how to pass the wav file but always response "No audio data received" or other error response.
I'm very sure the subscribe key is working cause when I use get token it responses 200.
And the wav file is not the problem,either.Cause when I download the file to my computer then upload it in Azure STT's homepage,it responses the correct answer.
The last,I've tried to figure out how to pass it in right form,but every things are for website.
Here's my code
`
const file = new ReactNativeFile({
uri:
`file://${audioFile}`,
type: 'audio/wav',
name: 'ABCS160101e1a011b160a3e169d7b0.wav',
});
let form = new formData();
const headers = {
'Ocp-Apim-Subscription-Key': 'MyKey',
'Content-type': 'audio/wav; codecs=audio/pcm;samplerate=16000',
Accept: 'application/json',
};
const url = `https://eastasia.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US`;
await form.append('audio', file);
console.log('before');
let response = await axios.post(url, form, {
headers: headers,
});
console.log('after');
console.log('result', JSON.stringify(response));
} catch (err) {
getlog.cw('err23', err);
return err;
}
};`
and Here's My recording function in another place,it's working for play.
import AudioRecord from 'react-native-audio-record';
const options = {
sampleRate: 16000, // default 44100
bitsPerSample: 16, // 8 or 16, default 16,
wavFile: "ABCS160101e1a011b160a3e169d7b0.wav"
};
const toggleRecord = async () => {
if (isRecording) {
const audioFile = await AudioRecord.stop();
setIsRecording(false);
// reloadRecorder();
} else {
setIsRecording(true);
AudioRecord.init(options);
AudioRecord.start();
}
};
Firstly, write
let form = new FormData();
Instead of
let form = new formData();
Secondly, I suppose you audioFile looks like this - file://.... So you dont have to write like this uri: file://${audioFile} ..You can simply write
uri:audioFile
SO final implementation would be
let form = new FormData();
form.append('audio', {
uri: audioFile,
type: 'audio/wav',
name: 'ABCS160101e1a011b160a3e169d7b0.wav',
});
const headers = {
'Ocp-Apim-Subscription-Key': 'MyKey',
'Content-type': 'audio/wav; codecs=audio/pcm;samplerate=16000',
Accept: 'application/json',
};
const url = `https://eastasia.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US`;
console.log('before');
let response = await axios.post(url, form, {
headers: headers,
});
console.log('after');
console.log('result', JSON.stringify(response));
I have a code as shown below that uploads files from the browser and saves in the server, once it has been saved to the server, I want the server to connect to the Pinata API so the file can also be saved to the IPFS node.
let data = new FormData();
const fileBuffer = Buffer.from(`./public/files/${fileName}`, 'utf-8');
data.append('file', fileBuffer, `${fileName}`);
axios.post('https://api.pinata.cloud/pinning/pinJSONToIPFS',
data,
{
headers: {
'Content-Type': `multipart/form-data; boundary= ${data._boundary}`,
'pinata_api_key': pinataApiKey,
'pinata_secret_api_key': pinataSecretApiKey
}
}
).then(function (response) {
console.log("FILE UPLOADED TO IPFS NODE", fileName);
console.log(response);
}).catch(function (error) {
console.log("FILE WASNT UPLOADED TO IPFS NODE", fileName);
console.log(error);
});
The issue i'm having is that after creating a buffer of my file and wrapping it in a formdata, the pinata API returns an error :
data: {
error: 'This API endpoint requires valid JSON, and a JSON content-type'
}
If i convert the data to string like JSON.stringify(data) and change the content-type to application/json, the file buffer will be uploaded successfully as string.
I hope explained it well to get a solution. Thanks.
It looks like you're attempting to upload a file to the pinJSONToIPFS endpoint, which is intended to purely be used for JSON that is passed in via a request body.
In your situation I would recommend using Pinata's pinFileToIPFS endpoint
Here's some example code based on their documentation that may be of help:
//imports needed for this function
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
export const pinFileToIPFS = (pinataApiKey, pinataSecretApiKey) => {
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
//we gather a local file for this example, but any valid readStream source will work here.
let data = new FormData();
data.append('file', fs.createReadStream('./yourfile.png'));
return axios.post(url,
data,
{
maxContentLength: 'Infinity', //this is needed to prevent axios from erroring out with large files
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
'pinata_api_key': pinataApiKey,
'pinata_secret_api_key': pinataSecretApiKey
}
}
).then(function (response) {
//handle response here
}).catch(function (error) {
//handle error here
});
};
The proper code to pin any file to IPFS is as below.
Apparently, even Pinata support staff didn't know this.
You need to set an object with the property name filepath as your last parameter. The name doesn't matter, it can be a duplicate, it can be the same as others, or it can be unique.
const url = "https://api.pinata.cloud/pinning/pinFileToIPFS";
const fileContents = Buffer.from(bytes);
const data = new FormData();
data.append("file", fileContents, {filepath: "anyname"});
const result = await axios
.post(url, data, {
maxContentLength: -1,
headers: {
"Content-Type": `multipart/form-data; boundary=${data._boundary}`,
"pinata_api_key": userApiKey,
"pinata_secret_api_key": userApiSecret,
"path": "somename"
}
});
Code to upload a file on IPFS using Pinata.
There are two methods available to upload files/images on Pinata. One is with Pinata SDK and the second is the pinFileToIPFS endpoint.
If you are uploading files from Next.js then you cannot convert your image into binary using fs.createReadStream or Buffer.from. These packages support the Node side. So if you want to upload the file with Next.js on Pinata then you can use this code.
// convert file into binary
const data = new FormData();
data.append("title", file.name);
data.append("file", file);
const url = "https://api.pinata.cloud/pinning/pinFileToIPFS";
// pass binary data into post request
const result = await axios.post(url, data, {
maxContentLength: -1,
headers: {
"Content-Type": `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: "your_pinata_key",
pinata_secret_api_key:
"your_pinata_secret",
path: "somename",
},
});
console.log("RESULT", result);
this will upload a file to ipfs under the path ipfs://{cid}/images/{fileId}
const PINATA_BASE_URL = "https://api.pinata.cloud";
const PINATA_PIN_URI = "/pinning/pinFileToIPFS";
const fileExt = file.type.split("/")[1];
let nftId = 1
// creates a 64byte string '0000...0001' to follow ERC-1155 standard
const paddedId = createPaddedHex(nftId);
const ipfsFileId = `${paddedId}.${fileExt}`;
const ipfsImageFilePath = `/images/${ipfsFileId}`;
const fileUploadData = new FormData();
// this uploads the file and renames the uploaded file to the path created above
fileUploadData.append("file", file, ipfsImageFilePath);
fileUploadData.append(
"pinataOptions",
'{"cidVersion": 1, "wrapWithDirectory": true}'
);
fileUploadData.append(
"pinataMetadata",
`{"name": "${ipfsImageFilePath}", "keyvalues": {"company": "Pinata"}}`
);
const pinataUploadRes = await axios.post(
PINATA_BASE_URL + PINATA_PIN_URI,
fileUploadData,
{
headers: {
Authorization: `Bearer ${PINATA_JWT}`,
},
}
);
const ipfsCID = pinataUploadRes.data.IpfsHash;
I am trying to send post request to backend containing base64url encoded value of my image. When I send request with any random string, the request is received at backend but when I try to do same thing using encoded value, it responds me back with
request failed with status code 500
My code for the request is:
const uploadFileHandler = async (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setPreviewSource(reader.result);
uploadtobackend(reader.result);
};
const uploadtobackend = async (filedata) => {
try {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
console.log(config);
console.log(filedata);
const { data } = await axios.post(
`/api/uploads`,
{
data: filedata,
},
config,
);
setImages(data);
setUploading(false);
} catch (error) {
console.log(error);
setUploading(false);
}
};
};
Here filedata is the encoded value and it is displayed if I console it.
Instead of
data: filedata
If I send
data: "some random string"
then request reaches backend otherwise not.
You nedd to send your file by wrapping in FormData.
You can do something like
const fd = new FormData()
// data you want to send other than file
fd.append('payload',JSON.stringify(payload))
fd.append('file',file) // file you want to send to node backend
Now in your backed you can get this file using req.file
Probably you need to use JSON.stringify() here are some example of use
const { data } = await axios.post(
`/api/uploads`,
{
data: new Buffer(JSON.stringify(filedata)).toString("base64")
},
config
)