Encrypt in JavaSscript with NaCl, Decrypt With PHP libsodium using KeyPair - javascript

i want to use NaCl library in the client side and libsodium in php to encrypt/decrypt messages between the client and the server using keypair(public,private).
When i encrypt with libsodium and decrypt with NaCl everything works, But when i try to encrypt with NaCl and decrypt with libsodium i get no result.
Encrypting with NaCl in the client side
var message = "Sample Message";
var bytes = nacl.util.decodeUTF8(message);
var client_public = nacl.util.hex2bytearray('35e9f8477b60f61747722698d98791c4e0818ff75da3943a30e13dc0a457d402');
var client_private = nacl.util.hex2bytearray('6382cd5ef713375b6fe3835a763c72dfdc7ddcade7b8bcb9640065582708ef2c');
var server_public = nacl.util.hex2bytearray('1f348bac3b60d3076fc1a32d4e205df67356a9ef16d4b3f391c0e4817f3b987f');
var sharedkey = nacl.box.before(server_public, client_private);
var nonce = nacl.util.hex2bytearray('c031122a5f57e253686d9f6082d1061a1546e5567290c7f0'); // 24 bytes
var cipher = nacl.box.after(bytes, nonce, sharedkey );
var cihper_with_nonce = nacl.util.bytearray2hex(nonce) + bytearray2hex(cipher); // will be sent to the server
//c031122a5f57e253686d9f6082d1061a1546e5567290c7f0b2c05c9fb1483fe9b1bd2d4ae261832a54569748dd7925511b3d236e23e8
nacl.util.hex2bytearray = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
nacl.util.bytearray2hex = bytes => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
nacl.util.decodeUTF8=function(e){;var n,r=unescape(encodeURIComponent(e)),t=new Uint8Array(r.length);for(n=0;n<r.length;n++)t[n]=r.charCodeAt(n);return t}
Decrypting with libsodium in PHP
$cihper_with_nonce = 'c031122a5f57e253686d9f6082d1061a1546e5567290c7f0b2c05c9fb1483fe9b1bd2d4ae261832a54569748dd7925511b3d236e23e8';// received from the client
$server_public = hex2bin('1f348bac3b60d3076fc1a32d4e205df67356a9ef16d4b3f391c0e4817f3b987f');// generated with libsodium
$server_private = hex2bin('cea48d3c14bc9ad26bf3f62932db5d7336b8212866c14dec64670f295ec89d5d');// generated with libsodium
$client_public = hex2bin('35e9f8477b60f61747722698d98791c4e0818ff75da3943a30e13dc0a457d402');// generated with NaCl
$sharedkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($server_private, $client_public );
$nonce = hex2bin(substr($cihper_with_nonce, 0, 48));//24 bytes
$cihper = hex2bin(substr($cihper_with_nonce, 48));
$message = sodium_crypto_box_open($cihper , $nonce, $sharedkey );
var_dump($message);// bool(false)
Am i using the Library the Wrong way or there is something i am doing it wrong?
Because when i encrypt with php and decrypt with NaCl everything works and i did the same way in reverse for the Decryption.
all the bytes(hex) used are generated with the same library.
NaCl Source nacl util
libsodium Source libsodium

Related

Can't decrypt CryptoJS-encrypted strings in PHP

First of all, I'm a newbie in this subject. This is the first time I need to send encrypted data from front-end to back-end in a secure way.
I'm trying to use CryptoJS to encrypt some sensitive data (credit card information) and send it to my PHP backend, where I need to decrypt it to use with the integration with my payment gateway.
This is my JS code:
let card_number = form.find("input#card_number").val();
let card_holder = form.find("input#card_holder").val();
let card_expiration_date = form.find("input#card_expiration_date").val();
let card_cvv = form.find("input#card_cvv").val();
let key = globalSettings.encryption_key;
card_number = CryptoJS.AES.encrypt(card_number, key);
card_holder = CryptoJS.AES.encrypt(card_holder, key);
card_expiration_date = CryptoJS.AES.encrypt(card_expiration_date, key);
card_cvv = CryptoJS.AES.encrypt(card_cvv, key);
formData = {
card_number: card_number.toString(),
card_holder: card_holder.toString(),
card_expiration_date: card_expiration_date.toString(),
card_cvv: card_cvv.toString(),
};
For note, the globalSettings object is sent from backend with configured keys. The generated formData object is sent to back-end with no problem. But, when trying to decrypt the values with openssl_decrypt, I'm only getting false results.
Here is my PHP code:
$card_number = $request->get("card_number");
$card_holder = $request->get("card_holder");
$card_expiration_date = $request->get("card_expiration_date");
$card_cvv = $request->get("card_cvv");
$key = ENCRYPTION_KEY;
$method = "AES-256-CBC";
$card_number = openssl_decrypt($card_number, $method, $key);
$card_holder = openssl_decrypt($card_holder, $method, $key);
$card_expiration_date = openssl_decrypt($card_expiration_date, $method, $key);
$card_cvv = openssl_decrypt($card_cvv, $method, $key);
Before applying openssl_decrypt, the variables are exact the same as sent from front-end. But, after applying openssl_decrypt, they are all changed to false, and I can't figure out why and how to fix it to get back decrypted information.

