I've got a set of functions for encrypting/decrypting on the server (C#), however I'd like to port the decryption piece to Javascript. Here's the C# decryption functions I've used previously in C# with a sample encrypted string, password and IV
I'm having trouble with the JavaScript decryption piece using Cryptojs. The result that comes back is unexpectedly an empty string.
var ciphertext = "0MuDwNoWBFjN/1anszbl0Cxkrwh9ahRwE3c61t7io2c=";
var key = "8beee7ac-42d1-4294-91b8-68cd032cf1e1";
var iv = "9bC_#$/-+%#Kli%1Az=-#qT";
var ciphertextWA = CryptoJS.enc.Hex.parse(ciphertext);
var keyWA = CryptoJS.enc.Utf8.parse(key);
var ivWA = CryptoJS.enc.Utf8.parse(iv);
var ciphertextCP = { ciphertext: ciphertextWA };
var decrypted = CryptoJS.AES.decrypt(
ciphertextCP,
keyWA,
{ iv: ivWA }
);
console.log(decrypted.toString(CryptoJS.enc.Utf8));
The bugs are in the determination of IV and key. Both are derived from passwords using SHA512. In the case of the key the first 32 bytes (bytes 0-31) are used, in the case of the IV the 16 bytes following the first 32 bytes (bytes 32-47).
The fixed code is:
var ciphertext = "0MuDwNoWBFjN/1anszbl0Cxkrwh9ahRwE3c61t7io2c=";
var key = "8beee7ac-42d1-4294-91b8-68cd032cf1e1";
var iv = "9bC_#$/-+%#Kli%1Az=-#qT";
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var ciphertextCP = { ciphertext: ciphertextWA };
var keyHashWA = CryptoJS.SHA512(key);
var keyWA = CryptoJS.lib.WordArray.create(keyHashWA.words.slice(0, 32/4));
var ivHashWA = CryptoJS.SHA512(iv);
var ivWA = CryptoJS.lib.WordArray.create(ivHashWA.words.slice(32/4, 48/4));
var decryptedWA = CryptoJS.AES.decrypt(
ciphertextCP,
keyWA,
{ iv: ivWA }
);
console.log(decryptedWA.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Edit:
As you correctly guessed, the problem is caused by the different encodings (Windows-1252 in the C# code and UTF-8 in the JavaScript code). The fix is to decode in the JavaScript code with Windows-1252.
CryptoJS supports Latin1 (aka ISO-8859-1) but not Windows-1252 (aka Cp1252), s. encoders. The differences between the two encodings are minor, ranging from 0x80 to 0x9F. Just like UTF-8, both correspond to ASCII encoding in the range between 0x00 and 0x7F. Since the original plaintext contained only ASCII characters, there were no problems.
However, the new plaintext now contains byte sequences that are not compatible with UTF-8 (hence the error) or whose characters differ from Latin1 (hence the mismatches).
Since CryptoJS does not support Windows-1252, the decoding must be done with JavaScript on-board means or another library. One possibility is to convert the CryptoJS type WordArray to the more general JavaScript type Uint8Array. Then, using TextDecoder(), which supports a large repertoire of encodings, decoding can be performed using Windows-1252:
var ciphertext = `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`;
var key = "55790c03-4a4b-49f7-b7fe-d178454d88f0";
var iv = "9bC_#$/-+%#Kli%1Az=-#qT";
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var ciphertextCP = { ciphertext: ciphertextWA };
var keyHashWA = CryptoJS.SHA512(key);
var keyWA = CryptoJS.lib.WordArray.create(keyHashWA.words.slice(0, 32/4));
var ivHashWA = CryptoJS.SHA512(iv);
var ivWA = CryptoJS.lib.WordArray.create(ivHashWA.words.slice(32/4, 48/4));
var decryptedWA = CryptoJS.AES.decrypt(
ciphertextCP,
keyWA,
{ iv: ivWA }
);
function convertWordArrayToUint8Array(wordArray) {
var arrayOfWords = wordArray.hasOwnProperty("words") ? wordArray.words : [];
var length = wordArray.hasOwnProperty("sigBytes") ? wordArray.sigBytes : arrayOfWords.length * 4;
var uInt8Array = new Uint8Array(length), index=0, word, i;
for (i=0; i<length; i++) {
word = arrayOfWords[i];
uInt8Array[index++] = word >> 24;
uInt8Array[index++] = (word >> 16) & 0xff;
uInt8Array[index++] = (word >> 8) & 0xff;
uInt8Array[index++] = word & 0xff;
}
return uInt8Array;
}
var decryptedUint8Array = convertWordArrayToUint8Array(decryptedWA);
var decrypted = new TextDecoder('windows-1252').decode(decryptedUint8Array);
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Related
I have the following code for decryption in java, I want that to be implemented in nodejs but the i found many tuto for my problem but they use a salt and that don't work when i try to remove the salt system.
My olds functions from java
public void readLastLogin(File lastLogin) {
try {
final Cipher ciph = this.openCipher(Cipher.DECRYPT_MODE);
final DataInputStream dis = new DataInputStream(new CipherInputStream(new FileInputStream(lastLogin), ciph));
String user = dis.readUTF();
String token = dis.readUTF();
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void saveLastLogin(File lastLogin, String user, String token) {
try {
final Cipher ciph = this.openCipher(Cipher.ENCRYPT_MODE);
final DataOutputStream dos = new DataOutputStream(new CipherOutputStream(new FileOutputStream(lastLogin), ciph));
dos.writeUTF(user);
dos.writeUTF(token);
dos.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
private Cipher openCipher(final int mode) throws Exception {
final Random rnd = new Random(43287234L);
final byte[] data = new byte[8];
rnd.nextBytes(data);
final PBEParameterSpec spec = new PBEParameterSpec(data, 5);
final SecretKey key = SecretKeyFactory
.getInstance("PBEWithMD5AndDES")
.generateSecret(new PBEKeySpec("mysecretpassword".toCharArray()));
final Cipher ret = Cipher.getInstance("PBEWithMD5AndDES");
ret.init(mode, key, spec);
return ret;
}
I try this but not working
KDF(password, iterations) {
var key = new Buffer(password,'utf-8');
var i;
for (i = 0; i < iterations; i+=1) {
key = crypto.createHash("md5").update(key).digest();
}
return key;
}
getKeyIV(password, iterations) {
var key = this.KDF(password, iterations);
var keybuf = new Buffer(key,'binary').slice(0,8);
var ivbuf = new Buffer(key,'binary').slice(8,16);
return [ keybuf, ivbuf ];
}
decrypt(message) {
var kiv = this.getKeyIV('password' ,5);
var decipher = crypto.createDecipheriv('des-ede', kiv[0], kiv[1]);
var result = decipher.update(message, 'hex', 'utf-8');
return result + decipher.final('utf-8');
}
Edit: The salt generated at runtime of the Java code is hex encoded: 0x0C9D4AE41E8315FC
final byte[] data = new byte[8];
data[0] = 12;
data[1] = -99;
data[2] = 74;
data[3] = -28;
data[4] = 30;
data[5] = -125;
data[6] = 21;
data[7] = -4;
There are three issues in the code:
The Java code uses writeUTF(). This is based on modified UTF-8, with two additional bytes prepended to contain the data size. A NodeJS library for modified UTF-8 is mutf-8.
Instead of des-ede (3DES/2TDEA in ECB mode), des-cbc (DES in CBC mode) must be applied. If DES is not available in the used NodeJS version, it can be mimicked with 3DES/2TDEA in CBC mode, des-ede-cbc, where the 8 bytes key has to be concatenated with itself to a 16 bytes key, reducing 3DES to DES. This workaround has the disadvantage of a performance loss.
The Java code applies a static 8 byte salt because Random() is instantiated with a static seed. This salt can be determined at runtime to (hex encoded): 0x0C9D4AE41E8315FC.
pbewithmd5anddes-js is a port of PBEWithMD5AndDES to NodeJS, from which the KDF(), getKeyIV() and decrypt() methods can be used, but must be adapted taking into account the above points. In addition, the functionality of readUTF() (the counterpart of writeUTF()) must be implemented. One possible solution is:
var crypto = require('crypto');
const { MUtf8Decoder } = require("mutf-8");
var pbewithmd5anddes = {
KDF: function(password,salt,iterations) {
var pwd = Buffer.from(password,'utf-8');
var key = Buffer.concat([pwd, salt]);
var i;
for (i = 0; i < iterations; i+=1) {
key = crypto.createHash('md5').update(key).digest();
}
return key;
},
getKeyIV: function(password,salt,iterations) {
var key = this.KDF(password,salt,iterations);
var keybuf = Buffer.from(key,'binary').subarray(0,8);
var ivbuf = Buffer.from(key,'binary').subarray(8,16);
return [ keybuf, ivbuf ];
},
decrypt: function(payload,password,salt,iterations) {
var encryptedBuffer = Buffer.from(payload,'base64');
var kiv = this.getKeyIV(password,salt,iterations);
//var decipher = crypto.createDecipheriv('des-cbc', kiv[0], kiv[1]); // Fix 1: If supported, apply DES-CBC with key K
var decipher = crypto.createDecipheriv('des-ede-cbc', Buffer.concat([kiv[0], kiv[0]]), kiv[1]); // otherwise DES-EDE-CBC with key K|K
var decryptedBuf = Buffer.concat([decipher.update(encryptedBuffer), decipher.final()])
var decrypted = this.readUTF(decryptedBuf) // Fix 2: apply writeUTF counterpart
return decrypted;
},
readUTF: function(decryptedBuf) {
var decoder = new MUtf8Decoder()
var decryptedData = []
var i = 0;
while (i < decryptedBuf.length){
var lengthObj = decryptedBuf.readInt16BE(i);
var bytesObj = decryptedBuf.subarray(i+2, i+2+lengthObj);
var strObj = decoder.decode(bytesObj)
decryptedData.push(strObj)
i += 2 + lengthObj;
}
return decryptedData;
}
};
Test:
On the Java side, the following is executed:
saveLastLogin(file, "This is the first plaintext with special characters like §, $ and €.", "This is the second plaintext with special characters like §, $ and €.");
The raw ciphertext written to file is Base64 encoded:
Ow8bdeNM0QpNFQaoDe7dhG3k9nWz/UZ6v3+wQVgrD5QvWR/4+sA+YvqtnQBsy35nQkwhwGRBv1h1eOa587NaFtnJUWVHsRsncLWZ05+dD2rYVpcZRA8s6P2iANK6yLr+GO/+UpZpSe0fA4fFqEK1nm3U7NXdyddfuOlZ3h/RiyxK5819LieUne4F8/TpMzT0RIWkxqagbVw=
This can be decrypted on the NodeJS side with:
var payload = 'Ow8bdeNM0QpNFQaoDe7dhG3k9nWz/UZ6v3+wQVgrD5QvWR/4+sA+YvqtnQBsy35nQkwhwGRBv1h1eOa587NaFtnJUWVHsRsncLWZ05+dD2rYVpcZRA8s6P2iANK6yLr+GO/+UpZpSe0fA4fFqEK1nm3U7NXdyddfuOlZ3h/RiyxK5819LieUne4F8/TpMzT0RIWkxqagbVw=';
var password = 'mysecretpassword';
var salt = Buffer.from('0C9D4AE41E8315FC', 'hex');
var iterations = 5;
var decrypted = pbewithmd5anddes.decrypt(payload,password,salt,iterations);
console.log(decrypted);
with the output:
[
'This is the first plaintext with special characters like §, $ and €.',
'This is the second plaintext with special characters like §, $ and €.'
]
Note that the code contains serious vulnerabilities:
PBEWithMD5AndDES uses a key derivation based on the broken MD5 digest (PBKDF1), and as encryption algorithm the deprecated DES (officially withdrawn almost 20 years ago).
Use of a static salt.
An iteration count of 5 is generally much too small.
Random() (unlike SecureRandom()) is not cryptographically strong. Also note that it is not a good idea to use a PRNG like Random() for deterministic key derivation, as the implementation may change, leading to different results even with the same seed.
I have encoded a string using this method in Python. I want to decode the encoded string using Javascript.
from Crypto.Cipher import XOR
def encrypt(key, plaintext):
cipher = XOR.new(key)
return base64.b64encode(cipher.encrypt(plaintext))
I got a better solution to encrypt in Python and decrypt in Javascript.
Python Encoding Part:
import base64
from Crypto import Random
from Crypto.Cipher import AES
import json
def encrypt_val(clear_text):
master_key = '0123456789101112'
encoder = PKCS7Encoder()
raw = encoder.encode(clear_text)
iv = Random.new().read( 16 )
cipher = AES.new( master_key, AES.MODE_CBC, iv, segment_size=128 )
return base64.b64encode( iv + cipher.encrypt( raw ) )
print(encrypt_val(json.dumps({"name":"Tahir"})))
Javascript Decoding Part:
const CryptoJS = require('crypto-js')
const atob = require('atob')
function decryptMsg (data) {
master_key = '0123456789101112';
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
// Split by 16 because my IV size
var iv = rawData.substring(0, 16);
var crypttext = rawData.substring(16);
//Parsers
crypttext = CryptoJS.enc.Latin1.parse(crypttext);
iv = CryptoJS.enc.Latin1.parse(iv);
key = CryptoJS.enc.Utf8.parse(master_key);
// Decrypt
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: crypttext},
key,
{iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}
);
// Can be Utf8 too
output_plaintext = CryptoJS.enc.Latin1.stringify(plaintextArray);
console.log("plain text : " + output_plaintext);
}
encoded_message = "XF8T/miPPVk9B7GfNUshRqJf6//sJnWey+TqoXj70Tp0vJhjw22uDLOLh7es4BOp"
decryptMsg(encoded_message)
I am getting an encrypted key which is generated using Java. I need to decrypt it in AngularJS app using CryptoJS. I had done similar thing using node but now in Angular I am stuck. This fiddle http://jsfiddle.net/s5g82rqh/ is what I have tried so far but it returns empty.
Below is what I have tried till now
function decrypt_core_AES_CBC(password, ciphertext) {
var iv = CryptoJS.lib.WordArray.random(128/8);
var message = CryptoJS.AES.decrypt(ciphertext, password, { mode: CryptoJS.mode.CBC, iv: password });
console.log("The current iv is: " + iv.toString() );
return CryptoJS.enc.Utf8.stringify(message);
}
var data = '6615702f2dd672f643fd57623d6362a510a98faf4b1c068fd468b525a5fa5471809852a0f9cb7936ce3d3892c233b8c48ce2608f16ce6fa66005b2d97689fbb4';
var key = '3426D38AB846B62B9C236D288778D997';
var dec = decrypt_core_AES_CBC(key, data);
console.log(dec);
Below is the node.js code which works for me. I have no success in achieving similar in CryptoJS. As per my understanding crypto comes as built-in library which node has its own wrapper on top of it.
var crypto = require('crypto');
var defaultAlgorithm= 'aes-128-cbc';
var defaultFormat= 'hex';
var ivLength= 16;
function decode (data, key, algorithm, format) {
// Make sure the data is a buffer object
if (data instanceof Buffer) {
data = data.toString();
}
// Get defaults if needed
algorithm = algorithm || defaultAlgorithm;
format = format || defaultFormat;
ivLength = ivLength * 2;
// Get the initialization vector
var iv = new Buffer(data.substring(0, ivLength), 'hex');
// Remove the iv from the data
data = data.substring(ivLength);
var decipher = crypto.createDecipheriv(algorithm, new Buffer(key, 'hex'), iv);
var decrypted = decipher.update(data, format, 'utf8') + decipher.final('utf8');
return decrypted;
}
var data ='6615702f2dd672f643fd57623d6362a510a98faf4b1c068fd468b525a5fa5471809852a0f9cb7936ce3d3892c233b8c48ce2608f16ce6fa66005b2d97689fbb4';
var key = '3426D38AB846B62B9C236D288778D997';
var dec = decode(data, key, defaultAlgorithm, defaultFormat);
console.log(dec);
You have three issues:
CryptoJS supports two types of encryption/decryption: key derived from a password and directly passed key. You want to do this from a key, so you need to parse the hex-encoded key string into CryptoJS' native format before passing it to the decrypt() function:
key = CryptoJS.enc.Hex.parse(key);
Also, don't confuse a key with a password.
You forgot to slice off the IV from the ciphertext before decrypting.
var iv = CryptoJS.enc.Hex.parse(ciphertext.slice(0, 32));
ciphertext = CryptoJS.enc.Hex.parse(ciphertext.slice(32));
CryptoJS' expects either a CipherParams object or an OpenSSL-formatted string to decrypt from. Since you only have a hex string, you have to parse it before use and use it like this:
var message = CryptoJS.AES.decrypt({
ciphertext: ciphertext
}, key, {
iv: iv
});
It took one week for me to find out working code for aes-128-cbc in Java, PHP and Java Script. I had to search alot at various website. Finally with multiple hit and trial below code worked out for me. Both encryption and decryption. Encrypt in Java; Decrypt in PHP or JavaScript, Encrypt in PHP; Decrypt in Java or JavaScript, Encrypt in JavaScript Decrypt in PHP or Java. All option will work
The length of Key will be 16 digit having Alpha-numeric values. Use Same key to encrypt and decrypt data. IV is 16 digit Random value of Alpha-numeric to be passed while encryption.
PHP code for Encryption and Decryption:
function encrypt($key, $iv, $data) {
static $OPENSSL_CIPHER_NAME = "aes-128-cbc"; //Name of OpenSSL Cipher
static $CIPHER_KEY_LEN = 16; //128 bits
if (strlen($key) < $CIPHER_KEY_LEN) {
$key = str_pad("$key", $CIPHER_KEY_LEN, "0"); //0 pad to len 16
} else if (strlen($key) > $CIPHER_KEY_LEN) {
$key = substr($str, 0, $CIPHER_KEY_LEN); //truncate to 16 bytes
}
$encodedEncryptedData = base64_encode(openssl_encrypt($data, $OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, $iv));
$encodedIV = base64_encode($iv);
$encryptedPayload = $encodedEncryptedData.":".$encodedIV;
return $encryptedPayload;
}
function decrypt($key, $data) {
// $key = $request['key'];
// $data = $request['data'];
static $OPENSSL_CIPHER_NAME = "aes-128-cbc"; //Name of OpenSSL Cipher
static $CIPHER_KEY_LEN = 16; //128 bits
if (strlen($key) < $CIPHER_KEY_LEN) {
$key = str_pad("$key", $CIPHER_KEY_LEN, "0"); //0 pad to len 16
} else if (strlen($key) > $CIPHER_KEY_LEN) {
$key = substr($str, 0, $CIPHER_KEY_LEN); //truncate to 16 bytes
}
$parts = explode(':', $data); //Separate Encrypted data from iv.
$decryptedData = openssl_decrypt(base64_decode($parts[0]), $OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, base64_decode($parts[1]));
return $decryptedData;
}
Java Code for Encryption and Decryption
import android.util.Base64;
import android.util.Log;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Java_AES_Cipher {
private static String CIPHER_NAME = "AES/CBC/PKCS5PADDING";
private static int CIPHER_KEY_LEN = 16; //128 bits
/**
* Encrypt data using AES Cipher (CBC) with 128 bit key
*
*
* #param key - key to use should be 16 bytes long (128 bits)
* #param iv - initialization vector
* #param data - data to encrypt
* #return encryptedData data in base64 encoding with iv attached at end after a :
*/
public static String encrypt(String key, String iv, String data) {
try {
if (key.length() < Java_AES_Cipher.CIPHER_KEY_LEN) {
int numPad = Java_AES_Cipher.CIPHER_KEY_LEN - key.length();
for(int i = 0; i < numPad; i++){
key += "0"; //0 pad to len 16 bytes
}
} else if (key.length() > Java_AES_Cipher.CIPHER_KEY_LEN) {
key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
}
IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("ISO-8859-1"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("ISO-8859-1"), "AES");
Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector);
byte[] encryptedData = cipher.doFinal((data.getBytes()));
String base64_EncryptedData = Base64.encodeToString(encryptedData, Base64.DEFAULT);
String base64_IV = Base64.encodeToString(iv.getBytes("ISO-8859-1"), Base64.DEFAULT);
base64_EncryptedData = base64_EncryptedData.replaceAll(System.getProperty("line.separator"), "");
base64_IV = base64_IV.replaceAll(System.getProperty("line.separator"), "");
Log.i("Java_AES_Cipher","Encrypted data is "+ base64_EncryptedData + ":" + base64_IV);
return base64_EncryptedData + ":" + base64_IV;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* Decrypt data using AES Cipher (CBC) with 128 bit key
*
* #param key - key to use should be 16 bytes long (128 bits)
* #param data - encrypted data with iv at the end separate by :
* #return decrypted data string
*/
public static String decrypt(String key, String data) {
try {
if (key.length() < Java_AES_Cipher.CIPHER_KEY_LEN) {
int numPad = Java_AES_Cipher.CIPHER_KEY_LEN - key.length();
for(int i = 0; i < numPad; i++){
key += "0"; //0 pad to len 16 bytes
}
} else if (key.length() > Java_AES_Cipher.CIPHER_KEY_LEN) {
key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
}
String[] parts = data.split(":");
if (parts.length<2)
{
Log.i("Java_AES_Cipher","Length "+ parts.length);
return data;
}
IvParameterSpec iv = new IvParameterSpec(Base64.decode(parts[1], Base64.DEFAULT));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("ISO-8859-1"), "AES");
Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decodedEncryptedData = Base64.decode(parts[0], Base64.DEFAULT);
byte[] original = cipher.doFinal(decodedEncryptedData);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
Java Script Code for Encryption and Decryption
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
function encrypt (messageText,key, iv){
var message = messageText;
var key = CryptoJS.enc.Utf8.parse(key);
var iv = CryptoJS.enc.Utf8.parse(iv);
var encrypted = CryptoJS.AES.encrypt(
message,key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
this.encrypted = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
console.log('encrypted:'+encrypted);
return encrypted;
}
function decrypt(keyBase64, messagebase64) {
const digest = messagebase64.split(':');
const crypttext = CryptoJS.enc.Base64.parse(digest[0])
const ivBase64 = CryptoJS.enc.Base64.parse(digest[1])
const decrypted = CryptoJS.AES.decrypt(
{
ciphertext: crypttext
},
CryptoJS.enc.Utf8.parse(keyBase64),
{
iv: ivBase64,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
console.log('decrypted: '+decrypted.toString(CryptoJS.enc.Utf8));
return decrypted.toString(CryptoJS.enc.Utf8);
}
I'm able to decrypt AES encrypted message with Ruby like this:
require 'openssl'
require 'base64'
data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg="
key = "2e35f242a46d67eeb74aabc37d5e5d05"
aes = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
aes.decrypt
aes.key = key.scan(/../).collect{ |x| x.hex }.pack('c*')
aes.iv = Base64.decode64(data)[0...16]
puts aes.update(Base64.decode64(data)[16..-1]) + aes.final
# => JSON data...
Being new to CryptoJS I fail to put together working alternative. Here's what i've done so far:
data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg=";
key = "2e35f242a46d67eeb74aabc37d5e5d05";
CryptoJS.AES.decrypt(atob(data).substring(16), key, {
iv: atob(cipher).substring(0, 16),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
Does CryptoJS expect data, key and initialization vector in some different way? As far as i understand, the IV are the first 16 bytes in data.
This works for decryption using javascript.
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/aes.js"></script>
var key = "2e35f242a46d67eeb74aabc37d5e5d05";
var data = CryptoJS.AES.encrypt("Message", key); // Encryption Part
var decrypted = CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8); // Message
Guess I am a little late to the party.
This seems to work for me (writes decoded string to the console):
data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg=";
key = "2e35f242a46d67eeb74aabc37d5e5d05";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = btoa(rawData.substring(0,16));
var crypttext = btoa(rawData.substring(16));
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{
ciphertext: CryptoJS.enc.Base64.parse(crypttext),
salt: ""
},
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Base64.parse(iv) }
);
// Convert hex string to ASCII.
// See https://stackoverflow.com/questions/11889329/word-array-to-string
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
console.log(hex2a(plaintextArray.toString()));
The important bits are to specify no salt in your input and pass the key as a WordArray as jlvaquero says. There are probably some efficiency improvements to be made, e.g. I'm guessing there's an easier way to convert a string to a WordArray than going to base64 and back, but at least it successfully decodes. I lifted the hex2a function from StackOverflow question Word Array to String.
Edit: I figured out how to convert strings to WordArrays and vice-versa with CryptoJS so the extra base64 encode/decode and hex-to-ASCII function are not needed. Also it turns out the salt property need not be specified. This is more compact and efficient:
data = "IYkyGxYaNgHpnZWgwILMalVFmLWFgTCHCZL9263NOcfSo5lBjAzOZAtF5bF++R0Bi+9c9E+p3VEr/xvj4oABtRWVJ2wlWzLbYC2rKFk5iapFhb7uZCUpO4w4Su3a5QFa2vInjYueziRoqySZd/DpstMJ8rsJ94VGizFFFZ1l0sw1ax+wfBAv5+wHs/hlnHi/ea66KBO3rgXKahvV28h+4bh5etc8RCrmiiNbfg6Oj0jQJDjdYIdW8T9YPOI9E1hih8lbfRnMWcOFJgYekfLpoy5LI525UGnlM46J1k6ekLqsn9FqvbiOOoLgqa4YqBm1i9P0ePyjkME+t+RiL8xXX+ItgOYr9G7kM64wlTJPCW8B/crmUdmGzQNC/hD/u/8wfHBS2f8u6OtQMG/+Kpk1oju8lcUZGI/4S8A6/OuktvQr2zgnbs2aADMrM37Oait/pJ3G73S7NwVT8EaK+X43c0C/fUvW2/bD/rqCNpAh9WQlz4Cj6JHwjbmwuind6aCimF1tHjXuR9FXu+g17sPT4ZkKZ6aeBG+m170XdCGn2hVM0wH1rh3VeCG2u/JFqfuGKGSoqeHeNY/icu9pEhtZDzHd7aPoaMXcWvXC9PjooBf7GM1EPacSdnon1kBobjtKSt1l15DjO5TMrJoX7VO7GotQwo+uI/u5Kop01hBXxyxyggl1/8N0ESohPJoqLDrIwvbGK5kW4B49FVPnx9CMvjZDdSsoxPAh+hx6SPe8Hj0Nx4bRs06cbtOkte/V8QSYIqjiJDleEqPrdiKlvgToZz9L29ZR/3Ln65qU1sq7q9c0SEYxIopV7TdTjFS7y76zDPFZkhzc3DjfLtJo/M1hdtt648APcZdmAIgWH6fh3eJZ0qbiPh8RStYH7I2COmnlMw4+t/B5mlhYVSgwPK2Ir736Mh+P9Bw0fF8r9Ghhs4AJzpU0RiK9d1tCsrLfK/hSRwTXhtsSB6eDWEGkO7oeEIz43mgn0sv3SrA9JNHzYkg=";
key = "2e35f242a46d67eeb74aabc37d5e5d05";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = rawData.substring(0,16);
var crypttext = rawData.substring(16);
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Latin1.parse(iv) }
);
console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));
encryptWithCryptoJS(plainText: string): string {
const key = CryptoJS.enc.Utf8.parse("hf8685nfhfhjs9h8");
const iv1 = CryptoJS.enc.Utf8.parse("hf8685nfhfhjs9h8");
const encrypted = CryptoJS.AES.encrypt(plainText, key, {
keySize: 16,
iv: iv1,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted + "";
}
decryptionWithCryptoJS(cipher: string): string {
const key = CryptoJS.enc.Utf8.parse("hf8685nfhfhjs9h8");
const iv1 = CryptoJS.enc.Utf8.parse("hf8685nfhfhjs9h8");
const plainText = CryptoJS.AES.decrypt(cipher, key, {
keySize: 16,
iv: iv1,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return plainText.toString(CryptoJS.enc.Utf8);
}
for the hex2a provided by another user, it may not working if ascii code is over 128 (i.e text contains chinese , etc)
you can use the follow to return proper unicode
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2){
var dec = parseInt(hex.substr(i, 2), 16);
character = String.fromCharCode(dec);
if (dec > 127)
character = "%"+hex.substr(i,2);
str += character;
}
return decodeURI(str);
}
var key = CryptoJS.enc.Utf8.parse('8080808080808080');
var iv = CryptoJS.enc.Utf8.parse('8080808080808080');
var _enid = CryptoJS.AES.decrypt(data, key,
{
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
return _enid;
I'm trying to reproduce the same hmacsha1 hash and base64 encoding from .net membership provider in a javascript function. I've tried using crypto-js and am getting different results. The .net code will hash "test" into "W477AMlLwwJQeAGlPZKiEILr8TA="
Here's the .net code
string password = "test";
HMACSHA1 hash = new HMACSHA1();
hash.Key = Encoding.Unicode.GetBytes(password);
string encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
And here's the javascript method I tried using crypto-js that does not produce the same output
var hash = CryptoJS.HmacSHA1("test", "");
var encodedPassword = CryptoJS.enc.Base64.stringify(hash);
How can I get my javascript hash to match the hash being generated from .net.
//not sure why crypt-js's utf16LE function doesn't give the same result
//words = CryptoJS.enc.Utf16LE.parse("test");
//utf16 = CryptoJS.enc.Utf16LE.stringify("test");
function str2rstr_utf16le(input) {
var output = [],
i = 0,
l = input.length;
for (; l > i; ++i) {
output[i] = String.fromCharCode(
input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF
);
}
return output.join('');
}
var pwd = str2rstr_utf16le("test");
var hash = CryptoJS.HmacSHA1(pwd, pwd);
var encodedPassword = CryptoJS.enc.Base64.stringify(hash);
alert(encodedPassword);
You don't specify a key in .NET:
var secretKey = "";
var password = "test";
var enc = Encoding.ASCII;
System.Security.Cryptography.HMACSHA1 hmac = new System.Security.Cryptography.HMACSHA1(enc.GetBytes(secretKey));
hmac.Initialize();
byte[] buffer = enc.GetBytes(password);
var encodedPassword = Convert.ToBase64String(hmac.ComputeHash(buffer));
Edit: as #Andreas mentioned, your problem is the encoding. So you just need to replace UTF by ANSI in your own code:
string password = "test";
System.Security.Cryptography.HMACSHA1 hash = new System.Security.Cryptography.HMACSHA1();
hash.Key = Encoding.ASCII.GetBytes("");
string encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.ASCII.GetBytes(password)));