Problems when using AES crypto between Node and CryptoJS in browser - javascript

I want encrypt a string with Node, and decrypt the string with CryptoJS in browser.
Encrypt:
var crypto = require('crypto');
function encrypt(txt, cryptkey) {
var cipher = crypto.createCipher('aes-256-cbc', cryptkey);
var crypted = cipher.update(txt, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
}
encrypt('1', 'key'); // 83684beb6c8cf063caf45cb7fad04a50
Include:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
Decrypt:
var decrypted = CryptoJS.AES.decrypt('83684beb6c8cf063caf45cb7fad04a50', 'key');
console.log(decrypted.toString(CryptoJS.enc.Utf8)); // empty string
The actual result is empty string.
What is the right way to decrypt data from node?

CryptoJS supports the same password-based encryption mode that the crypto module in node.js supports which is implemented as the equivalent to EVP_BytesToKey. CryptoJS generates a random salt by default, but node.js doesn't and uses an empty salt. An empty salt is bad and should not be used. Also, it's not secure to derive a key from a password with this method. One needs to use PBKDF2 (supported by CryptoJS and node.js) or similar with a lot of iterations and a random salt.
var ctHex = '83684beb6c8cf063caf45cb7fad04a50';
var ct = CryptoJS.enc.Hex.parse(ctHex);
var salt = CryptoJS.lib.WordArray.create(0); // empty array
var decrypted = CryptoJS.AES.decrypt({ciphertext: ct, salt: salt}, 'key');
document.querySelector("#dec").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
Expected: "1"<br/>Got: "<span id="dec"></span>"
You said that this will be done over HTTP.
If you want to use transport security without user intervention, then this is completely insecure, because the key needs to be transmitted alongside of the ciphertext which makes this at best obfuscation.
If the user and the server both know the password before communication, then this is still insufficient, because the key derivation that both CryptoJS and node.js provide is insufficient and something like PBKDF2 must be used. MD5 is easily brute-forceable.
You would need to use asymmetric cryptography to protect this communication against a passive attacker (one that cannot inject arbitrary packets into the stream between server and client). I suggest that you generate an RSA key pair and sent the public key to the client so that the client can encrypt a message to the server. You can use forge for that.
Encryption would look like this:
var salt = CryptoJS.lib.WordArray.create(0); // empty array
var params = CryptoJS.kdf.OpenSSL.execute('key', 256/32, 128/32, salt);
var pt = '1';
var encrypted = CryptoJS.AES.encrypt(pt, params.key, {iv: params.iv});
document.querySelector("#enc").innerHTML = encrypted.ciphertext.toString();
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
Expected: "83684beb6c8cf063caf45cb7fad04a50"<br/>Got: "<span id="enc"></span>"

Related

Not able to login after encrypting the username

I have 2 fields username and password, while registering the user I am encrypting the username with AES and store into database, now I am unable to login because the plain text is not matching with encrypted data.
How can I resolve this problem?
P.s.-
1)I am using Crypto.js library
2)While login I can't again encrypt the username again as it is giving different encrypted data.
What you are trying to achieve is usually done with a hash. A hash function of the username will always return the same hash value.
If there is a specific reason for encrypting the username rather than hashing (which you haven’t explained though), then you will need to decrypt it in order to compare the values or use an encryption mode that is deterministic and does not involve a random initialization vector. See this question and its answers for details.
On your login logic, you should use the same AES encryption to encrypt the user 's input username during their login.
If the username is actually in your database, using that encrypted username should be able to query/locate the username in your database.
Below are for your reference on how the encrypt and decrypt works (copied from HERE):
var CryptoJS = require("crypto-js");
// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();
// Decrypt
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);
console.log(originalText); // 'my message'

Encrypt in nodeJS and Decrypt in Javascript

I am encrypting text in node JS by using node-RSA and passing it to client(javascript), in which JSEncrypt library is using,but all the time the decrypted message is coming null. Public key and private Key is developing on nodeJS server, encrypting with Public key and decrypting on javascript side with Private Key .
This is not happening right!!!!
Can anyone tell which library i should use in javascript to decrypt the message coming from nodejs(using Node-RSA).OR any other IDEA!!
We are already using HTTPS but our use case is such that we have a broker between it.. and its not trusted broker, and we are forced to use it.. so we would like to use encryption decryption.. Although we have trusted people in our client side, so we are decrypting at client side.
I used CryptoBrowserify to encrypt at javascript (client side)
import CryptoBrowserify from 'crypto-browserify';
public encryptStringWithRsaPublicKey(data: string, publicKey: string): string {
var encrypted = CryptoBrowserify.publicEncrypt( publicKey,new Buffer(data));
return encrypted.toString('Base64');
}
And crypto to dedcrypt at Nodejs
decrypt = function(privateKey, data) {
var crypto = require('crypto');
var buffer = new Buffer(data, 'base64');
var decrypted = crypto.privateDecrypt(privateKey, buffer);
return decrypted.toString('utf8')
};
Nodejs has its builtin cryto library,it is optimized and tested, recommend to use that: https://nodejs.org/api/crypto.html

How to decipher string in node js which is encrypted in crypto js in javascript

