Appel Flap
Appel Flap

Reputation: 271

PHP - Decrypt Encrypted String From Node.js

I want to decrypt a encrypted string (encrypted in Nodejs) using PHP passed to the server.

I have found the perfect Nodejs encrypt/decrypt library: Cryptr. I've created a connection in my JavaScript file sending a request to my server with the encrypted string in it.

Now basically I want to decrypt that string.

Taking a look at the Cryptr source, it seems they're using aes-256-ctr as algo method, and sha256 as encryption method.

My Nodejs: https://runkit.com/embed/keu82yjhwyxj

Encrypted string: 1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333

Then I do this in PHP:

<?php
$encrypted = "1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333";
$algorithm = "aes-256-ctr";
$secret_key = "myTotalySecretKey"; 
$iv = "";

$decrypted_data = openssl_decrypt(pack('H*', $encrypted), $algorithm, $secret_key, OPENSSL_RAW_DATA, $iv);
echo $decrypted_data;

But since the IV is randomly generated in Cryptr, how would I generate this in PHP?

How can I decrypt Cryptr encrypted strings using PHP, so it returns "I love pizza!"?


Edit:

Instead pack('H*', $encrypted) try with $encrypted only. Also, you need only data, method and key for decryption.

<?php
$encrypted = "1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333";
$algorithm = "aes-256-ctr";
$secret_key = "myTotalySecretKey";

$decrypted_data = openssl_decrypt($encrypted, $algorithm, $secret_key);
echo $decrypted_data;

Upvotes: 1

Views: 340

Answers (2)

user149341
user149341

Reputation:

DO NOT USE CRYPTR. It is insecure. I have opened an issue with the developer, although I'm not sure how it can be fixed without a complete rewrite of the module.

Cryptr uses the CTR encryption mode. This mode is designed for specific use cases, and is not resistant to malleability attacks. If the contents of any encrypted message are known, it is possible to transform that message into any other message. For example, given the encrypted string from the usage sample:

const cryptr = new Cryptr('myTotalySecretKey');

const encryptedString = cryptr.encrypt('bacon');
const decryptedString = cryptr.decrypt(encryptedString);

console.log(encryptedString); // "bcb23b81c4839d06644792878e569de4f251f08007"

(Note that the encrypted string isn't even the same length as what is shown in the module usage. This is an apparent error in their documentation.)

Without knowledge of the key, it is possible to modify this string to make it decrypt to "hello":

var tmp = Buffer.from(encryptedString, "hex");
var b1 = Buffer.from("bacon"), b2 = Buffer.from("hello");
for (var i = 0; i < b1.length; i++) {
    tmp[i + 16] ^= b1[i] ^ b2[i];
}
var ep = tmp.toString("hex");
console.log(ep); // "bcb23b81c4839d06644792878e569de4f855ff8306"

And indeed:

var dp = cryptr.decrypt(ep);
console.log(dp); // "hello"

This is a really big deal from a cryptographic perspective. An attacker has just modified an encrypted message in transit, and you have no way of detecting it.

Don't use this module. If you need portable encryption, use the Sodium library; there are bindings available for both Node and PHP.

Upvotes: 2

Pararera
Pararera

Reputation: 372

You need to pass IV as you pass message and security key.

So I did little test

$encrypted = openssl_encrypt("test", "aes-256-ctr", "123", 0, "aaaaaaaaaaaaaaaa");
$algorithm = "aes-256-ctr";
$secret_key = "123"; 
$iv = "";

$decrypted_data = openssl_decrypt($encrypted, $algorithm, $secret_key, 0, "aaaaaaaaaaaaaaaa");
echo $decrypted_data;

And it Works. Yes, it's randomly generated, but you can and have to pass it(as encrypted message) to the server.

I'm not sure what is the purpose of IV parameter but PHP gives you warning if IV param is empty.

I just found this on GitHub page for Cryptr. It says:

The iv is randomly generated and prepended to the result

Upvotes: 0

Related Questions