I am trying to implement AES 256 bit encryption with string on php and JavaScript. For jasvascript I a musing CryptoJS and php I use openssl_decrypt/enecrypt.
Below is the code in JS for encryption and decryption.
JavaScript
function aes_encrypt(str_to_encrypt){
if(str_to_encrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
var encryptedString = encrypted.toString();
return encryptedString;
}
function aes_decrypt(str_to_decrypt){
if(str_to_decrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedString;
}
And in php the code is
PHP
class Crypto_AES256
{
public $key = "0123456789abcdef0123456789abcdef";
public $iv = "abcdef9876543210abcdef9876543210";
public $encrypt_method = 'AES-256-CBC';
function __construct ()
{
$this->key = hex2bin($this->key);
$this->iv = hex2bin($this->iv);
}
public function encrypt ( $string )
{
if ( $encrypted = base64_encode( openssl_encrypt ( $string, $this->encrypt_method, $this->key, 0, $this->iv ) ) )
{
return $encrypted;
}
else
{
return false;
}
}
public function decrypt ($string)
{
if ( $decrypted = openssl_decrypt ( base64_decode ( $string ), $this->encrypt_method, $this->key, 0, $this->iv ) )
{
return $decrypted;
}
else
{
return false;
}
}
}
But the result of encryption at JavaScript side is not same as php, I need to produce same encrypted and encrypted result at both JavaScript and php. What could be the problem.
Both codes differ in two ways:
The PHP code applies AES-256, but since only a 16 bytes key is used (because of the hex decoding), PHP automatically pads it with 0 values to a length of 32 bytes. In the CryptoJS code, the key length determines the mode, thus AES-128 is applied. So that both codes produce the same result, the key must be extended in the CryptoJS code analogously to the PHP code, or AES-128 must be used in the PHP code.
In the PHP code, openssl_encrypt() returns the ciphertext Base64 encoded by default, so the ciphertext is currently Base64 encoded twice. Therefore remove the explicit base64_encode() or use OPENSSL_RAW_DATA as the 4th parameter so that the raw data is returned. Similarly for openssl_decrypt().
When these issues are fixed, both codes provide the same ciphertext on my machine. Note that a static IV is insecure (see also the comment), but you probably only do this for testing purposes.
Example: The following code uses your unmodified CryptoJS code, i.e. AES-128:
function aes_encrypt(str_to_encrypt){
if(str_to_encrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var encrypted = CryptoJS.AES.encrypt(str_to_encrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv});
var encryptedString = encrypted.toString();
return encryptedString;
}
function aes_decrypt(str_to_decrypt){
if(str_to_decrypt==null)
return "";
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var decrypted = CryptoJS.AES.decrypt(str_to_decrypt,key, {'mode': CryptoJS.mode.CBC, iv: iv });
var decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedString;
}
var ciphertext = aes_encrypt('The quick brown fox jumps over the lazy dog');
var decrypted = aes_decrypt(ciphertext);
console.log(ciphertext.replace(/(.{56})/g,'$1\n'));
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
The PHP code returns the same ciphertext if AES-128-CBC is applied and if the OPENSSL_RAW_DATA flag is set as the 4th parameter in openssl_encrypt() and openssl_decrypt().
Related
I am trying to create a PHP equivalent to this JS code with CryptoJS:
function aesEncrypt (data) {
const key = 'GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=';
const iv = '\0';
const cipher = CryptoJS.AES.encrypt(data, CryptoJS.enc.Base64.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv), // parse the IV
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
})
return cipher.toString()
}
result of the js code : pHjpwiyKq7Rf4dFcBMbm1w==
here is the PHP code I wrote by reading other stackoverflow questions. But it did not return the same result.
$plaintext = "plainText";
$method = 'aes-256-cbc';
$key = base64_encode("GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=");
$iv = hex2bin('00000000000000000000000000000000');
$ciphertext = openssl_encrypt(
$plaintext,
$method,
$key,
OPENSSL_RAW_DATA,
$iv
);
$ciphertext = base64_encode($ciphertext);
echo $ciphertext;
result of the PHP code : +YJOMi2vISmEXIjUZls3MA==
In the PHP code, the key must be Base64 decoded and not Base64 encoded:
$key = base64_decode("GSTEGSTEjdfheyhdHSHSHSHDHHDHmdjjdn12ndndn5r=");
With this change the desired ciphertext is created.
Note that the ciphertext is Base64 encoded by default if 0 is passed instead of OPENSSL_RAW_DATA in the fourth parameter of the openssl_encrypt() call. The explicit Base64 encoding of the ciphertext is then not necessary.
Keep in mind that a static IV is insecure. Usually during encryption a random IV is generated, which is passed to the decrypting side along with the ciphertext (typically concatenated).
I am encrypting a text with AES256 in swift language and outputting it as hex. I want to decrypt this code I received with JS, but I could not reach the result. I tried the CryptoJS library but still couldn't get the result I wanted. All I want is the js code that will give me the decoded version when I enter the IV, password and ciphertext.
const crypto = require("crypto");
var key = "";
const iv = "";
const token = "";
function decrypt(token, iv, key) {
const decrypter = crypto.createDecipheriv("aes-256-cbc", key, iv);
let decrypted = decrypter.update(token, "hex", "utf8");
decrypted += decrypter.final("utf8");
return decrypted
}
console.log(decrypt(token, iv, key));
With the Node.js code above, I achieve what I want, but I want to do it with normal JS code, not using node. I don't want to mess with the server. I would be very happy if you help.
EDIT:
I am using CryptoSwift library in Swift language.
func encryption(uuid: String, token: String) -> String {
do {
let aes = try AES(key: String(uuid.prefix(32)), iv: String(uuid.prefix(16)))
let ciphertext = try aes.encrypt(Array(token.utf8))
let encrypttext = ciphertext.toHexString()
return encrypttext
}
catch {
return "error"
}
}
I tried to do something with CryptoJS with the codes from the site below, but it didn't work like the codes in Node.js.
https://embed.plnkr.co/0VPU1zmmWC5wmTKPKnhg/
EDIT2:
I've been trying different things but couldn't quite figure it out. I get an error when I add PBKDF2. I don't fully understand the problem.
var password = "6268890F-9B58-484C-8CDC-34F9C6A9";
var iv = "6268890F-9B58-48";
var cipher = "79a247e48ac27ed33ca3f1919067fa64";
/*
var key = CryptoJS.PBKDF2(password, {
keySize: 32
});
*/
var dec= CryptoJS.enc.Hex.parse(cipher);
const decrypted = CryptoJS.AES.decrypt({
ciphertext: dec
},
password, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js"></script>
CryptoJS uses WordArrays, so that key, IV and ciphertext have to be converted accordingly. For this purpose the appropriate encoders have to be applied. Furthermore decrypt() expects the ciphertext as CipherParams object.
This results in the following possible CryptoJS implementation:
var ciphertext = "79a247e48ac27ed33ca3f1919067fa64";
var key = "6268890F-9B58-484C-8CDC-34F9C6A9";
var iv = "6268890F-9B58-48";
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)); // Apple
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
which is functionally identical to the posted NodeJS code that also successfully decrypts the test data.
Regarding the question asked in the comment about the encodings:
In general, the decryption side must have knowledge of the encodings used for encryption. However, in this case the encodings can be derived from the posted NodeJS code:
For decryption, the input encoding of the ciphertext is specified as 'hex', see decipher.update().
key and iv are defined as strings which are UTF-8 encoded, see crypto.createDecipheriv().
Also, the data used is consistent with these conclusions.
Note that for security reasons a static IV may not be used. Instead, a random IV must be generated for each encryption.
Also, no password may be applied as key, even if it has the right length. If a password is to be used, a key derivation is necessary, e.g. with PBKDF2.
For test purposes, the data is of course enough.
I have an application in which I am encrypting a json encoded array using AES CBC 128 algorithm and then Decrypting it in javascript(React/Next Js Project). My Encryption in php is as shown in the below code
ENCRYPTION PHP
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
I am facing problems in decryption of this in Javascript
my code so far is as shown below
const baseenc = CryptoJS.enc.Base64.parse(cipher).toString();
var encrypted = CryptoJS.AES.decrypt(cipher, key, { iv: iv }).toString();
var plaintext = CryptoJS.enc.Latin1.stringify(encrypted);
Can any body please show what is the error or help me in getting the correct output
The following steps must be implemented in the CryptoJS code:
Separate IV, HMAC and ciphertext (after Base64 decoding)
Calculate the HMAC for the ciphertext
Check the authenticity of the ciphertext. The ciphertext is authentic if the received and calculated HMAC are identical.
Perform decryption, only if the ciphertext is authentic
The following code is a possible implementation. As key 0123456789012345 was applied and with the PHP code the used ciphertext was generated:
var ciphertext = 'WqfMfCxKg7U7h5S1mbx7mSHOkkkIrUUpg++mX4ZdWt0I26VfKn7bsi60Oo/SIsWQGyC4dF5z081NvjTXwZGjIpguA0k/QqIM/GDEpCojaro=';
var key = '0123456789012345';
// Convert key and ciphertext into WordArrays
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext);
var keyWA = CryptoJS.enc.Utf8.parse(key);
// Separate IV, HMAC and ciphertext
var ivWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(0, 4));
var hmacWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4, 4 + 8));
var actualCiphertextWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(4 + 8));
// Authenticate
var hmacCalculatedWA = CryptoJS.HmacSHA256(actualCiphertextWA, keyWA);
if(CryptoJS.enc.Base64.stringify(hmacCalculatedWA) === CryptoJS.enc.Base64.stringify(hmacWA)) {
// Decrypt if authentication is successfull
var decryptedMessageWA = CryptoJS.AES.decrypt({ciphertext: actualCiphertextWA}, keyWA, {iv: ivWA});
var decryptedMessage = CryptoJS.enc.Utf8.stringify(decryptedMessageWA);
console.log(decryptedMessage);
} else {
console.log('Authentication failed!');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
Please note that it is better to use different keys for encryption and authentication, see here.
I encrypt and decrypt data using PHP like this:
<?php
function encrypt($data, $secret){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return base64_encode($iv.openssl_encrypt($data, 'aes-256-cbc', $secret, 0, $iv));
}
function decrypt($encryptedData, $secret){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$data = base64_decode($encryptedData);
$iv = substr($data, 0, $iv_size);
return openssl_decrypt(substr($data, $iv_size), 'aes-256-cbc', $secret, 0, $iv);
}
?>
I am now wanting to be able to encrypt my data locally (identically to the PHP method) using Crypto-JS. I have done the same as above to get the key and iv:
var key = '<?php echo $secret;?>';
var iv = '<?php echo base64_encode(mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND));?>';
Now when using Crypto-JS I have tried to encrypt using:
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(text), CryptoJS.enc.Hex.parse(key), { iv: CryptoJS.enc.Hex.parse(iv) });
But I also need to store the IV like I do with PHP So I have added:
var withIV = iv+encrypted;
but that is not encoded. So I have added:
CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(withIV));
But this is not the same encoding as the PHP above for some reason?
Here's how I encrypt data with CryptoJS:
function encrypt(str, key, iv) {
var key = CryptoJS.enc.Hex.parse(key);
var iv = CryptoJS.enc.Hex.parse(iv);
return CryptoJS.AES.encrypt(str, key, { iv: iv }).toString();
};
In PHP, I decrypt the encrypted string produced by that function using this line of code:
openssl_decrypt($encrypted_data_string, "AES-128-CBC", hex2bin($key_hex_string), 0, hex2bin($iv_hex_string));
I suppose you could encode/decode the encrypted data in base 64 instead of hexadecimal if you wanted to. Anyway, hope this helps!
It seems you have trouble concatenating IV and the ciphertext in CryptoJS. This is rather easy, because CryptoJS' native binary data format (WordArray) supports the concat function:
var ivWords = CryptoJS.enc.Hex.parse(iv); // WordArray instance
var plaintext = CryptoJS.enc.Utf8.parse(text); // WordArray instance
var keyWords = CryptoJS.enc.Hex.parse(key); // WordArray instance
var encrypted = CryptoJS.AES.encrypt(plaintext, keyWords, { iv: ivWords }); // CipherParams instance
var ct = ivWords.clone().concat(encrypted.ciphertext); // WordArray instance
var ct = ct.toString(CryptoJS.enc.Base64); // string instance
console.log(ct);
// example data
var iv = "0102030405060708090a0b0c0d0e0f";
var text = "text";
var key = "1112131415161718191a1b1c1d1e1f";
// actual code
var ivWords = CryptoJS.enc.Hex.parse(iv); // WordArray instance
var plaintext = CryptoJS.enc.Utf8.parse(text); // WordArray instance
var keyWords = CryptoJS.enc.Hex.parse(key); // WordArray instance
var encrypted = CryptoJS.AES.encrypt(plaintext, keyWords, { iv: ivWords }); // CipherParams instance
var ct = ivWords.clone().concat(encrypted.ciphertext); // WordArray instance
var ct = ct.toString(CryptoJS.enc.Base64); // string instance
output.innerHTML = ct;
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/components/enc-base64-min.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
<div id="output"></div>
This would produce the same output as
base64_encode($iv.openssl_encrypt($data, 'aes-256-cbc', $secret, 0, $iv));
as long as iv is actually hex-encoded string with that contains the same bytes as the decoded version in $iv. The same must be true for text (except for the encoding), key and $data, $secret, respectively.
I am trying to Encrypt a string using AES 128bit encryption. I have code for both Javascript and C#. The main objective is to encrypt the string using Javascript CryptoJS and then take the resultant cipher text and Decrypt it using C# AES AesCryptoServiceProvider.
Javascript Code:
function EncryptText()
{
var text = document.getElementById('textbox').value;
var Key = CryptoJS.enc.Hex.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0=");
var IV = CryptoJS.enc.Hex.parse("YWlFLVEZZUFNaWl=");
var encryptedText = CryptoJS.AES.encrypt(text, Key, {iv: IV, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});
//var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
var encrypted = document.getElementById('encrypted');
encrypted.value = encryptedText;
}
C# Code:
private String AES_decrypt(string encrypted)
{
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Pkcs7;
aes.Key = Key;
aes.IV = IV;
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
return System.Text.ASCIIEncoding.ASCII.GetString(secret);
}
When using "hello" as the plain text for javascript i get this ciphertext:
uqhe5ya+mISuK4uc1WxxeQ==
When passing that into the C# application, upon running the Decrypt method i recieve:
Padding is invalid and cannot be removed.
I am stumped here and have tried many solutions resulting in the same error.
When encrypting hello through the C# encryption AES method I receive:
Y9nb8DrV73+rmmYRUcJiOg==
I thank you for your help in advance!
javascript code :
function EncryptText()
{
var text = CryptoJS.enc.Utf8.parse(document.getElementById('textbox').value);
var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); //secret key
var IV = CryptoJS.enc.Utf8.parse("2314345645678765"); //16 digit
var encryptedText = CryptoJS.AES.encrypt(text, Key, {keySize: 128 / 8,iv: IV, mode: CryptoJS.mode.CBC, padding:CryptoJS.pad.Pkcs7});
var encrypted = document.getElementById('encrypted');
encrypted.value = encryptedText;
//Pass encryptedText through service
}
C# code :
private String AES_decrypt(string encrypted,String secretKey,String initVec)
{
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
//aes.BlockSize = 128; Not Required
//aes.KeySize = 256; Not Required
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Pkcs7;
aes.Key = Encoding.UTF8.GetBytes(secretKey);PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0=
aes.IV = Encoding.UTF8.GetBytes(initVec); //2314345645678765
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
return System.Text.ASCIIEncoding.ASCII.GetString(secret);
}
Used above code working fine !!!
Try using var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); instead of HEX.
Because actually the string you are putting in your key (and IV) and parsing is not a hex string. hex is 0 to F.
First, your Key variable in JS contains a string with 32 characters (after the odd-looking parse call). Although this might be interpreted as a 128-bit key, there is a certain chance that CryptoJS takes it as a pass phrase instead (and generates a key from it using some algorithm). So your actual key looks quite different. The string also looks suspiciously like hex-encoded, so there might be some additional confusion about its C# value. You have to make sure that you are using the same key in JS and C#.
Second, the IV variable also, after parsing, looks like a hex-encoded value. So you have to be careful what value you are using on the C# side as well.
FYI, here are the values for Key and IV after parsing:
Key = 00000000000e00000d000c0000010000,
IV = 0000000e000f0a00
Thank you "Uwe" parsing with UTF8 solved everything.
What happens if you use: var Key = CryptoJS.enc.Utf8.parse("PSVJQRk9QTEpNVU1DWUZCRVFGV1VVT0="); instead >of HEX? And what is your Key and IV in C#? Because actually the string you are putting in your key and >parsing is not a hex string. hex is 0 to F
Thank you so much!