encryption in PHP and decryption in node.js - javascript

I've tried suggestions and tricks by reading various answers on stack overflow but they don't seem to be sufficient or maybe there's something basic i'm missing. Basically i'm trying to encrypt a value in php and pass it to the webpage from where it's read by JavaScript and send to node server for processing. But i'm unable to get the same value back on node server which i encrypted in php.
Below is the php code and php version is 5.5.12 running on windows 7 64 bit:-
function encrypt($string){
$key = hash("SHA256", '1d417e2ffb2a00a3', true);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $blockSize - (strlen($string) % $blockSize);
$string .= str_repeat(chr($padding), $padding);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,$string, MCRYPT_MODE_CBC, $iv);
$result['cipher'] = base64_encode($ciphertext);
$result['iv'] = base64_encode($iv);
return $result;
}
My node.js version is 0.10.31 running on windows 7 64 bit and code is below:-
var express = require("express");
var Server = require("http").Server;
var cookie = require("cookie");
var app = express();
var server = Server(app);
var sio = require("socket.io")(server);
var crypto = require('crypto');
sio.sockets.on("connection", function(socket) {
try{
socket.on('incoming_data', function(data){
var txt = new Buffer(data.encrypted_text,'base64');
var key = new Buffer('1d417e2ffb2a00a3','utf8');
var iv = new Buffer(data.iv,'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc',key,iv);
var chunks = [];
chunks.push(decipher.update(txt,'hex','binary'));
chunks.push(decipher.final('binary'));
var fuid = chunks.join('');
console.log(fuid);
});
}catch(e){
console.log("err:-"+e);
console.log(e);
}
});// on connection ends
server.listen(9267, function(){
console.log('Node server listening on *:9267');
});
process.on('uncaughtException', function(err) {
console.log("FATAL: "+new Date().getTime()+": "+err);
});
The error i get from printing fuid in nodejs console is as below:-
FATAL: 1414483246855: TypeError: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
I am looking for solutions to the following in the answers:-
1) Problems with my code and what needs to be fixed to get back the same value on node server as a string.
2) Would like to concatenate the encrypted text and the iv and send them both as a single base64 encoded string to the server. So would like the code that will be needed to separate them back on node server ready to be passed to the crypto module.
3) This code seems vulnerable to oracle padding attacks. It will be great if you can suggest how can i make it secure.
Thanks

