I am currently working on encryption and decryption. I have encrypted my api key using https://medium.com/#amitasaurus/encrypting-decrypting-a-string-with-aes-js-1d9efa4d66d7 like below
var api_key = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
var d = new Date();
var n = d.getTime();
var final_key = api_key+'/'+n;
var encrypted = CryptoJS.AES.encrypt('encryption', final_key);
var encrypted_key = encrypted.toString();
and passed the encrypted key to the server side. I used
<?php
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
$encrypted = base64_decode('U2FsdGVkX19gHSzwsrc5H9K6rqDYr2E8oYoVNSp8INU=');
$decrypt_string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
echo $decrypt_string;
?>
for decrypting the encrypted string. When i print decrypted string , it is like this ���9Һ��دa<��5*| աT�;��놻��V�[�}��ID-�}��硵�
Any suggestions to print as decoded string?
mcryptdefaults to zero padding. That means that, no matter what kind of ciphertext and key combination you are using, that the unpadding will not fail. Instead, it just returns invalid, randomized plaintext.
CryptoJS by default uses OpenSSL key derivation from a given password. Your decryption will return randomized plaintext as long as you cannot mimic the final AES key value that is generated by CryptoJS.
Modern modes such as GCM include an authentication tag with the ciphertext so that the validity of the ciphertext / key combination is ensured, or a verification error will be generated. Note that CBC mode is absolutely not secure when directly used for transport mode security.
Related
I have an application running on php which have some values encrypted using openssl encrption by using the code below
<?php
define('OSSLENCKEY','14E2E2D1582A36172AE401CB826003C1');
define('OSSLIVKEY', '747E314D23DBC624E971EE59A0BA6D28');
function encryptString($data) {
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_encrypt($data, $encrypt_method, $key, 0, $iv);
$output = base64_encode($output);
return $output;
}
function decryptString($data){
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', OSSLENCKEY);
$iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
$output = openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv);
return $output;
}
echo encryptString("Hello World");
echo "<br>";
echo decryptString("MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09");
?>
I have another endpoint which runs on nodejs where I need to decrypt and encrypt values based on the above php encrypt/decrypt rule.
I have searched but could'nt find a solution for this.
I tried with the library crypto But ends up with errors Reference
My nodejs code which I have tried is given below
message = 'MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09';
const cypher = Buffer.from(message, "base64");
const key = crypto.createHash('sha256').update('14E2E2D1582A36172AE401CB826003C1');//.digest('hex');
// $iv = substr(hash('sha256', '747E314D23DBC624E971EE59A0BA6D28'), 0, 16); from php returns '0ed9c2aa27a31693' need nodejs equivalent
const iv = '0ed9c2aa27a31693';
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
console.log( decipher.update(contents) + decipher.final());
Someone please help me to find a nodejs code for openssl encryption and decyption
Thanks in advance
There are the following problems in the code:
The key is returned hex encoded in the PHP code, so in the NodeJS code for AES-256 only the first 32 bytes must be considered for the key (PHP does this automatically).
The PHP code Base64 encodes the ciphertext implicitly, so because of the explicit Base64 encoding the ciphertext is Base64 encoded twice (which is unnecessary). Therefore, a double Base64 encoding is necessary in the NodeJS code as well.
Also, note that using a static IV is insecure (but you are probably only doing this for testing purposes).
The following NodeJS code produces the same ciphertext as the PHP code:
const crypto = require('crypto');
const plain = 'Hello World';
const hashKey = crypto.createHash('sha256');
hashKey.update('14E2E2D1582A36172AE401CB826003C1');
const key = hashKey.digest('hex').substring(0, 32);
const hashIv = crypto.createHash('sha256');
hashIv.update('747E314D23DBC624E971EE59A0BA6D28');
const iv = hashIv.digest('hex').substring(0, 16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var encrypted = cipher.update(plain, 'utf-8', 'base64');
encrypted += cipher.final('base64');
encrypted = Buffer.from(encrypted, 'utf-8').toString('base64');
console.log(encrypted); // MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09
encrypted = Buffer.from(encrypted, 'base64').toString('utf-8');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var decrypted = decipher.update(encrypted, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
console.log(decrypted); // Hello World
EDIT:
As mentioned in the comments, PHP's hash() method returns the hash as a hexadecimal string by default (unless the third parameter is explicitly set to true, which is not the case in the reference code). This doubles the length, because in this encoding each byte of the hash is represented by two hex digits (hexits), i.e. 2 bytes.
Therefore it is necessary to shorten the key in the NodeJS code (see the first point of my original answer). This shortening is not necessary in the PHP code, since PHP does this implicitly (which is actually a design flaw, since this way the user does not notice a possible issue with the key).
The use of the hex string has two disadvantages:
With a hex encoded string, each byte consists of 16 possible values (0-15), as opposed to 256 possible values of a byte (0-255). This reduces the security from 256 bit to 128 bit (which is arithmetically equivalent to AES-128), see here.
Depending on the platform, the hexits a-f can be represented as lowercase or uppercase letters, which can result in different keys and IVs (without explicit agreement on one of the two cases).
For these reasons it is more secure and robust to use the raw binary data of the hash instead of the hex encoded strings. If you want to do this, then the following changes are necessary.
In the PHP code:
$key = hash('sha256', OSSLENCKEY, true);
$iv = substr(hash('sha256', OSSLIVKEY, true), 0, 16);
in the NodeJS code:
const key = hashKey.digest();
const iv = hashIv.digest().slice(0, 16)
Note, however, that this version is not compatible with the old one, i.e. encryptions before this change cannot be decrypted after the change. So the old data would have to be migrated.
I'm trying to create an encrypted string according to one sample code which is given in PHP, my main problem is Node.js crypto module doesn't accept keys with more than 32 bytes length but PHP openssl_encrypt does, it seems that's why I'm getting Invalid key size error.
here is my js code:
let iv = sha1(await HelpersService.makeRandomNumber(null, null, 16));
iv = iv.substr(0, 16);
const text = bundledData;
const password = sha1(this.credentials.appSecret);
let salt = sha1(await HelpersService.makeRandomNumber(null, null, 4));
salt = salt.substr(0, 4);
const key = crypto.createHash('sha256').update(password + salt).digest('hex');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encoded = cipher.update(text, 'utf8', 'hex');
encoded += cipher.final('hex');
and this is the PHP sample:
function generateCashOutAPIHashKey($app_secret ){
//remove plus(+) sign from gsm number.
$data = 'text';
$iv = substr(sha1(mt_rand()), 0, 16);
$password = sha1($app_secret);
$salt = substr(sha1(mt_rand()), 0, 4);
$saltWithPassword = hash('sha256', $password . $salt);
$encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv );
return $encrypted;
}
The key generated in the PHP and NodeJS code has a length of 64 bytes. The used encryption AES-256 needs a 32 bytes key. In the PHP code, openssl_encrypt() implicitly shortens the 64 bytes key to 32 bytes. In the NodeJS code this has to be done explicitly:
const key = crypto.createHash('sha256').update(password + salt).digest('hex').substr(0, 32);
Also, openssl_encrypt() returns the ciphertext Base64 encoded by default. In the NodeJS code the result is returned hex encoded. Here you have to change the output encoding from 'hex' to 'base64' in the update() and final() call:
let encoded = cipher.update(text, 'utf8', 'base64');
encoded += cipher.final('base64');
Please note that the PHP reference code has a number of vulnerabilities:
mt_rand() is not a cryptographically secure pseudorandom number generator (CSPRNG). PHP provides cryptographically secure methods for deriving a random IV / salt, e.g. random_bytes() or random_int().
The key is inferred using SHA-256. It is more secure to use a reliable key derivation function such as PBKDF2. A 4 bytes salt is generally too small.
SHA1 is considered insecure in most contexts and must no longer be used.
Anyone have a problem with the following ?
crypto.createHash('sha256').update(password +
salt).digest('hex').substr(0, 32);
I understand the intent of needing a 32 byte value produced by sha256 but if you hex encode and then take the first 32 characters of the string you have SIGNIFICANTLY reduced the value of this. You now have a string 32 characters long that has only 16 possible values (0-9a-f) for each character. You really want a 32 byte value that has 256 possible values for each byte. You have changed your key security of your encryption key from the intended 256^32 (1.1E77) to 16^32 (3.4E38).
crypto.createHash('sha256').update('').digest('hex');
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
crypto.createHash('sha256').update('').digest('hex').substr(0,32);
'e3b0c44298fc1c149afbf4c8996fb924'
Find a way to use the byte values produced by SHA256.
I have a private key encrypted script with a password and here is its output:
{
"iv":"Ra6kDXvh2DBiZ0r37pNuzg==",
"v":1,
"iter":10000,
"ks":256,
"ts":64,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"pNN1xP7SZks=",
"ct":"Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64="
}
I dont know exactly whats name of this type of encryption and I want to know how to decrypt it using a password and PHP.
According to my research, it can be decrypted by the openssl_decrypt function.
But I couldn't find how to use my parameters in this function.
For example, I have a key called salt in the json that I have and I don't know what to do with it.
Also, in the openssl_decrypt function, there is an input argument called tag. I don't know the json key that it belongs to.
This is a sample of the code I'm using:
$ct = 'Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64=';
$method = 'aes-256-ccm';
$password = 'Qw370207610';
$options = 0;
$iv = base64_decode('Ra6kDXvh2DBiZ0r37pNuzg==');
$output = openssl_decrypt($ct, $method, $password, $options, $iv);
And I received this error:
openssl_decrypt(): Setting of IV length for AEAD mode failed
UPDATE:
So I have gethered that for producing the third parameter (key) that is used in openssl_decrypt, I should act like this:
$ks = 256;
$key_length = $ks/8;
$password = 'Qw370207610';
$salt_base64 = 'pNN1xP7SZks=';
$salt = base64_decode($salt_base64);
$iterations = 10000;
$digest_algorithm = 'sha256';
$key = openssl_pbkdf2 ( $password , $salt , $key_length , $iterations , $digest_algorithm );
And then it can be decrypted in this way:
$ct_base64 = 'Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64=';
$ct = base64_decode($ct_base64);
$ts = 64;
$tag_length = $ts/8;
$tag = substr($ct,-$tag_length);
$ccm = substr($ct,0,-$tag_length);
$method = 'aes-256-ccm';
$options = OPENSSL_RAW_DATA;
$iv_base64 = 'Ra6kDXvh2DBiZ0r37pNuzg==';
$iv = base64_decode($iv_base64); // 16 bytes length
$output = openssl_decrypt($ccm, $method, $key, $options, $iv, $tag);
However in PHP, for decrypting aes-ccm, there is just openssl, and they haven't offered another library.
On the other hand, these functions don't accept an IV (initialization vector) larger than 12 bytes. Because IV of my encrypted message is 16 bytes and it can not be decrypted in PHP at all !!
Have PHP developers not thought about this?
I have never had such problems in nodejs, but I always face some kind of restriction in PHP.
No, you have a password encrypted script, where a secret key is generated from that password. And it was generated using SJCL or a compatible library, demonstration here.
The salt and iteration count iter are input to the PBKDF2 function, which generates the AES key.
I hope you can progress using this information, because SO is not a code delivery service. Fortunately I know that OpenSSL contains PBKDF2, but I'm not sure if it is exposed to you.
please try this : From mcrypt_decrypt to openssl_decrypt
and for this you don't need the value of $iv if you give your data raw-value it will convert into base64 and other operation will be done by your method and algorithm. I think this might help you.
I'm trying to encrypt (create a hash) of a string in PHP and duplicate the behaviour in NodeJS. I'm not worried about security on the strings. Both strings should match.
This is the PHP code that I have:
$key = 'supersecretkey';
$plaintext = "example string to encrypt";
$cipher = "rc4-hmac-md5";
$encryptedText = openssl_encrypt($plaintext, $cipher, $key);
echo $encryptedText . "\n";
This is my output
> php crypto.php
cRRDH1KSTZmbWLx+h0Q/l17jfDeAsQb/GA==
This is the NodeJS that I have tried:
var crypto = require('crypto'),
algorithm = 'rc4-hmac-md5',
password = 'supersecretkey';
function encrypt(text){
var cipher = crypto.createCipher(algorithm,password)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
var hw = encrypt("example string to encrypt")
// outputs hello world
console.log(hw);
And this is the output:
> node crypto.js
7ddd856a0227489b5cabda26e82eb99fb0c2ec9b6dfb477d43
Why are these values different and how do I get them the same?
While I'm unfamiliar with both of these specific APIs, I do notice you are using a an initialization vector with openssl, but not with node.js. I would try using the Node.js crypto.createCiperhiv form (https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options) and be sure to use the same exact initialization vector bytes for both php and node.js (you should be able to just create some arbitrary ascii string, e.g. "my initialization vector", of the appropriate size).
(an initialization vector is another configuration input to a crypytographic cipher that acts as a seed that introduces additional randomness/variability)
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;
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.
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