Digital Envelope Routines: Bad Decrypt - javascript

I am encrypting an object in the frontend and sending an HTTP POST request with the encrypted data. In the backend I am trying to decrypt this object but it fails.
The tests are passing but when integrated to the actual project this decrypt method fails with error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt at Error (native) at Decipher.final (crypto.js:158:26) at Object.exports.decrypt.error [as decrypt]
Here is the relevant code:
export const decrypt = text => {
if (!text)
throw Error('Decrypt: Text may not be blank');
const decipher = crypto.createDecipher(encryptAlgorithm,
encryptionKey)
let decrypted = decipher.update(text, textEncodingHex,
textEncodingUtf8)
decrypted += decipher.final(textEncodingUtf8)
return decrypted
}
And this is how I am using it
authSignInWeb(): any {
return async (request: any, reply: any) => {
try {
let decrytedRequestPayload = request.payload;
if (process.env.REACT_APP_ENCRYPT) {
decrytedRequestPayload = JSON.parse(cryptoHelper.decrypt(request.payload))
}
...
} catch (error) {
reply(error);
...
}
};
};

After going through the Documentation and other online resources I managed to solve this problem. What made this fail is that HapiJs takes the incoming payload, parses it, and pass it down to the authSignInWeb() as a Javascript object with the actual payload as the key in that object it makes on my behalf.
To solve this I had to, in the frontend, encrypt the data, manually create an object and assign the encrypted information. And then in the backend access the payload's key of the object.
In code:
The frontend is like so:
let encryptedData = {};
if (process.env.REACT_APP_ENCRYPT) {
encryptedData.data = Crypt.encrypt(JSON.stringify(requestBody))
}
and then in the backend (inside authSignInWeb()) do:
let userAuthData = request.payload;
if (process.env.REACT_APP_ENCRYPT) {
userAuthData = JSON.parse(cryptoHelper.decrypt(userAuthData.data))
}

Related

How you manually parse a raw request withtout using express.raw()

i'm implementing Stripe Webhooks and everything works fine but I'm having problems validating the payload when there are special characters(example, áéíóú). The code:
const endpointSecret = "whsec_xxx";
// stripe signature
const sig = headers['Stripe-Signature'][0]
const stripe = require('stripe')(
'sk_test_yyy'
);
//const buf = new Buffer(body, 'base64');
// let text = buff.toString('ascii');
try {
event = stripe.webhooks.constructEvent(body.text(), sig, endpointSecret);
} catch (err) {
response.setStatusCode(400);
throw err;
}
The thing is that i'm using Realm MongoDB HTTP EndPoints that don't support adding a body parser in the function. So What I have is just this:
exports = async function({ query, headers, body }, response) {
I can read the raw body using body.text() and this works fine in most cases, but not all.
What I Need is to emulate the JSON parsing when I declare the function like this:
app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
This way always works. But I don't know how to manually parse the body in the same way that bodyparse does it.
Is there any way to achieve this?

How do I upload a string as a file to Google Drive?

here's my problem :
I want to create a Google Sheets extension in which I basically extract data from a sheet in Google Sheets, that I modify using methods in node JS.
Then, having the data that I modified in a string, I want to upload that string into the client's Drive, in a csv or xml file. Therefore I don't have a local file that I can use to upload the file, just a string variable.
How do I upload that string ?
Thanks a lot, that's my first app and I'm struggling a bit.
Code
const {google} = require ('googleapis');
const keys = require ('./keys.json');
const client = new google.auth.JWT(
keys.client_email, null,
keys.private_key,
['googleapis.com/auth/drive'],
'https://www.googleapis.com/…'
);
client.authorize(function(err, tokens){
if (err){
console.log(err);
return
} else {
console.log('Connected');
gsrun(client);
}
});
async function gsrun(cl) {
const gsapi = google.sheets({version: 'v4', auth: cl});
}
You have to set your file's metadata and the data it will contain (it's important the MIME type for this case must be text/csv) and the file's body will be a simple string. This code will help you taking into consideration you already did the OAuth process and have the string you want to insert:
module.exports.init = async function (){
// Before calling the API, build your own Drive service instance
// In the second argument, you must pass your own string message
const pro = await uploadSimpleString(drive, null);
console.log(pro);
}
uploadSimpleString = (drive, message) => {
// Set file metadata and data
message = message || 'This is a simple String nice to meet you';
const fileMetadata = {'name': 'uploadSimpleStringt.csv'};
const media = {
mimeType: 'text/csv',
body: message
};
// Return the Promise result after completing its task
return new Promise((resolve, reject) => {
try{
// Call Files: create endpoint
return drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
},(err, results) => {
// Result from the call
if(err) reject(`Drive error: ${err.message}`);
resolve(results);
})
} catch (error){
console.log(`There was a problem in the promise: ${error}`);
}
});
}
Notice
To test this code, run it in your CLI using this command:
node -e 'require("./index.js").init()'
Where index.js is your file's name and init() is your main function.
Docs
For more info, please check these links and also consider using the [google-drive-api] tag in that way, there are more chances to receive help because more people will be able to find your question.
How to get Help
Files: create
G Suite documents and corresponding export MIME types

