I have to decode a rijndael 128 string
the string can be successfully using this online tool http://www.tools4noobs.com/online_tools/decrypt/ with theses parameters :
Algorythm rijndael 128
Mode : CBC
Decode the output using base64
I have to decode this using node.js and crypto module
Here is my code
function Token(TokenBase64 )
{
var crypto = require('crypto');
this.TokenToCheck = new Buffer(TokenBase64,'base64').toString();
this.GameKey = 'xxxxxxxxxxxxxxxxx';
var cryptKey = crypto.createHash('sha256').update(this.GameKey).digest()
this.decipher = crypto.createDecipheriv('aes-128-cbc', cryptKey, '12345678901234561234567890123456');
var dec = this.decipher.update( this.TokenToCheck);
dec += this.decipher.final();
return dec;
}
module.exports = Token;
The error output by this code when called is :
Error: DecipherInitIv error at new Decipheriv (crypto.js:360:17) at Object.Decipheriv (crypto.js:357:12) at new Token
The size of your IV is 32 characters (which will probably be used as 32 bytes). AES always uses a 128 bit block size and the IV for CBC is always the size of a single block. So you've got 16 characters too many.
Related
As a newbie to Cryptography, I'm trying to reproduce the same default behavior of the AES256TextEncryptor Class of the jasypt-library with the CrpytoJS library. This is my Java method, that basically takes in two arguments - the message that I want to encrypt as well as my secret paraphrase:
private String encryptWithAes256(String messageToBeEncrypted, String encryptorSecret) {
AES256TextEncryptor encryptor = new AES256TextEncryptor();
encryptor.setPassword(encryptorSecret);
return encryptor.encrypt(messageToBeEncrypted);
}
When encrypting the messageToBeEncrypted with this code, the resulting encrypted message is fine. What I found out is that the AES256TextEncryptor, which internally uses the StandardPBEStringEncryptor as a encryptor, seems to use the PBEWithHMACSHA512AndAES_256 algorithm as a default.
How can I reproduce the same encryption behavior with CrpytoJS? When I'm trying to encrypt the message with CryptoJS in the way it's documented here, the result is totally different from what I expect it to be.
Based on Topaco's comment, I came up with the following JavaScript Code to mimic the Java code:
function encryptWithAes256(messageToEncrypt, encryptorKey){
// Generate random 16 bytes salt
var salt = CryptoJS.lib.WordArray.random(128/8);
// Derive key
var key = CryptoJS.PBKDF2(encryptorKey, salt, { keySize: 256/32, iterations: 1000 });
console.log("derived key: " + key);
// Generate random 16 bytes init vector (iv)
var iv = CryptoJS.lib.WordArray.random(128/8);
var cipherText = CryptoJS.AES.encrypt(messageToEncrypt, key, {iv: iv});
console.log("aes encrypted text: "+ salt.toString() + iv.toString() + cipherText.toString());
}
The generated result still seems not be as expected though, as it's length is 88 characters, whereas the Java code generates a 64 character long encrypted message.
The posted code is close to the required result. The following still needs to be corrected:
PBKDF2 applies SHA1 by default, which means SHA512 must be explicitly specified.
The concatenation must be done on a binary level and not with the hex and Base64 encoded data.
If this is fixed, a possible implementation is:
function encryptWithAes256(messageToEncrypt, encryptorKey){
// Generate random 16 bytes salt
var salt = CryptoJS.lib.WordArray.random(128/8);
// Derive key
var key = CryptoJS.PBKDF2(
encryptorKey,
salt,
{ keySize: 256/32, iterations: 1000, hasher: CryptoJS.algo.SHA512 } // Apply SHA512
);
console.log("derived key:\n" + key);
// Generate random 16 bytes init vector (iv)
var iv = CryptoJS.lib.WordArray.random(128/8);
// Encrypt
var cipherText = CryptoJS.AES.encrypt(messageToEncrypt, key, {iv: iv});
// Concatenate
var encryptedData = salt.clone().concat(iv).concat(cipherText.ciphertext); // Concatenate on binary level
var encryptedDataB64 = encryptedData.toString(CryptoJS.enc.Base64); // Base64 encode the result
console.log("aes encrypted text:\n", encryptedDataB64.replace(/(.{56})/g,'$1\n'));
}
encryptWithAes256('The quick brown fox jumps over the lazy dog', 'my passphrase');
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Since because of the random salt and IV always different data is generated, a test of the implementation is not possible by comparing the data. Instead, it must be checked whether the data generated with the CryptoJS code is decryptable with the Jasypt counterpart for decryption:
private static String decryptWithAes256(String ciphertextToBeDecrypted, String encryptorSecret) {
AES256TextEncryptor encryptor = new AES256TextEncryptor();
encryptor.setPassword(encryptorSecret);
return encryptor.decrypt(ciphertextToBeDecrypted);
}
which is indeed the case with the above CryptoJS implementation.
I am using CryptoJS in my angular app to implement AES encryption but I am keep getting TypeError: Cannot read property '0' of undefined error when I try to send empty 16 byte array in IV
Here's my typescript code:
aesEncrypt(keys: string, value: string) { // encrypt api request parameter with aes secretkey
var key = CryptoJS.enc.Utf8.parse(keys);
//var iv = CryptoJS.enc.Utf8.parse(keys);
var iv = new Uint16Array(16);
var encrypted = CryptoJS.AES.encrypt(JSON.stringify(value), key,
{
//keySize: 256,
keySize: 128,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
}
But same thing works fine in .NET, android, ios when I send empty 16 byte array in IV
.NET code:
private static AesCryptoServiceProvider AesCryptoServiceProvider(string key)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.UTF8.GetBytes(key);
//aes.IV = Encoding.UTF8.GetBytes(key);
aes.IV = new byte[16];
return aes;
}
android code:
public static String encryptURLEncoding(byte[] key, String encryption) throws GeneralSecurityException
{
if (key.length != 16)
{
throw new IllegalArgumentException("Invalid key size.");
}
// Setup AES tool.
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
byte[] dstBuff = cipher.doFinal(encryption.getBytes());
String encryptedStringData = android.util.Base64.encodeToString(dstBuff, android.util.Base64.DEFAULT);
return encryptedStringData;
}
I want to implement AES encrypt decrypt by providing empty 16 byte array because this app is interconnected with my other apps which are on android, ios platform with same encryption setup but I am getting error in my angular app, How can I resolve this issue?
In the JavaScript code the IV must be passed as WordArray. Since a 0-IV was used in the C# code, this must also be done in the JavaScript code. The corresponding WordArray could be e.g.
var iv = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");
Note that the Hex encoder is used so that the 16 bytes 0-IV correspond to 32 0-values.
Also be aware that generally a 0-IV should only be used for testing purposes. In practice, for security reasons, a random IV has to be generated for each encryption. Additionally, a key / IV pair may only be used once.
Furthermore CryptoJS does not know the parameter keySize and ignores it. The used AES variant is determined by the key size, e.g. for a 32 bytes key AES-256 is applied, here.
For those who faced a similar error in typescript for encrypting a string, you just need to import the package this way
import * as Crypto from 'crypto-js';
I faced the same problem.
Including the iv option solved it.
var iv = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f"); var ciphertext = CryptoJS.AES.encrypt("msg", CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f"),{ iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.AnsiX923 });
I using online tools for encrpyt using AES. I'm using my module to decrypt. But I get not the same results. Why?
I used one of those tool for encrypt:
https://www.browserling.com/tools/aes-encrypt
https://www.tools4noobs.com/online_tools/encrypt/
This is the data I provided:
This is the text to encrpyt: Hello World
This is the password:12345
This is the result of the tool: U2FsdGVkX19HLG+YDMe3kYl+MYwEMlnC5mK78s3rZZg=
Now I try to decrypt it using node, and it is not giving me same results. But everything works welll
var crypto = require('crypto'),
algorithm = 'aes-128-ctr',
password = '12345';
module.exports.decrypt=function(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'base64','utf8')
dec += decipher.final('utf8');
return dec;
}
text=module.exports.decrypt('U2FsdGVkX1+OOp0KE3lv6qcKQeS/JDFPF8YhgdU131o=')
text
I tried to change to AES-192, and got the same issue.
Update: (Based on zaph response)
This is the new data I entered here: https://www.tools4noobs.com/online_tools/encrypt/
key: 0123456789abcdef (16 bytes) Rijndael-128. mode: CBC. encode:hex.
This is the result: 8b25e846b6a2d52ad87f38f8134906c3
I cannot decrypt it. Here is my code:
var crypto = require('crypto'),
algorithm = 'aes-128-cbc',
password = '0123456789abcdef';
module.exports.decrypt=function(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
if(!module.parent){
var text=module.exports.decrypt('8b25e846b6a2d52ad87f38f8134906c3')
console.log(text)
}
Use an encryption key that is an exact length in order to avoid non-standard padding (there is not standard on handling keys of incorrect lengths). AES supports 128 192 and 256 bit length passwords (16, 24 and 32 bytes). Obviously 12345 does not meet the supported password lengths.
It is generally best not to use CTR mode, is is very easy to get it wrong. The issue is that the same key and counter must never be reused. Generally CBC mode is used with a random IV and PKCS#7 padding is used to accommodate input that is not a multiple of the block size.
The output U2FsdGVkX19HLG+YDMe3kYl+MYwEMlnC5mK78s3rZZg= is Base encoded 32 bytes, twice the block size, so there is something else than the encrypted data in the output.
Entering the password and text into https://www.tools4noobs.com/online_tools/encrypt/ in Rijndael-128 (which is AES) in CTR mode produces 53TI1is8kfYkztQ=, not the resulty in the question. Note that this tool used mcrypt that only supports non-standard padding.
This is my final code for encrypt and decrypt in AES-256 in NodeJS. Using IV, and key (password).
var crypto = require('crypto')
var algorithm = 'aes-128-cbc'
var key = 'AABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
var iv = 'AABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
key=new Buffer(key,'hex')
iv=new Buffer(iv,'hex')
module.exports.encrypt=function(text){
var cipher = crypto.createCipheriv(algorithm,key,iv)
text=new Buffer(text)
var crypted = cipher.update(text,'utf-8','base64')
crypted += cipher.final('base64');
return crypted;
}
module.exports.decrypt=function(text){
var decipher = crypto.createDecipheriv(algorithm,key,iv)
dec = decipher.update(text,'base64','utf-8');
dec += decipher.final();
return dec;
}
if(!module.parent){
var enc=module.exports.encrypt('Exaxmple of encoding')
console.log(enc)
var dec=module.exports.decrypt(enc)
console.log(dec)
}
I have a PHP code that encrypt in AES-128 an ip address:
$ip = "MY_IP";
$secret = "MY_KEY";
$ip = #mcrypt_ecb(MCRYPT_RIJNDAEL_128, $secret, $ip, MCRYPT_ENCRYPT);
$encrypted = bin2hex($ip); // encrypted: 2854edb405cb7230ba1f4b87acddba8a
What I need to do is to have the same piece of code but using javascript/node.js. I've searched in the crypto node.js native module but I wasn't able to reproduce the same result:
var crypto = require('crypto');
var ip = "MY_IP";
var secret = "MY_KEY";
var cipher = crypto.createCipher("AES-128-ECB", secret);
var encrypted = cipher.update(ip, 'utf8', 'hex');
encrypted += cipher.final('hex'); // encrypted: e84c06888696edda0139e98fc2c0a8cc
Does someone have an idea ?
I've posted too quickly, found the solution:
$> npm install mcrypt
And then the code:
var MCrypt = require('mcrypt').MCrypt;
var ip = "MY_IP";
var secret = "MY_KEY"
var desEcb = new MCrypt('rijndael-128', 'ecb');
desEcb.open(secret);
var cipherText = desEcb.encrypt(ip); // cipherText: 2854edb405cb7230ba1f4b87acddba8a
MCrypt github for more encryption tool: https://github.com/tugrul/node-mcrypt
The problem here is that there are some things that PHP's mcrypt extension (and node's createCipher()) does behind the scenes that you're probably not aware of.
First off, createCipher() accepts a 'password' that is hashed with MD5 to derive the actual key. So instead what you should be using is createCipheriv(), which allows you to pass the key (and IV) directly, like PHP's mcrypt accepts. In ECB mode, IVs are ignored, so you can just pass in an empty string for the IV parameter.
Secondly, PHP's mcrypt will magically pad both your input and your key with null bytes if they are less than the cipher's block size and key size, respectively.
So for AES-128-ECB, we need to make sure the input and key lengths are a multiple of 16 bytes. With all of this knowledge we then find that appropriate code for the built-in crypto module might look something like:
var crypto = require('crypto');
function makePadded(str) {
var buf;
var len = str.length;
if (str.length % 16)
len = str.length + (16 - str.length % 16);
buf = new Buffer(len);
buf.fill(0);
buf.write(str, 0, str.length);
return buf;
}
var ip = makePadded('MY_IP');
var secret = makePadded('MY_KEY');
var cipher = crypto.createCipheriv("AES-128-ECB", secret, '');
var encrypted = cipher.update(ip, 'binary', 'hex');
encrypted += cipher.final('hex');
// Slice off at 16 bytes to match the input length
encrypted = encrypted.slice(0, 32);
console.log(encrypted);
One last thing that might be worth mentioning is that MCRYPT_RIJNDAEL_128 in PHP can be used to do 128, 192, or 256 bit encryption. So in PHP if 0 < keylen <= 16 then 128-bit encryption will be used, 192-bit encryption will be used if 16 < keylen <= 24 and 256-bit encryption will be used if 24 < keylen <= 32. However in node, you will need to adjust the cipher name appropriately, as node does not do the kind of "automatic adjustment" that PHP does.
in nodejs - password must be a 'binary' encoded string or a buffer. in PHP, the deprecated #mcrypt_ecb expects a key to be a string
I am trying to figure out the Node.js Crypto library and how to use it properly for my situation.
My Goal is:
key in hex string 3132333435363738313233343536373831323334353637383132333435363738
text in hex string 46303030303030303030303030303030
ciphered text in hex string 70ab7387a6a94098510bf0a6d972aabe
I am testing this through a c implementation of AES 256 and through a website at http://www.hanewin.net/encrypt/aes/aes-test.htm
This is what I have to far, it's not working the way I would expect it to work. My best guess is that the input and output types are incorrect for the cipher function. The only one that works is utf8 if I use hex it fails with a v8 error. Any ideas on what I should convert or change to get it to work.
var keytext = "3132333435363738313233343536373831323334353637383132333435363738";
var key = new Buffer(keytext, 'hex');
var crypto = require("crypto")
var cipher = crypto.createCipher('aes-256-cbc',key,'hex');
var decipher = crypto.createDecipher('aes-256-cbc',key,'hex');
var text = "46303030303030303030303030303030";
var buff = new Buffer(text, 'hex');
console.log(buff)
var crypted = cipher.update(buff,'hex','hex')
The output in crypted in this example is 8cfdcda0a4ea07795945541e4d8c7e35 which is not what I would expect.
Your code is using aes-256-cbc when the website you are deriving test vectors from is using ecb mode. Also, you are calling createCipher, but with ECB you should use createCipheriv with no IV (see nodeJS: can't get crypto module to give me the right AES cipher outcome),
Here is some code that demonstrates this:
var crypto = require("crypto");
var testVector = { plaintext : "46303030303030303030303030303030",
iv : "",
key : "3132333435363738313233343536373831323334353637383132333435363738",
ciphertext : "70ab7387a6a94098510bf0a6d972aabe"};
var key = new Buffer(testVector.key, "hex");
var text = new Buffer(testVector.plaintext, "hex");
var cipher = crypto.createCipheriv("aes-256-ecb", key, testVector.iv);
var crypted = cipher.update(text,'hex','hex');
crypted += cipher.final("hex");
console.log("> " + crypted);
console.log("? " + testVector.ciphertext);
The output of running that code is not exactly what I expect, but the first block of the encrypted output matches your expectation. Probably another parameter that needs to be tweaked.:
$ node test-aes-ecb.js
> 70ab7387a6a94098510bf0a6d972aabeeebbdaed7324ec4bc70d1c0343337233
? 70ab7387a6a94098510bf0a6d972aabe