How to use node-js crypto in javascript?

I need following encryption decryption, but in java script for client-side.
This code is written in Node-JS and using crypto library, i couldn't find same solution for java script for client side run.
const crypto = require('crypto');
const decrypt = (textBase64, keyBase64, ivBase64) => {
const algorithm = 'aes-256-cbc';
const ivBuffer = Buffer.from(ivBase64, 'base64');
const keyBuffer = Buffer.from(keyBase64, 'base64');
const decipher = crypto.createDecipheriv(algorithm, keyBuffer, ivBuffer);
decipher.setAutoPadding(false);
let decrypted = decipher.update(textBase64, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
const encryptedMessage = '';
const key = 'cGFzc3dvcmQxMjM0NTY3ODk=';
const iv = Buffer.from(encryptedMessage, 'base64').slice(0, 16);
// the message comes from the bytes AFTER the IV - this is what you should decrypt
const message = Buffer.from(encryptedMessage, 'base64').slice(16);
const result = decrypt(message, key, iv);
console.log(result);
//console.log(Buffer.from(encryptedMessage, 'base64').slice(0, 16))
First I would say it is not recommended to decrypt on the client side as the key is visible. but you know what you do.
this library is pure JS and should be working in a browser without any pain : ricmoo/aes-js

Decrypting File In Node.js Throws Error But Able To Decrypt Using Bouncy Castle In C#

I'm having an issue where I was given a file to decrypt along with the Key, IV and encryption method (aes-256-ctr). I have been struggling to decrypt this using node.js and keep running into an error saying the IV length is invalid. I was also given the method to decrypt the file using the BouncyCaste library in C#. The C# method seems to work fine with no issues with the IV. What do I need to do differently in node than what is being done here in C# to avoid this error?
Working C# Decryption (Note: IV is in the filename)
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace c_app
{
class Program
{
static void Main(string[] args)
{
var baseFolder = Directory.GetCurrentDirectory();
// Create parameters
var myDi = new DirectoryInfo(baseFolder);
string keyString = "xxxxxxxxxxxxx32BitKeyxxxxxxxxxxx";
byte[] keyBytes = ASCIIEncoding.UTF8.GetBytes(keyString);
var c = CipherUtilities.GetCipher("AES/CTR/NoPadding");
KeyParameter sk = ParameterUtilities.CreateKeyParameter("AES", keyBytes);
// Get the IV
var files = myDi.GetFiles("001398.20180823.000_1966151284357350418");
var fileInfo = files[0];
var pathSplit = fileInfo.FullName.Split('_');
var filePath = pathSplit[0];
var iv = long.Parse(pathSplit[1]);
// Decrypt the file
var base64String = File.ReadAllText(fileInfo.FullName);
c.Init(false, new ParametersWithIV(sk, BitConverter.GetBytes(iv)));
var inputBytesToDecrypt = Convert.FromBase64String(base64String);
var decryptedBytes = c.DoFinal(inputBytesToDecrypt);
var decryptedText = ASCIIEncoding.UTF8.GetString(decryptedBytes);
// Write file back to current directory
File.WriteAllBytes(string.Format(#"{0}/decrypted.csv", myDi.FullName), decryptedBytes);
}
}
}
My attempt in Node.js
var crypto = require('crypto');
var fs = require('fs');
var iv = Buffer.from('1966151284357350418', 'base64'); // Experimenting with encoding types like base64
var key = Buffer.from('xxxxxxxxxxxxx32BitKeyxxxxxxxxxxx');
fs.readFile('./001398.20180823.000_1966151284357350418', (err, encryptedText) => {
if (err) throw err;
var decipher = crypto.createDecipheriv('aes-256-ctr', key, iv); // <--- Error thrown here
decrypted = decipher.update(encryptedText, 'binary', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
});
In case it is helpful I was also given the method for encrypting the file using BouncyCastle, which can be found here: https://pastebin.com/PWNzDum3

Having trouble correctly creating JWT for google service account auth

I'm trying to create a script on scriptr.io that creates a JWT/JWS to send to google's token endpoint in order to get an auth_token for my service account. I'm using the CryptoJS library in order to do the encrypting. I'm able to generate all 3 parts of the JWT, but I'm doing something wrong when doing so. I believe it has something to do with the last of the three parts of the string (so, the signature part), but I could be wrong.
var cryptoJs = {};
cryptoJs['SHA256'] = require('CryptoJS/rollups/sha256.js').CryptoJS.SHA256
var pHeader = {"alg":"RS256","typ":"JWT"}
var sHeader = JSON.stringify(pHeader);
var encodedHeader = Base64EncodeUrl(btoa(sHeader));
console.log("encodedHeader: " + encodedHeader);
var now = new Date();
var oneHourExpiration = ((now.getTime()-now.getMilliseconds())/1000)+3000;//3000, not 3600 which is 1 hour
var pClaim = {};
pClaim.iss = "-------#---iam.gserviceaccount.com";
pClaim.scope = "https://www.googleapis.com/auth/spreadsheets";
pClaim.aud = "https://www.googleapis.com/oauth2/v3/token";
pClaim.exp = oneHourExpiration;
pClaim.iat = Math.floor(Date.now()/1000);
console.log("exp: " + pClaim.exp);
console.log("iat: " + pClaim.iat);
var sClaim = JSON.stringify(pClaim);
var encodedClaim = Base64EncodeUrl(btoa(sClaim));
console.log("encodedClaim: " + encodedClaim);
var byteArray = encodedHeader + "." + encodedClaim;
console.log("byteArray: " + byteArray);
var secret = "-----BEGIN PRIVATE KEY-----\n.....MIIE.....=\n-----END PRIVATE KEY-----\n";
var signature = cryptoJs.SHA256(byteArray, secret);
var encodedSignature = Base64EncodeUrl(btoa(signature));
console.log("Encoded Signature: " + encodedSignature);
var sJWS = byteArray + "." + encodedSignature;
console.log("JWT: " + sJWS);
function Base64EncodeUrl(str){
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
}
var http = require("http");
var requestObject = {
"url": "https://www.googleapis.com/oauth2/v3/token",
"method": "POST",
"headers": {"Content-Type":"application/x-www-form-urlencoded"},
"params": {"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":sJWS}
}
var response = http.request(requestObject);
var responseBodyStr = response.body;
console.log(responseBodyStr);
var token = JSON.parse(responseBodyStr.access_token);
console.log(token);
When I send the request to the token endpoint with the JWT I get the following response
{
"error": "invalid_grant",
"error_description": "Invalid JWT Signature."
}
Any idea where I'm going wrong? Can someone help me correctly format the JWT so I can get a token?
The function used is doing a hash, not a digital signature
var signature = cryptoJs.SHA256(byteArray, secret);
Digital signature with a RSA private key is not supported . Take a look at the comment in the main repository of CryptoJS
Inactivity
CryptoJS is a project that I enjoy and work on in my spare time, but
unfortunately my 9-to-5 hasn't left me with as much free time as it
used to. I'd still like to continue improving it in the future, but I
can't say when that will be. If you find that CryptoJS doesn't meet
your needs, then I'd recommend you try Forge.
I suggest to move the code to use other Javascript library like recommended. For example forge support RSA signatures (https://github.com/digitalbazaar/forge#rsa)
Google OAuth2 server uses RS256. I have provided an snippet to convert the secret key (I assumed PEM format) to forge and sign data using RSA with SHA256
The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm. This is expressed as RS256 in the alg field in the JWT header.
// convert a PEM-formatted private key to a Forge private key
var privateKey = forge.pki.privateKeyFromPem(pem);
// sign data with a private key and output DigestInfo DER-encoded bytes (defaults to RSASSA PKCS#1 v1.5)
var md = forge.md.sha256.create();
md.update(byteArray, 'utf8');
var signature = privateKey.sign(md);
//convert signature to base64
var encodedSignature = Base64EncodeUrl(btoa(signature));

how to encrypt in Python and decrypt in Javascript?

I can encrypt/decrypt with Python OR with JavaScript but passing encrypted data generated by Python to my JavaScript code fails.
The base64 encoding/decoding works across languages so that base encoding on Python and decoding on JavaScript retrieves the original encrypted string.
Outside of test functions I am not using Python decrypt or JavaScript encrypt but they are here for completeness as some readers miss text saying they exist.
In Python 2:
import base64
from pyaes import AESModeOfOperationCTR
SECRET_KEY = "This_key_for_demo_purposes_only!"
def encrypt(raw_data, key=SECRET_KEY):
aes = AESModeOfOperationCTR(key)
encrypted_data = aes.encrypt(raw_data)
base64_encrypted_data = base64.b64encode(encrypted_data)
return base64_encrypted_data
def decrypt(base64_encrypted_data, key=SECRET_KEY):
encrypted_data = base64.b64decode(base64_encrypted_data)
aes = AESModeOfOperationCTR(key)
decrypted = aes.decrypt(encrypted_data)
return decrypted
In Javascript (running Server-side, on Parse.com Cloud Code):
var Buffer = require('buffer').Buffer;
var Crypto = require('crypto');
encrypt: function(raw) {
var cryptoAlgorithm = "aes-256-ctr";
var key = "This_key_for_demo_purposes_only!"
var cipher = Crypto.createCipher(cryptoAlgorithm, key);
var encrypted = cipher.update(raw, 'utf8', 'binary');
encrypted += cipher.final('binary');
var base = toBase64(encrypted);
return base;
},
decrypt: function(base64raw) {
var raw = fromBase64(base64raw);
var cryptoAlgorithm = "aes-256-ctr";
var key = "This_key_for_demo_purposes_only!"
var decipher = Crypto.createDecipher(cryptoAlgorithm, key);
var decrypted = decipher.update(raw, 'binary', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
The output of the JavaScript function decrypt() is nonsense.
Where is the mismatch?
The output of the two encrypt functions does not match when given the same raw string.
My guess: in the JavaScript I shouldn't be using 'binary'. The JavaScript decrypt() function chokes if specify 'hex' for the data generated from the Python side.

Categories