The problem might be with your encoding:
chunks.push(decipher.update(txt,'hex','binary'));
hex looks strange, since your inputs are buffers, not strings.
The following quick test works (also answers 2.):
php:
$key = 'secretsecretsecr';
$string = 'attack at dawn';
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $blockSize - (strlen($string) % $blockSize);
$string .= str_repeat(chr($padding), $padding);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,$string, MCRYPT_MODE_CBC, $iv);
$packet = chr($iv_size) . $iv . $ciphertext;
print base64_encode($packet);
node:
var crypto = require('crypto');
var key = 'secretsecretsecr';
var data = 'paste what the php code printed';
var txt = new Buffer(data,'base64');
var iv_size = txt[0];
var iv = txt.slice(1, iv_size + 1);
var ct = txt.slice(iv_size + 1);
var decipher = crypto.createDecipheriv('aes-128-cbc',key,iv);
var chunks = [];
chunks.push(decipher.update(ct));
chunks.push(decipher.final());
console.log(chunks.join(''));
The passes the iv size along, you can also simply hardcode it on the node side.
Regarding 3), I am by no means an expert, from what I've read the workaround is to sign your encrypted packets with HMAC to make sure they're coming from your application and not from the "oracle" (http://www.ietf.org/id/draft-mcgrew-aead-aes-cbc-hmac-sha2-05.txt).

Related

How do I replicate this PHP hashing implementation in Node.js?

I'm attempting to recreate a password hashing implementation in node.js (LTS latest--14.x) that was originally written in PHP (7.2). I believe the node.js implementation that I've written should do the exact same thing; however, the node.js implementation differs after the first pass of the hash in the loop. What am I missing here?
PHP implementation (I cannot change this since it's part of a web framework and existing authentication relies on the hashing mechanism staying the same):
$algo = "sha512";
$salt = "someSalt";
$password = 'somePassword';
$count = 32768;
$hash = hash($algo, $salt . $password, TRUE);
// $hash is the same as in the corresponding line in the node.js implementation
do {
$hash = hash($algo, $hash . $password, TRUE);
// $hash differs from the node.js implementation after the first pass here... why?
} while (--$count);
Node.js implementation:
const crypto = require('crypto');
const algorithm = 'sha512';
const salt = 'someSalt';
const password = 'somePassword';
let count = 32768;
let hash = crypto
.createHash(algorithm)
.update(salt + password)
.digest('binary');
// hash is the same as in the PHP implementation here
do {
hash = crypto.createHash(algorithm).update(hash + password).digest('binary');
// hash differs between the two implementations after the first pass here... why?
} while (--count);
EDIT: Updated to show the original Node.js implementation where I did not stringify data being passed to update().
Expanding on what my comment was I think the following might work.
NODE:
const crypto = require('crypto');
const algorithm = 'sha512';
const salt = 'someSalt';
const password = 'somePassword';
let count = 32768;
let hash = crypto
.createHash(algorithm)
.update(String(salt) + String(password))
.digest('hex');
// hash is the same as in the PHP implementation here
do {
hash = crypto.createHash(algorithm).update(String(hash) + String(password)).digest('hex');
// hash differs between the two implementations after the first pass here... why?
} while (--count);
console.log(hash);
PHP code:
$algo = "sha512";
$salt = "someSalt";
$password = 'somePassword';
$count = 32768;
$hash = bin2hex(hash($algo, $salt . $password, TRUE));
// $hash is the same as in the corresponding line in the node.js implementation
do {
$hash = bin2hex(hash($algo, $hash . $password, TRUE));
// $hash differs from the node.js implementation after the first pass here... why?
} while (--$count);
var_dump($hash);
Confirmed my findings with the output from NODE:
node nodeTest.js
df8202221e5cbff38c16a33945efa8dcb44d0e7267cdf1514cefffb3df321f69ad1d9b01cfb6360391f1de4791e26a179fd165248b4b75699cb2d3395c971351
PHP output:
php test.php
string(128) "df8202221e5cbff38c16a33945efa8dcb44d0e7267cdf1514cefffb3df321f69ad1d9b01cfb6360391f1de4791e26a179fd165248b4b75699cb2d3395c971351"
I had to pass 'binary' to update() while hashing in the loop, since the data being provided is in binary.
// The data being passed to update() here is string (update defaults to utf-8, I believe).
let hash = crypto
.createHash(algorithm)
.update(salt + password)
.digest('binary');
do {
// The data being passed to update() is binary.
hash = crypto.createHash(algorithm).update(hash + password, 'binary').digest('binary');
} while (--count);
Credit: #Topaco for the finding the solution to my problem!

nwjs-nodejs- encrypt and decrypt img file (jpg) and use the decrypted data to an img element

I developed a desktop application with nwjs (nodejs / html / css ), now i want to put the app for the production so i need to prevent stealing my assets (my images are very valuables), nwjs provide a tool to compile (encrypt) the js files but not the asset so i thought about encrypting my assets with a js then encrypt the js with nwjs tool, i am not very familiare with node modules and dealing with files in js so i struggled with this task !
This code is what i tried to do but i did not reach my goal ?
encrypt
let crypto;
try {
crypto = require('crypto');
} catch (err) {
console.log('crypto support is disabled!');
}
var algorithm = 'aes-256-ctr',
password = 'secret';
var fs = require('fs');
var r;
// encrypt content
var encrypt = crypto.createCipher(algorithm, password);
// decrypt content
var decrypt = crypto.createDecipher(algorithm, password);
// write file
var w;
var path = require('path');
var dirPath = './Files/'; //directory path
var fileType = '.' + 'jpg'; //file extension
var files = [];
fs2.readdir(dirPath, function (err, list) {
if (err) throw err;
for (var i = 0; i < list.length; i++) {
if (path.extname(list[i]) === fileType) {
r = fs.createReadStream('Files/' + list[i]);
w = fs.createWriteStream('encFiles/' + list[i].replace('.jpg', ''));
console.log(list[i]); //print the file
// start pipe
r.pipe(encrypt).pipe(w);
}
}
});
decrypt
'use strict';
var crypt = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'secret';
var fs = require('fs');
var zlib = require('zlib');
var toArray = require('stream-to-array');
// input file
var r = fs.createReadStream('./encFiles/an example file');
// decrypt content
var decrypt = crypt.createDecipher(algorithm, password);
//b64 module so i could put the base64 data to img html element
const B64 = require('b64');
const encoder = new B64.Encoder();
// start pipe
var stream = r.pipe(decrypt);
var d = stream.pipe(encoder);
d.pipe(process.stdout);
var data;
toArray(stream, function(err, arr) {
console.log(err,arr);
data = Buffer.concat(arr);
console.log(data);
});
console.log(data);
thank you for giving me comments on the code or other IDEAS
So the solution was so simple, I used the nw-js code protection feature to protected the script in which I decrypt the assets (images in my case) (this script contains the key of decryption), so you could implement the encryption/decryption with any method you want and protect the decryption script which is going to be shipped with you product (in my case the desktop app).
Since you are building a desktop app, you may want to look at cryptojs for this. I would still strongly suggest that you watermark images and hide them when your application looses focus. Even with that, screenshots can be taken without leaving your application.

Best approch to decode the PKCS12 file and get the encrypted private key from it using JavaScript

Please suggest any idea to decode the PKCS12 file and get the encrypted private key from it using JavaScript. I know that it can be done very easily using Java Keytool command and Java Security package. But I want it to be done by Java Script. Bellow is my actual requirement.
I have a ".p12" extention file which is one of the formats of pkcs12.
It should be decoded first and need to trace out the decoded file where exactly the encrypted Private key is placed.
Need to get that encrypted Private key and Decrypt it and send it to the receiver.
And all this Should be done only in JAVASCRIPT.
I think this might be what you are looking for:
"A native implementation of TLS (and various other cryptographic tools) in JavaScript."
https://github.com/digitalbazaar/forge#pkcs12
It sounds like this example is close:
// decode p12 from base64
var p12Der = forge.util.decode64(p12b64);
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(p12Der);
// decrypt p12
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
// look at pkcs12.safeContents
// generate p12, base64 encode
var p12Asn1 = forge.pkcs12.toPkcs12Asn1(
privateKey, certificateChain, 'password');
var p12Der = forge.asn1.ToDer(p12Asn1).getBytes();
var p12b64 = forge.util.encode64(p12Der);
Rgds....Hoonto/Matt
This will work Perfectly
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(buffer);
// decrypt p12 using the password 'password'
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);
// get bags by type
var certBags = p12.getBags({bagType: forge.pki.oids.certBag});
var pkeyBags = p12.getBags({bagType: forge.pki.oids.pkcs8ShroudedKeyBag});
// fetching certBag
var certBag = certBags[forge.pki.oids.certBag][0];
// fetching keyBag
var keybag = pkeyBags[forge.pki.oids.pkcs8ShroudedKeyBag][0];
// generate pem from private key
var privateKeyPem = forge.pki.privateKeyToPem(keybag.key);
// generate pem from cert
var certificate = forge.pki.certificateToPem(certBag.cert);
Thanks to the examples from #Ujjawal and #hoonto I was able to get the following working well.
const decodePKCS12 = (
file // Dom File object
) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = evt => {
try {
const binary = evt && evt.target ? evt.target.result : null
if (!binary) {
reject(new Error('No file data'))
}
const p12Asn1 = asn1.fromDer(binary)
const p12 = pkcs12.pkcs12FromAsn1(p12Asn1)
const certBags = p12.getBags({bagType: pki.oids.certBag})
const pkeyBags = p12.getBags({bagType: pki.oids.pkcs8ShroudedKeyBag})
const certBag = certBags[pki.oids.certBag][0]
const keybag = pkeyBags[pki.oids.pkcs8ShroudedKeyBag][0]
const certificate = pki.certificateToPem(certBag.cert)
const privateKey = pki.privateKeyToPem(keybag.key)
resolve({certificate, privateKey})
} catch (e) {
reject(e)
}
}
reader.onerror = reject
reader.readAsBinaryString(file)
})
}

