How to translate this encryption function from PHP to Javascript - javascript

I am moving a web app from PHP to a JS-based framework. The app uses mcrypt_encrypt and base64 for encryption. I tried using the mcrypt module in Javascript but I'm not getting the same result.
The original PHP function looks like this
function safe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+', '/', '='), array('-', '_', ''), $data);
return $data;
}
function encrypt($value) {
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, ENCRYPTION_KEY, $text, MCRYPT_MODE_ECB, $iv);
return trim(safe_b64encode($crypttext));
}
My JS version looks like this
const MCrypt = require('mcrypt').MCrypt
const rijndael128Ecb = new MCrypt('rijndael-128', 'ecb')
const iv = rijndael128Ecb.generateIv()
rijndael128Ecb.validateKeySize(false)
rijndael128Ecb.open(ENCRYPTION_KEY, iv)
let cipherText = rijndael128Ecb.encrypt('sometext')
cipherText = Buffer.concat([iv, cipherText]).toString('base64')
cipherText = cipherText.replace('+','-').replace('/','_').replace('=','')

I think you're nearly there, you just need to use the same algorithm, you were using 128-bit Rijndael, I switched to 256-bit in Node.js, it's working now.
// Surely this key is uncrackable...
const ENCRYPTION_KEY = 'abcdefghijklmnop';
const MCrypt = require('mcrypt').MCrypt;
function encryptRijndael256(plainText, encryptionKey) {
const rijndael256Ecb = new MCrypt('rijndael-256', 'ecb');
const iv = rijndael256Ecb.generateIv();
rijndael256Ecb.validateKeySize(false);
rijndael256Ecb.open(encryptionKey, iv);
let cipherText = rijndael256Ecb.encrypt(plainText);
cipherText = cipherText.toString('base64');
cipherText = cipherText.replace('+','-').replace('/','_').replace('=','')
return cipherText;
}
const plainText = 'sometext';
const cipherText = encryptRijndael256(plainText, ENCRYPTION_KEY);
console.log("Cipher text: ", cipherText);
I'm getting the following ciphertext (with the trivial and insecure!) key I'm using:
k3ZQ8AbnxhuO8TW1VciCsNtvSrpbOxlieaWX9qwQcr8
for the result in both PHP and JavaScript.

Related

How to get private key (in the right format) with sodium-plus js (secretbox) from html page for decrypting a message

I can do that : Encrypt on frontend (sodium-plus.js) with public-key from backend (PHP sodium)
But I want to do the contrary (encrypt with php, decrypt with javascript), and I have an issue.
I can get my private key from my html page (generated with php) as a hex string ( sodium_bin2hex(sodium_crypto_secretbox_keygen()) ), but I can't use it with sodium plus.
I know this code to get public key :
let key = X25519PublicKey.from('...', "hex");
but in my case that doesn't work and I have an error passing this variable in
await sodium.crypto_secretbox_open(text, nonce, key);
I've tried just with the hex string convert to bin ( await sodium.sodium_hex2bin(key) ) but it doesn't work too.
Here is my code :
define(function (require) {
const { SodiumPlus } = require("sodium-plus");
});
let sodium;
(async function () {
if (!sodium) sodium = await SodiumPlus.auto();
let text = "...";//my text + nonce (at the end) in hex
let nonce = text.substr(-48);
text = text.substr(0, text.length - 48);
let key = X25519PublicKey.from($("#key").text(), "hex");//get my private key in hex, on my html page
text = await sodium.sodium_hex2bin(text);
nonce = await sodium.sodium_hex2bin(nonce);
let output = await sodium.crypto_secretbox_open(text, nonce, key);
console.log(output.toString());
})();
thank you
Here is my solution with crypto_box and secret and public twice :
define(function (require) {
const { SodiumPlus } = require("sodium-plus");
});
let sodium;
(async function () {
if (!sodium) sodium = await SodiumPlus.auto();
let text = ""; //string in hex to be decrypted (see below for my php code server side)
let nonce = text.substring(text.length -48); //nonce at the end of the string
nonce = await sodium.sodium_hex2bin(nonce); //nonce hex to bin
text = text.substring(0, text.length - 48); //text without nonce
text = await sodium.sodium_hex2bin(text); //text hex to bin
let publicKey = $("#key").text(); //to get my key in hex from my html page (key = secret key + public key), see below my php code $decryption_key
let secretKey = publicKey.substring(0, 64);//to get the public key in hex
secretKey = X25519SecretKey.from(secretKey, "hex"); //public key hex to array bin
publicKey = publicKey.substring(publicKey.length -64); //same for public key
publicKey = X25519PublicKey.from(publicKey, "hex");
let output = await sodium.crypto_box_open(text, nonce, secretKey, publicKey);
output = output.toString(); // bin to string
console.log(output);
})();
Here is my php code to encrypt, for example :
$text = '...'; //text to encrypt
$key = sodium_hex2bin( '...' ); //key in hex (got from an other code, see below $encryption_key)
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$text_encrypt = sodium_crypto_box($text, $nonce, $key);
$text_encrypt = sodium_bin2hex($text_encrypt.$nonce);
return $text_encrypt;
My php code for encryption key in php, and decryption key on my html page (then got by javascript)
$keypair1 = sodium_crypto_box_keypair();
$keypair1_secret = sodium_crypto_box_secretkey($keypair1);
$keypair1_public = sodium_crypto_box_publickey($keypair1);
$keypair2 = sodium_crypto_box_keypair();
$keypair2_secret = sodium_crypto_box_secretkey($keypair2);
$keypair2_public = sodium_crypto_box_publickey($keypair2);
$encryption_key = sodium_crypto_box_keypair_from_secretkey_and_publickey($keypair1_secret, $keypair2_public);
$encryption_key = sodium_bin2hex( $encryption_key ); //for my php encrypt code
$decryption_key = sodium_crypto_box_keypair_from_secretkey_and_publickey($keypair2_secret, $keypair1_public);
$decryption_key = sodium_bin2hex( $decryption_key ); //for my html page, then got by javascript

