Convert Java AES encryption to Javascript using the crypto library - javascript

Im trying to convert the java library - AESCrypt-Java
to javascript.
This is my implementation so far for the decrypt function. Im not able to decrypt the text. Can someone figure out where I'm going wrong?
function decrypt(password, base64text) {
key = generateKey(password);
var decodedCipherText = new Buffer(base64text, 'base64')
var iv = new Buffer(16);
iv.fill(0);
var decipher = crypto.createDecipheriv("aes-256-cbc", key, iv)
let decrypted = decipher.update(decodedCipherText, 'base64', 'utf-8');
decrypted += decipher.final('utf-8')
return decryptedBytes
}
function generateKey(password) {
return crypto.createHash('sha256').update(usr_id).digest();
}
var encryptedText = '1+2yFMDH1C/uIc1huwezbrsQ==';
var password = '8AVrWtyabQ';
decrypt(password, encryptedText)
The expected plaintext output is Wordpress.

You are making a few decisions that will adversely affect the security of your sensitive values:
You are using a static, all-zero IV. The IV must be unique and non-predictable for every message encrypted with a specific key. The IV can then be prepended to the cipher text and transmitted unprotected to the recipient, where it is sliced and used for decryption.
Your key derivation function (KDF) is weak -- SHA-256 can be cracked at 23 billion attempts per second on commodity hardware. Use a key-stretching algorithm like PBKDF2 with a high iteration count, or bcrypt or scrypt for memory hardness.
Your cipher text is not authenticated -- AES/CBC provides confidentiality, but not integrity or authentication. An interceptor can manipulate the cipher text in transmission and attempt to decrypt it. This can result in unauthorized decryption (i.e. injecting malicious plaintext into your application) or a padding oracle attack, and eventually cipher text recovery. Use an authenticated encryption (with associated data) (AE or AEAD) cipher mode to mitigate this, or add a strong HMAC construction using a separate key over the cipher text and verify prior to decryption with a constant-time equals method.
new Buffer(string, encoding) and new Buffer(size) are deprecated and Buffer.from(string, encoding) and Buffer.alloc(size) should be used instead. You create a Buffer containing the provided cipher text which is encoded in Base64. I have a feeling there is an issue occurring with your encoding (you don't provide any example output for us to see). Here is an example of encrypting and decrypting with Buffer objects.
function encrypt(buffer){
var cipher = crypto.createCipher(algorithm,password)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
function decrypt(buffer){
var decipher = crypto.createDecipher(algorithm,password)
var dec = Buffer.concat([decipher.update(buffer) , decipher.final()]);
return dec;
}
var hw = encrypt(new Buffer("hello world", "utf8"))
// outputs hello world
console.log(decrypt(hw).toString('utf8'));
As you can see, cipher.update(buffer) handles the encoding internally so you don't need to.

Related

How can I write a client side cryption same as node js server side? [duplicate]

How to convert the following Node's built-in crypto module encryption to CryptoJS?
const crypto = require('crypto');
const pass = 'some,password:)with>spec(chars*'
const cipher1 = crypto.createCipher('aes-256-cbc', pass)
const c1 = cipher1.update(input, 'utf8', 'hex') + cipher1.final('hex')
I tried something like this, but the results are not the same:
const CryptoJS = require('crypto-js');
const pass = 'some,password:)with>spec(chars*'
const cipher2 = CryptoJS.AES.encrypt(input, pass, {
mode: CryptoJS.mode.CBC,
});
const c2 = cipher2.ciphertext.toString(CryptoJS.enc.Hex);
I need this to use as a Postman prerequest script as it does not support Node's crypto, but crypto-js.
Both codes use the OpenSSL proprietary key derivation function EVP_BytesToKey() with an iteration count of 1 and MD5 as digest.
NodeJS does not use a salt, while CryptoJS applies a random salt. For this reason, the NodeJS result is unchanged for each encryption, while the CryptoJS result always changes (assuming the same plaintext and passphrase).
Thus, to get the result of the NodeJS code with the CryptoJS code, you must not use a salt. However, by default, a salt is always applied. This can only be circumvented by explicitly determining key and IV with the key derivation function EvpKDF and then using both in the encryption:
var input = "The quick brown fox jumps over the lazy dog";
var pass = 'some,password:)with>spec(chars*'
var keySize = 32/4;
var ivSize = 16/4;
var kdf = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize, hasher: CryptoJS.algo.MD5 }).compute(pass, ''); // no salt!
var key = CryptoJS.lib.WordArray.create(kdf.words.slice(0, keySize), keySize * 4);
var iv = CryptoJS.lib.WordArray.create(kdf.words.slice(keySize), ivSize * 4);
var ciphertextCP = CryptoJS.AES.encrypt(input, key, {iv: iv}); // default: CBC, PKCS#7 padding
var ciphertext = ciphertextCP.ciphertext.toString(CryptoJS.enc.Hex);
document.getElementById("ct").innerHTML = ciphertext; // d98cf2d285bf0c1d796226190bf54d9c5540300ee1c6f35618f8bb3564b5053920ec958d31b41bbe4e4880e23543d709
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="ct"></p>
This CryptoJS code produces the same ciphertext for the same plaintext and passphrase as the NodeJS code.
Note that the key derivation with EVP_BytesToKey() and the chosen parameters is deprecated and considered insecure today. This is true for NodeJS to a greater extent than for CryptoJS, due to the lack of a salt.
It is more secure to avoid the built-in key derivation function and specify the key directly.
To do this, use createCipheriv() in NodeJS and pass the key as WordArray in the CryptoJS code. This way, a random IV must be explicitly generated for each encryption.
Optionally, a reliable key derivation function like PBKDF2 can be used, which is supported by both libraries.