Encrypt with Cryptico.js, Decrypt with OpenSSL

I am creating a public/private key on the server, sending the key to the JavaScript client where it encrypts a users password. The client sends the password to the server, and the server uses the private key to decrypt it, but the password is coming back null. I have verified all values supporting the situation are correct, so it's something with the encryption/decryption specifically. Where am I going wrong?
Possibly, is cryptico.js not compatible with php openssl?
Library Info:
https://github.com/wwwtyro/cryptico
http://www.php.net/manual/en/function.openssl-pkey-new.php
Here are relevant code snippets:
PHP - create public/private key
$config = array(
"digest_alg" => "sha512",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
// Create the private and public key
$res = openssl_pkey_new($config);
// Extract the private key from $res to $privateKey
openssl_pkey_export($res, $privateKey);
// Extract the public key from $res to $publicKey
$publicKey = openssl_pkey_get_details($res);
$publicKey = $publicKey["key"];
JavaScript - Client encrypts data with public key.
var xhr = new XMLHttpRequest();
var data = new FormData();
xhr.open('POST', '/signUp2.php');
data.append('user', User);
var encryptedPassword = cryptico.encrypt(password, localStorage["publicKey"]);
data.append('password', encryptedPassword.cipher);
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4 && xhr.status == 200)
{
var jsonArray = JSON.parse(xhr.responseText);
if(jsonArray[0] == "0")
{
alert("Account created. You may now sign in.");
}
else
alert("Error Code: " + jsonArray[0]);
}
}
xhr.send(data);
PHP - Server recieves encrypted password and attemps to decrypt unsuccessfully
openssl_private_decrypt($encryptedPassword, $decryptedPassword, $row[1]);
cryptico.js could work with openssl, but we have to modify it a bit.
it don't directly recognize the public key in pem format (which openssl use). we have to extract the 'n' and 'e' part of a public key in php side:
$key = openssl_pkey_new(array(
'private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'digest_alg' => 'sha256'
));
$detail = openssl_pkey_get_details($key);
$n = base64_encode($detail['rsa']['n']);
$e = bin2hex($detail['rsa']['e']);
also, cryptico.js hardcoded the 'e' part of a public key (see definition of publicKeyFromString in api.js), so we need to fix this:
my.publicKeyFromString = function(string)
{
var tokens = string.split("|");
var N = my.b64to16(tokens[0]);
var E = tokens.length > 1 ? tokens[1] : "03";
var rsa = new RSAKey();
rsa.setPublic(N, E);
return rsa
}
now we are able to encrypt strings:
var publicKey = "{$n}|{$e}",
encrypted = cryptico.encrypt("plain text", publicKey);
job is not finished yet. the result of cryptico.encrypt is NOT simply encrypted by RSA. indeed, it was combined of two parts: an aes key encrypted by RSA, and the cipher of the plain text encrypted with that aes key by AES. if we only need RSA, we could modify my.encrypt:
my.encrypt = function(plaintext, publickeystring, signingkey)
{
var cipherblock = "";
try
{
var publickey = my.publicKeyFromString(publickeystring);
cipherblock += my.b16to64(publickey.encrypt(plaintext));
}
catch(err)
{
return {status: "Invalid public key"};
}
return {status: "success", cipher: cipherblock};
}
now we are able to decrypt the cipher with openssl:
$private = openssl_pkey_get_private("YOUR PRIVATE KEY STRING IN PEM");
// $encrypted is the result of cryptico.encrypt() in javascript side
openssl_private_decrypt(base64_decode($encrypted), $decrypted, $private);
// now $decrypted holds the decrypted plain text