RSA private key unable to decrypt data

I'm using JavaScript and Node.js to work on a sort of messaging project. When a user is created, the server uses the Node.js crypto library to generate an RSA keypair. The private key is encrypted using the users password. On the webapp, when User A sends a message to User B, the data is encrypted using User B's public key. When User B receives the message, it is decrypted using their private key and password.
My problem is that while the app appears to encrypt the data, I am unable to decrypt the data. The error message that is thrown is literally "unable to decrypt data" which is not helpful.
Server-side code for generating keypairs:
// Generate public/private keypairs
exports.generateKeyPair = (password) => {
return new Promise((resolve, reject) => {
crypto.generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: password
}
}, (err, publicKey, privateKey) => {
if (err) reject(err);
else resolve([publicKey, privateKey]);
});
})
}
Client-side code for decrypting and encrypting (Node crypto library is sent to client using Browserify):
var crypto = require('crypto');
window.encrypt = function (data, publicKey) {
let buffer = Buffer.from(data);
let encrypted = crypto.publicEncrypt(publicKey, buffer);
return encrypted.toString('base64');
}
window.decrypt = function(data, privateKey, password) {
let buffer = Buffer.from(data, 'base64');
let decrypted = crypto.privateDecrypt({
key: privateKey,
passphrase: password
}, buffer);
return decrypted.toString('utf8');
}
Client-side code that interacts with the UI:
var sendMsg = function() {
let token = $('#token').val();
let toUser = $('#toUser').val();
let message = btoa($('#sendMessage').val());
fetch(`/keypairs/public/${toUser}?token=${token}`)
.then((res) => res.json())
.then((json) => {
if (json.code != 200) throw 'Error';
else return json.data.pubKey;
})
.then((pubKey) => encrypt(message, pubKey))
.then((encrypted) => $('#send-result').html(encrypted))
.catch((err) => alert(err));
};
var decryptMsg = function() {
let token = $('#token').val();
let encrypted = $('#decrypt-text').val();
let password = $('#decrypt-pass').val();
fetch(`/keypairs/private?token=${token}`)
.then((res) => res.json())
.then((json) => {
if (json.code != 200) throw 'Error';
else return json.data.privKey;
})
.then((privKey) => decrypt(encrypted, privKey, password))
.then((decrypted) => $('#decrypt-result').html(decrypted))
.catch((err) => (console.error(err), alert(err))); // <-- this line gives useless info
};
I paste the encrypted data into a field #'decrypt-text. I also pass the User B password. The token is retrieved during the sign-in process to associate the user with a key in the database. As I mentioned, the only error I get is "Unable to decrypt data". Any ideas? Perhaps I need to generate keys with pkcs1 type?
I figured it out. This issue was that when I created the public keys, the types were spki and pkcs8. For some reason changing both to pkcs1 and regenerating the keys was able to fix it. I also tried passing type: 'pkcs8 to the decrypt function, but that did not work.

How can i send receipt data using Swift to Node.js server

