RSA private key unable to decrypt data - javascript

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.

Related

Nodejs and Amazon s3 invalid sha256 calculations

I have this issue with node and amazon s3 when it comes to sha256 encryption. I'm reading my files from the file system using fs.createReadStream(filename).am getting this file in chunks.Then am pushing each chunk into an array. Each chunk consists of 1 *1024 *1024 bytes of data.. when the file has finished getting read, on readstream.on('end') am looping through each value in array and encrypting each chunk using sha256. in the process of looping, am also adding axios promises of each loop into an array so that when am finished looping through all the chunks and encrypting each at a time, I'm able to use promise.all to send all the requests. the result of each encrypted chunk is also sent together with the each request as an header . The challenge I've been facing and trying to solve is, whenever a request is made, it gets the calculations of the sha256 from s3 is completely different from what I have . I have tried to solve this and to understand this to no avail. below is my code, what could I be doing wrong ?
this is the error that am getting :
Corrupted chunk received:
File corrupted: Expected SHA jakoz9d12xYjzpWVJQlqYdgPxAuF+LjZ9bQRg0hzmL8=, but calculated SHA 103f77f9b006d9b5912a0da167cf4a8cec60b0be017b8262cd00deb3183f3a8b
const Encryptsha256 = function(chunksTobeHashed) {
var crypto = require('crypto');
var hash = crypto.createHash('sha256').update(chunksTobeHashed).digest('base64')
return hash;
}
const upload = async function(uploadFile) {
var folderPath = uploadFile.filePath
var chunksArray = []
var uploadFileStream = fs.createReadStream(folderPath, { highWaterMark: 1 * 1024 * 1024, encoding:"base64" })
uploadFileStream.on('data', (chunk) => {
chunksArray.push(chunk)
// console.log('chunk is ', chunk)
})
uploadFileStream.on('error', (error) => {
console.log('error is ', error)
})
// file_id: "2fe44d18-fa94b201-2fe44d18b196-f9066e05a81c"
uploadFileStream.on('end', async() => {
//code to get file id was here but removed.since it was not much neccessary to this quiz
var file_id = "2fe44d18-fa94b201-2fe44d18b196-f9066e05a81c"
let promises = [];
for (var i in chunksArray) {
var Content_SHA256 = Encryptsha256(chunksArray[i])
var payload = {
body: chunksArray[i],
}
promises.push(
axios.post(
`${baseURL}/home/url/upload/${fileId}/chunk/${i}`, payload, {
header: {
'Content-SHA256': Content_SHA256,
},
}
)
)
}
Promise.all(promises).then((response) => {
console.log('axios::', response)
})
.catch((error) => {
console.log('request error', error)
})
})

How to get receipt-data from Post request in Node.js

I'm trying to send a receipt to my Node.js server. At the moment It prints out the receipt-data in firebase which indicates its gotten it from the POST request I sent from my iOS App. However i can't pass properly in my code to be validated in const receipt = request.body;. How can I pass it along in the correct way?. Thanks.
exports.receiptValidation = functions.https.onRequest((request, response) => {
const receipt = request.body;
console.log(receipt);
iap.config({
applePassword: 'MySharedAppleKey',
test: true
});
iap.setup((error) => {
if (error) {
console.log('Failed to validate receipt', error);
}
});
iap.validate(receipt).then((pResponse) => {
//Check if receipt is valid
if (iap.isValidated(pResponse)) {
console.log('The receipt is valid', pResponse);
response.send(pResponse);
}
return pResponse;
})
.catch((error) => {
console.log(`Sorry couldn't validate receipt`, error);
response.send(error);
return error
});
});
Here is my output in Firebase. I'm using a firebase function
I solved it by changing my receipt-data string in both Swift and Server code
Swift
let receiptData = receipt!.base64EncodedString()
let requestReceiptDict = ["receipt": receiptData]
Node.js
const receipt = request.body.receipt;
console.log(receipt);

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

Digital Envelope Routines: Bad Decrypt

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))
}

Using node mssql how do I create a database?

I tried the execute method:
const sql = require('../node_modules/mssql');
var dbname = 'AddressBook';
sql.connect('mssql://sa:1234#localhost/').then(pool => {
return pool.request().input('db_name', sql.TYPES.Text, dbname).query`select db_id(#db_name) as idn`.then(result => {
if (result[0].idn === null) {
return pool.request().input('db_name', sql.TYPES.Text, dbname).execute`create database #db_name`;
}
}).catch(err => {throw(err)});
}).catch(err => console.log(err));
I get:
message: 'The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. The RPC name is invalid.',
Also tried the query method:
return pool.request().input('db_name', sql.TYPES.Text, dbname).query`create database #db_name`;
I get
message: 'Incorrect syntax near \'#db_name\'.',
Is there a different method or am I missing something?

Categories

Resources