Creating a signed S3 URL with Javascript

I am attempting to create a signed S3 URL using Javascript & NodeJS. I have used this
specification.
var crypto = require('crypto'),
date = 1331290899,
resource = '/myfile.txt',
awskey = "XXXX",
awssecret = "XXXX";
var stringToSign ='GET\n\n\n' + date + '\n\n' + resource;
var sig = encodeURIComponent(crypto.createHmac('sha1', awssecret).update(stringToSign ).digest('base64'));
var url = "https://s3-eu-west-1.amazonaws.com/mybucket" +
resource + "?AWSAccessKeyId=" + awskey + "&Expires="+ date +
"&Signature="+ sig
This creates a url similar to this:
https://s3-eu-west-1.amazonaws.com/mybucket/test.txt?AWSAccessKeyId=XXXXXX&Expires=1331290899&Signature=EciGxdQ1uOqgFDCRon4vPqTiCLc%3D
However, I receive the following error when accessing it:
SignatureDoesNotMatch
The request signature we calculated does not match the signature you provided.
Check your key and signing method.
What am I doing wrong when creating the signature?
EDIT - ATTEMPT WITH KNOX
I am now attempting to use Knox to produce a signed URL. I need to add headers with the request to force download. I have edited the following:
Added amazonHeaders: 'response-content-disposition:attachment', to client.signedUrl- http://jsfiddle.net/BpGNM/1/
Added options.amazonHeaders + '\n' + to auth.queryStringToSign - http://jsfiddle.net/6b8Tm/
The message that is now being sent to auth.hmacSha1 to create the the sig is:
'GET\n\n\n1321374212\nresponse-content-disposition:attachment\n/meshmesh-dev/test/Readme.md'
I have then tried to access my new URL with the response-content-disposition=attachment added as GET var. However, I am still receiving the same error stated above.
I would try using Knox along with Node.Js . Its known to be a great combination and also itself utilizes the Node.JS Crypto library which is kind of what you're trying to do - saving you time:)
More info here : https://github.com/LearnBoost/knox
Than, you could just do something like:
var knox = require('knox');
var s3Client = knox.createClient({
key: 'XXX',
secret: 'XXX',
bucket: 'XXX'
});
var expires = new Date();
expires.setMinutes(expires.getMinutes() + 30);
var url = s3Client.signedUrl(filename, expires);
Edit:
You could also look into Knox and just check what the signedUrl function does and implement that yourself.Than you could add to the auth.signQuery call an extra option called amazonHeaders:
Client.prototype.signedUrl = function(filename, expiration){
var epoch = Math.floor(expiration.getTime()/1000);
var signature = auth.signQuery({
amazonHeaders: 'response-content-disposition:attachment',
secret: this.secret,
date: epoch,
resource: '/' + this.bucket + url.parse(filename).pathname
});
return this.url(filename) +
'?Expires=' + epoch +
'&AWSAccessKeyId=' + this.key +
'&Signature=' + encodeURIComponent(signature);
};
Shai.
maybe one too many newlines?
var stringToSign ='GET\n\n\n' + date + '\n\n' + resource;
If its any help here is a rubbish PHP implementation which definitely works:
class myS3Helper{
public function getSignedImageLink($timeout = 1800)
{
$now = new Zend_Date(); //Gives us a time object that is set to NOW
$now->setTimezone('UTC'); //Set to UTC a-la AWS requirements
$now->addSecond($timeout);
$expirationTime = $now->getTimestamp(); //returns unix timestamp representation of the time.
$signature = urlencode(
base64_encode(
hash_hmac(
'sha1', $this->_generateStringToSign($expirationTime),
$my_aws_secretkey,
true
)
)
);
//FIXME make this less ugly when I know it works
$url = 'https://';
$url .= Zend_Service_Amazon_S3::S3_ENDPOINT; //e.g s3.amazonaws.com
$url .= $this->_getImagePath(); //e.g /mybucket/myFirstCar.jpg
$url .='?AWSAccessKeyId=' . $my_aws_key;
$url .='&Signature=' . $signature; //signature as returned by below function
$url .='&Expires=' . $expirationTime;
return $url;
}
protected function _generateStringToSign($expires)
{
$string = "GET\n"; //Methods
$string .= "\n";
$string .= "\n";
$string .= "$expires\n"; //Expires
$string .= $this->_getImagePath();
return $string;
}
}
EDIT--
Have a look at this node.js s3 upload code, (it's not mine but found it lying around on my mac - so if anyone can attribute it to someone let me know and i'll do the props). Hopefully this might help (3rd time lucky)
https://gist.github.com/1370593
My implementation using AWS-SDK and Rx.
import AWS from "aws-sdk"
import Rx from 'rx'
/*
* Credentials could be loaded from env variables
* http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
* */
const s3 = new AWS.S3({apiVersion: '2006-03-01'});
export function getS3SignedImage(objectKey) {
return Rx.Observable.create(function (observer) {
s3.getSignedUrl('getObject',{
Bucket: process.env.AWS_BUCKET,
Key: objectKey
}, (err, data) => {
if (err) {
return observer.onError(err);
}
observer.onNext(data);
observer.onCompleted();
});
});
}

Categories

Resources