Decrypt the crypto data

I am trying to encrypt and decrypt values using node inbuild module crypto. I have followed this tutorial to encrypt the data. They haven't to gave any sample code to decrypt. When I try to use other tutorial code to decrypt the data. It not working out. Please help me out,
Code
const crypto = require('crypto');
// Difining algorithm
const algorithm = 'aes-256-cbc';
// Defining key
const key = crypto.randomBytes(32);
// Defining iv
const iv = crypto.randomBytes(16);
// An encrypt function
function encrypt(text) {
// Creating Cipheriv with its parameter
let cipher = crypto.createCipheriv(
'aes-256-cbc', Buffer.from(key), iv);
// Updating text
let encrypted = cipher.update(text);
// Using concatenation
encrypted = Buffer.concat([encrypted, cipher.final()]);
// Returning iv and encrypted data
return encrypted.toString('hex');
}
var op = encrypt("Hi Hello"); //c9103b8439f8f1412e7c98cef5fa09a1
Since you havent provided the code for decryption, Cant help you what is actually wrong you doing, apart from that you can do this to get decrypted code:
const crypto = require('crypto')
// Defining key
const key = crypto.randomBytes(32)
// Defining iv
const iv = crypto.randomBytes(16)
// An encrypt function
function encrypt(text) {
// Creating Cipheriv with its parameter
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv)
// Updating text
let encrypted = cipher.update(text)
// Using concatenation
encrypted = Buffer.concat([encrypted, cipher.final()])
// Returning iv and encrypted data
return encrypted.toString('hex')
}
var op = encrypt('Hi Hello')
console.log(op)
function decrypt(data) {
// Creating Decipheriv with its parameter
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv)
// Updating text
const decryptedText = decipher.update(data, 'hex', 'utf8')
const finalText = decryptedText + decipher.final('utf8')
return finalText
}
var decrptedData = decrypt(op)
console.log(decrptedData)

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

Error - Simple Encryption and Decryption - Node.js

I'm trying to develop some very simple code to encrypt and decrypt some simple text. The problem appears to be that when running the code the .final() of my createDecipherIV causes that error in the title to be produced.
I've attempted to toy with the various encoding (Binary vs Hex, base 64, etc.)
node: '10.15.3'
openssl: '1.1.0j'
This is an electron project, though I don't know what impact that might have
const crypto = require('crypto');
let sessionKey = crypto.randomBytes(256/8).toString('hex');
class encryption {
constructor() {
this.encryptionOptions = {
algorithm: 'aes-256-cbc',
iv: crypto.randomBytes(16),
key: String,
}
}
encryptMem(memItem){
this.encryptionOptions['key'] = Buffer.from(sessionKey,'hex');
var cipher = crypto.createCipher(this.encryptionOptions['algorithm'], this.encryptionOptions['key'], this.encryptionOptions['iv']);
var cipherText = cipher.update(memItem,'utf8','hex');
cipherText += cipher.final('hex');
return this.encryptionOptions['iv'].toString('hex') + cipherText;
}
decryptMem(memObject){
this.encryptionOptions['key'] = Buffer.from(sessionKey,'hex');
var _iv = Buffer.from(memObject.slice(0,32),'hex')
var _data = memObject.slice(32)
var _decode = crypto.createDecipheriv(this.encryptionOptions['algorithm'], this.encryptionOptions['key'], _iv);
var _decoded = _decode.update(_data,'hex','utf8');
_decoded += _decode.final('utf8')
return _decoded;
}
}
The sample code
e = new encryption 
encryption {encryptionOptions: {…}}
val = e.encryptMem("test")
"adcd1f5876ca02a4420b61df5dfdaa9be3080108020df42dfc630951ffabe0ac"
e.decryptMem(val)
\lib\encrypt.js:25
internal/crypto/cipher.js:172 Uncaught Error: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
at Decipheriv.final (internal/crypto/cipher.js:172)
at encryption.decryptMem (\lib\encrypt.js:26)
at <anonymous>:1:3
final # internal/crypto/cipher.js:172
decryptMem # \lib\encrypt.js:26
(anonymous) # VM433:1
Where as it should simply return "test
EDIT As pointed out in the answer, createCipher needed to be changed to createCipheriv
The following is some shortened code that should also correctly create a new iv with every invocation of encryption, rather then with simply the invocation of the class
const crypto = require('crypto');
let sessionKey = crypto.randomBytes(256/8).toString('hex');
class encryption {
constructor(key = null){
this.algorithm = 'aes-256-cbc';
this.key = key || Buffer.from(sessionKey,'hex');
}
encrypt(plainText){
var iv = crypto.randomBytes(16);
var cipher = crypto.createCipheriv(this.algorithm,this.key,iv);
var cipherText = cipher.update(plainText,'utf8','hex') + cipher.final('hex');
return iv.toString('hex') + cipherText;
}
decrypt(cipherText){
var iv = Buffer.from(cipherText.slice(0,32),'hex');
var data = cipherText.slice(32);
var decode = crypto.createDecipheriv(this.algorithm,this.key,iv);
var decoded = decode.update(data,'hex','utf8') + decode.final('utf8');
return decoded;
}
}
crypto.createCipher
Your block mode (CBC) requires an IV, but you're using createCipher instead of createCipheriv. I assume this is a typo since you're using createDecipheriv.

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