I'm currently trying to verify IAP receipts of user subscriptions in iOS. At first i make a post request that gets me the receipt data from the receipt currently stored in Bundle.main.appStoreReceiptURL. I'm able to get use this receipt data in my server to verify the receipt which works pretty well. However this receipt data string is hard coded in Node which is specific to a certain user. How can i be able to send this receipt string to my server for any receipt depending on any user to be verified. Here is both my Swift and server code. Apple says in the documentation
To retrieve the receipt data, use the appStoreReceiptURL method of NSBundle to locate the app’s receipt, and then read the entire file. Send this data to your server—as with all interactions with your server, the details are your responsibility.
I would like to get the receipt data and send it to my server.
func getReceipt() {
if let receiptUrl = receiptUrl {
do {
let purchaseReceipt = try Data(contentsOf: receiptUrl)
self.validatePurchaseReceipt(pReceiptData: purchaseReceipt)
} catch {
let receiptRefreshRequest = SKReceiptRefreshRequest(receiptProperties: nil)
receiptRefreshRequest.delegate = self
receiptRefreshRequest.start()
}
}
}
func validatePurchaseReceipt(pReceiptData: Data) {
let base64encodedReceipt = pReceiptData.base64EncodedString()
let secretKey = "myAppstoreConnectSecretKey"
let requestReceiptDict = ["receipt-data": base64encodedReceipt, "password": secretKey]
guard JSONSerialization.isValidJSONObject(requestReceiptDict) else { return }
do {
let data = try JSONSerialization.data(withJSONObject: requestReceiptDict)
let validationString = "https://sandbox.itunes.apple.com/verifyReceipt"
guard let validationUrl = URL(string: validationString) else { return }
let session = URLSession(configuration: .default)
var request = URLRequest(url: validationUrl, cachePolicy: .reloadIgnoringLocalCacheData)
request.httpMethod = "POST"
let task = session.uploadTask(with:request, from: data) { (data, response, error) in
guard let data = data, error == nil else { return }
do {
let purchaseReceiptJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print("Success retrieved json:\(purchaseReceiptJSON)")
} catch let error {
print(error)
}
}
task.resume()
} catch let error {
print(error)
}
}
This is my server code
const express = require('express');
const requirePromise = require('request-promise');
const app = express();
let verificationURL = 'https://sandbox.itunes.apple.com/verifyReceipt';
let secretKey = 'myAppstoreConnectSecretKey';
let receiptData = 'MIIntgYJKoZIhvcNAQcCoIInpzCCJ6MCAQExCzAJBgUrDgMCGgUAMIIXVwYJKoZIhvcNAQcBoIIXSASCFetc'
const options = {
url: verificationURL,
method: 'POST',
headers: {
'User-Agent': 'Request-Promise',
'Content-Type': 'application/x-www-form-urlencoded',
},
json: true
};
options.form = JSON.stringify({
'receipt-data': receiptData,
'password': secretKey
});
requirePromise(options).then((resData) => {
console.log(resData);
return resData;
}).catch(function (error) {
console.log(error);
});
If you're going to be using your server to validate receipts (which you should) then the validatePurchaseReceipt method should be calling your server, not the /verifyReceipt endpoint. The receipt data is passed to your server exactly like you do with requestReceiptDict.
Also, the secretKey should be hardcoded on your server - not anywhere on the client.
Here's a similar question: https://stackoverflow.com/a/54261816/3166209

400 Error when Cloud Code from Twilio

We've configured our Twilio number to post to the following parse url but it's returning a 400 error: https://myAppId:javascript-key=myJSkey#api.parse.com/1/functions/sendMsgFromTwilio
The parse api says we need a content type header. Does anyone know what the problem might be?
Here's our code:
Parse.Cloud.define("sendMsgFromTwilio", function (req, res) {
//use From phone number param to get client object
ParseUtils.getUserAccount(Crypto.hash(req.params.From)).then(function(result) {
//get providerId from ProviderClient table
var clientId = result.id;
ParseUtils.getProviderClient(clientId).then(function(result) {
var providerId = result.providerId;
//make sure we're actually passing on a message
if(req.params.Body.length > 0) {
var messageType = 0; //text message type
//prepare parameters in hash as done in sendMsg function
var params = {
params : {
providerId: providerId,
clientId: clientId,
payload: {
type: messageType,
content: req.params.Body
}
}
}
//pass message to helper function to send to pubnub
Messaging.sendMsgAsIs(params).then(function (result) {
res.success(result);
}, function (error) {
res.error(error);
});
}
})
});
});
Thanks in advance.
The correct way to implement this is not as a cloud function, but as a custom endpoint. On the hosted Parse solution, this is documented here: https://parse.com/docs/cloudcode/guide#hosting-dynamic-websites
Now with Parse Server, it would be just a standard express route alongside your server. Find out more about Parse Server here: http://blog.parse.com/announcements/what-is-parse-server/

Categories

Resources