My client side code:
data.username = CryptoJS.AES.encrypt(user.username, "password");
data.password = CryptoJS.AES.encrypt(user.password, "password");
Then I am sending 'data' to server which is express.js
var user = req.body;
var decipher = crypto.createDecipher('aes256', "password");
var decrypted = decipher.update(user.username, 'hex', 'utf-8');
decrypted += decipher.final('utf-8');
I am getting this error:
Error: DecipherInit error
at new Decipher (crypto.js:368:17)
at Object.Decipher (crypto.js:365:12)
CryptoJS' encrypt function with a password uses the same EVP_BytesToKey function node.js' createCipher, with the important difference that CryptoJS uses a random salt to derive whereas node does not (emphasis mine):
Note: createCipher derives keys with the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
Either you directly use CryptoJS in node which is possible, because CryptoJS doesn't have any dependencies, or you do the key derivation yourself on both ends and use crypto.createCipheriv. If you do the former, you would have to additionally pass the salts of the username and password encryptions to node.
Note that data.username is the CryptoJS cipherParams object which contains the salt and the IV, but when you convert this to string with data.username.toString(), the salt is not included anymore, but the IV is. This is not the data that you would put into the node.js functions. Send data.username.ciphertext instead.

How can I get a certificate to verify my RSA digital signature using jsrsasign?

I am using jsrsasign and I have an RSA public and private key that I generated earlier. I used the following code to generate a signature.
var key = new RSAKey();
key.readPrivateKeyFromPEMString($("#private")[0].value); // "private" is a textarea containing the PEM encoded private key.
var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
sig.initSign(key);
sig.updateString('message');
var sigValueHex = sig.sign();
This produced the expected hex signature. The documentation for jsrsasign provides the following code to verify signatures.
var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
sig.initVerifyByCertificatePEM("-----BEGIN CERTIFICATE-----(snip)");
sig.updateString('message');
var isValid = sig.verify(sigValueHex);
My problem is that I do not have a certificate to verify with. sig.sign() only gives the signature, no certificate. There is also sig.initVerifyByPublicKey(RSAKey) which seemed promising at first. However, RSAKey only has ways to get the private key, not public key.
So, my question is how can I get this certificate? And if I can't, how can I get an RSAKey object from my public key? And if I can't do that, is there a better way to do digital signatures with javascript?
I am using jsrsasign and jQuery. http://kjur.github.io/jsrsasign/
If there is no way to do this with the methods I am using, is there a better way? My main goal here is to be able to send a message to a server and be sure that the message wasn't tampered with and that it came from the correct place. SSL is not an option here. The message does not need to be encrypted, only protected.
You can create the public key with KEYUTIL.getKey(param), where "param" can be a string with either your certificate or your public key.
Here are the docs for that method
Example:
var publicKey = KEYUTIL.getKey(publicKeyString);
var sig = new r.Signature({"alg": "SHA256withRSA", "prov": "cryptojs/jsrsa"});
sig.initVerifyByPublicKey(publicKey);
sig.updateString(message);
var isValid = sig.verify(signature);
Hope that helps.
You could generate an X.509 self-signed key/certificate pair by using openssl.
openssl req -newkey rsa:4096 -x509 -nodes -keyout key.pem -out certificate.pem
Then upload certificate.pem to the server, and use key.pem to sign messages on the client.
The message and signature can be verified on the server.
Here is the sample code of using jsrsasign:
const jsrsasign = require("jsrsasign");
const fs = require('fs');
// sign
let key = fs.readFileSync("./key.pem", "utf-8");
let sig = new jsrsasign.KJUR.crypto.Signature({"alg": "SHA1withRSA"});
sig.init(key);
sig.updateString("message");
const signature = sig.sign();
// verify
let certificate = fs.readFileSync("./certificate.pem", "utf-8");
let ver = new jsrsasign.KJUR.crypto.Signature({"alg": "SHA1withRSA"});
ver.init(certificate);
ver.updateString("message");
ver.verify(signature); // return true
For your reference.

Problems with AES in crypto-js and pycrypto

I try to implement a communication between
crypto-js (a javascript crypto library)
and pycrypto (a python crypto library)
On the python server side I encrypt a string with an iv and a passphrase and send the iv with the encrypted text base64 encoded to the javascript client side. Then I want to decrypt the string with the passphrase the user can enter.
python - server
from Crypto.Cipher import AES
from Crypto import Random
iv = Random.get_random_bytes(16)
key = "1234567812345678"
aes = AES.new(key, AES.MODE_CFB, iv)
encrypted_text = base64.b64encode(aes.encrypt("this is a test.."))
iv = base64.b64encode(iv)
# send iv, encrypted_text to client
javascript - client
// <script type="text/javascript"
src="http://crypto-js.googlecode.com/files/2.5.3-crypto-sha1-hmac-pbkdf2-blockmodes-aes.js">
</script>
// text, and iv is base64encoded from the python script
// key is a string from an <input type='text'>
decrypted = Crypto.AES.decrypt(text, key, {iv: iv, mode: new Crypto.mode.CFB});
With this example I get a javascript error
Uncaught URIError: URI malformed
But this is just one example - I tried every constellation of base64 encodings/decodings I could think of. I also tried to changed the Mode. But these are all random tests and I want to understand what I really have to do.
What encoding does the crypt-js want?
Which mode should I chose?
Is there something I should change on the python server side?
what is about padding? Could there be the fault?
any other javascript libraries you can recommend?
thank you very much and kind reagards,
samuirai
Before You encode to base64 You must sum iv and encrypted_text:
encrypted_text = base64.b64encode(iv + aes.encrypt("this is a test.."))
From the official documentation (https://www.dlitz.net/software/pycrypto/doc/) :
As an example, encryption can be done as follows:
from Crypto.Cipher import AES
from Crypto import Random
key = b'Sixteen byte key'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')

Categories

Resources