How to encrypt with CryptoJS and decrypt with Rust Magic Crypt

I'm attempting to encrypt a message using Javascript's CryptoJS like so:
const encrypted_payload = CryptoJS.AES.encrypt("Hello, world!", "magickey");
console.log(enrypted_payload.toString());
... and then decrypt using Rust's Magic Crypt Library, like so:
#[macro_use] extern crate magic_crypt;
use magic_crypt::MagicCryptTrait;
fn main() {
let message = r#"U2FsdGVkX1+anrxRX8UNTJ8ur9LIf6n2YcmbmDqPSls="#;
let mc = new_magic_crypt!("magickey", 256);
let result = mc.decrypt_base64_to_string(&message)
.expect("Could not decrypt base64 to string");
println!("{}", result)
}
The Cargo.toml file for Rust includes:
[dependencies]
magic-crypt = "3.1.6"
This fails to compile. The error statement is DecryptError(BlockModeError).
If in CryptoJS the key is passed as a string, then it is interpreted as a password and the key/IV is derived using a special derivation function (EVP_BytesToKey). To process the key directly as a key, it must be passed as a WordArray.
In the Rust code the passwords seem to be simply hashed (e.g. MD5 for AES-128, SHA256 for AES-256), which is surprisingly insecure (though I'm not a Rust expert and may be overlooking something).
Anyway this results in incompatible keys. One possibility is to use the hash of the Rust key as WordArray in CryptoJS:
Example: From the magic_crypt documentation, here:
use magic_crypt::MagicCryptTrait;
let mc = new_magic_crypt!("magickey", 256);
let base64 = mc.encrypt_str_to_base64("http://magiclen.org");
assert_eq!("DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=", base64);
assert_eq!("http://magiclen.org", mc.decrypt_base64_to_string(&base64).unwrap());
magic_crypt internally derives a 32 bytes key as SHA256 hash from the password. Then the encryption with CryptoJS is:
var key = CryptoJS.SHA256("magickey");
var ciphertext = "DS/2U8royDnJDiNY2ps3f6ZoTbpZo8ZtUGYLGEjwLDQ=";
var iv = CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==")
var decrypted = CryptoJS.AES.decrypt(ciphertext, key, {iv: iv});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Both codes apply CBC mode with a zero IV and PKCS7 padding. Note that a static IV is insecure and should only be used for testings.
The other direction is analogous, e.g. encryption with CryptoJS generates the ciphertext from the Rust example (assuming the same key, IV and plaintext):
var key = CryptoJS.SHA256("magickey");
var plaintext = "http://magiclen.org";
var iv = CryptoJS.enc.Base64.parse("AAAAAAAAAAAAAAAAAAAAAA==")
var ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv});
console.log(ciphertext.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

AES decryption with PyCryptodome does not produce the expected result

I have some difficulties reproducing AES encryption and decryption in python.
Context: A year ago, I created a small django based application using this javascript library for client side encryption. Basically, some users' inputs are encrypted with a key and sent as hexadecimal strings to be stored.
For the illustration, I'll focus on bd45bcccd0 (a.k.a 'Masha' encrypted with john's key: 3ed8bd71327aafd855aac37921519767)
Encryption and decryption with the current js library
encryption utf-8 -> bytes -> encrypted bytes -> hex
decryption hex -> encrypted bytes -> bytes -> utf-8
id_password is a MD5 hash of the user's password. It is stored in the session storage and is used as a key
function encrypt(t){
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
var textBytes = aesjs.utils.utf8.toBytes(t);
var aesCtr = new aesjs.ModeOfOperation.ctr(key);
var encryptedBytes = aesCtr.encrypt(textBytes);
var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
return encryptedHex;
}
function decrypt(t){
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
var textBytes = aesjs.utils.hex.toBytes(t);
var aesCtr = new aesjs.ModeOfOperation.ctr(key);
var decriptedBytes = aesCtr.decrypt(textBytes);
var decrypted_utf8 = aesjs.utils.utf8.fromBytes(decriptedBytes);
return decrypted_utf8;
}
Once loaded in key, I get a 16 items array (So I guess a AES 128bits CTR is performed):
var key = aesjs.utils.hex.toBytes(sessionStorage.getItem("id_password"));
console.log(key)
Array(16) [ 62, 216, 189, 113, 50, 122, 175, 216, 85, 170, … ]
With the current code, encryption and decryption work
Python implementation
For unit-testing purposes, I wanted to be able to decrypt. I am using this library. To mimic the client side as much as possible, I tried the following:
john_key = "3ed8bd71327aafd855aac37921519767"
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR)
d = cipher.decrypt(codecs.decode('bd45bcccd0', 'hex_codec'))
d.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xac in position 0: invalid start byte
Here is the problem but I am not sure at which stage it occurs. Here is what I checked:
# key's length is correct
k = codecs.decode(john_key,'hex_codec')
k
b'>\xd8\xbdq2z\xaf\xd8U\xaa\xc3y!Q\x97g'
len(k)
16
# decoded message's length is correct
d = cipher.decrypt(codecs.decode('bd45bcccd0', 'hex_codec'))
len(d)
5
Since I can't rely on a library I can't reproduce the results, I wonder whether I misused PyCryptodome or whether the way this javascript library implements AES CTR encryption is reliable. Any insights?
The CTR-mode requires an IV. Since you do not explicitly create the IV, an implicitly created IV is used. However, both codes generate different IVs, so that the decryption fails. In the Python-code, a random IV is generated, in the aes-js-code a fixed IV (1) is used.
So that the decryption is possible with the Python-code, the same IV must be used here as in the aes-js-code (here and here). For this purpose:
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR)
has to be replaced by
counter = Counter.new(128, initial_value = 1)
cipher = AES.new(codecs.decode(john_key,'hex_codec'), AES.MODE_CTR, counter = counter)
which decrypts the ciphertext to Maria (however not Masha).
For security reasons it is mandatory for CTR that key/IV pairs may only be used once, i.e. if the same key is applied, a new IV must be generated for each encryption. The current code has the weakness that key/IV pairs would be repeated when using the same key. A better way would be to generate a random IV for each encryption, send this IV together with the ciphertext to the recipient (the IV isn't secret, so it is usually prepended to the ciphertext), where it can be used for the decryption.

Javascript Decrypt C# AES Encryption

I'm trying to decrypt a C# encrypt string using javascript,
This is an example of the encryption on my server side
public class AesCrypt
{
public static string IV = #"!QAZ2WSX#EDC4RFV";
public static string Key = #"5TGB&YHN7UJM(IK<5TGB&YHN7UJM(IK<";
public static string Encrypt(string dectypted)
{
byte[] textbytes = ASCIIEncoding.ASCII.GetBytes(dectypted);
AesCryptoServiceProvider encdec = new AesCryptoServiceProvider();
encdec.BlockSize = 128;
encdec.KeySize = 256;
encdec.Key = ASCIIEncoding.ASCII.GetBytes(Key);
encdec.IV = ASCIIEncoding.ASCII.GetBytes(IV);
encdec.Padding = PaddingMode.PKCS7;
encdec.Mode = CipherMode.CBC;
ICryptoTransform icrypt = encdec.CreateEncryptor(encdec.Key, encdec.IV);
byte[] enc = icrypt.TransformFinalBlock(textbytes, 0, textbytes.Length);
icrypt.Dispose();
return Convert.ToBase64String(enc);
}
}
The encryption of "Hello World" is "1i4zI5rB3Df2CYFalsiTwg=="
Now I'm trying to decrypt it using js on my client and get Hello World and this is where I fail,
I'm using <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script> in order to decrypt and I watch some of examples over the web (including stackoverflow).
Accoring to some examples over the web this is what I came out of, but it's not returning "Hello World" back.
data = "1i4zI5rB3Df2CYFalsiTwg==";
key = "5TGB&YHN7UJM(IK<5TGB&YHN7UJM(IK<";
iv = "!QAZ2WSX#EDC4RFV";
CryptoJS.AES.decrypt(atob(data), key, {
iv: atob(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
I'm using the same mode and padding, but I'm probably missing something.
I'm not that familiar with CryptoJS and I hope someone can help me understand where I went wrong.
Thanks in advance
From the old CryptoJS pages, the Cipher input part:
For the key, when you pass a string, it's treated as a passphrase and used to derive an actual key and IV. Or you can pass a WordArray that represents the actual key. If you pass the actual key, you must also pass the actual IV.
Although you are passing an IV, you do currently put in a string rather than a binary key as word array. I suppose this is the problem as I don't see any other obvious programming mistakes.

Using Forge to encrypt/decrypt AES

I want to create two functions encrypt(message, key) and decrypt(ciphertext, key) using the Forge library in javascript, but I dont undestand the example code.
// generate a random key and IV
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(16);
// encrypt some bytes using CBC mode
// (other modes include: CFB, OFB, and CTR)
var cipher = forge.aes.createEncryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());
// decrypt some bytes using CBC mode
// (other modes include: CFB, OFB, and CTR)
var cipher = forge.aes.createDecryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
// outputs decrypted hex
console.log(cipher.output.toHex());
// generate a password-based 16-byte key
var salt = forge.random.getBytesSync(128);
var derivedKey = forge.pkcs5.pbkdf2('password', salt, numIterations, 16);
Where should I use my own key?
Where can I choose 256 bit mode?
Can you give me an easier example?
Where should I use my own key?
I haven't used that library but it seems pretty straight forward. Take this part at the top:
// generate a random key and IV
var key = forge.random.getBytesSync(16);
And put your key in like this:
// generate a random key and IV
var key = neverGuessMahKeyIs1234;
Do the same for the iv if you want.
Where can I choose 256 bit mode?
Ok, so first of all your dealing with symmetric encryption which has a key length of the desired size. Because it's symmetric, it's used on both the encrypting and decrypting ends, which is what the code that you posted seems to do. I say 'seems' because I'm trusting that the library's native functions are as you posted them.
So, the code as you posted seems to use (as I showed above) 128 bits (16*8=128). If you want a random 256, then just use:
var key = forge.random.getBytesSync(32);
Or just make your own key that 256 bits long.

Categories