Last active
February 6, 2025 19:07
-
-
Save CoderJava/5da28312758f8b4c4b4d9327ab838994 to your computer and use it in GitHub Desktop.
CryptoJS encryption decryption in Vue, PHP, and Dart based on this solution (https://stackoverflow.com/a/64330804/10308290)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
import 'dart:math'; | |
import 'dart:typed_data'; | |
import 'package:crypto/crypto.dart'; | |
import 'package:tuple/tuple.dart'; | |
import 'package:encrypt/encrypt.dart' as encrypt; | |
// Thanks to https://medium.com/@chingsuehok/cryptojs-aes-encryption-decryption-for-flutter-dart-7ca123bd7464 | |
class AES { | |
String encryptAESCryptoJS(String plainText, String passphrase) { | |
try { | |
final salt = genRandomWithNonZero(8); | |
var keyndIV = deriveKeyAndIV(passphrase, salt); | |
final key = encrypt.Key(keyndIV.item1); | |
final iv = encrypt.IV(keyndIV.item2); | |
final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); | |
final encrypted = encrypter.encrypt(json.encode(plainText), iv: iv); | |
return '{"ct":"${encrypted.base64}","iv":"${iv.base16}","s":"${bin2hex(salt)}"}'; | |
} catch (error) { | |
throw error; | |
} | |
} | |
String decryptAESCryptoJS(String jsonEncrypted, String passphrase) { | |
try { | |
final jsonData = json.decode(jsonEncrypted) as Map<String, dynamic>; | |
final salt = hex2bin(jsonData['s']); | |
final ct = base64.decode(jsonData['ct']); | |
final ivHex = hex2bin(jsonData['iv']); | |
final keyndIV = deriveKeyAndIV(passphrase, salt); | |
final key = encrypt.Key(keyndIV.item1); | |
final iv = encrypt.IV(ivHex); | |
final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); | |
final decrypted = encrypter.decrypt64(base64.encode(ct), iv: iv); | |
return json.decode(decrypted); | |
} catch (error) { | |
throw error; | |
} | |
} | |
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) { | |
var password = createUint8ListFromString(passphrase); | |
Uint8List concatenatedHashes = Uint8List(0); | |
Uint8List currentHash = Uint8List(0); | |
bool enoughBytesForKey = false; | |
Uint8List preHash = Uint8List(0); | |
while (!enoughBytesForKey) { | |
int preHashLength = currentHash.length + password.length + salt.length; | |
if (currentHash.length > 0) | |
preHash = Uint8List.fromList(currentHash + password + salt); | |
else | |
preHash = Uint8List.fromList(password + salt); | |
currentHash = Uint8List.fromList(md5.convert(preHash).bytes); | |
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash); | |
if (concatenatedHashes.length >= 48) enoughBytesForKey = true; | |
} | |
var keyBtyes = concatenatedHashes.sublist(0, 32); | |
var ivBtyes = concatenatedHashes.sublist(32, 48); | |
return new Tuple2(keyBtyes, ivBtyes); | |
} | |
Uint8List createUint8ListFromString(String s) { | |
var ret = new Uint8List(s.length); | |
for (var i = 0; i < s.length; i++) { | |
ret[i] = s.codeUnitAt(i); | |
} | |
return ret; | |
} | |
Uint8List genRandomWithNonZero(int seedLength) { | |
final random = Random.secure(); | |
const int randomMax = 245; | |
final Uint8List uint8list = Uint8List(seedLength); | |
for (int i = 0; i < seedLength; i++) { | |
uint8list[i] = random.nextInt(randomMax) + 1; | |
} | |
return uint8list; | |
} | |
String bin2hex(Uint8List bytes, {String? separator, int? wrap}) { | |
var len = 0; | |
final buf = StringBuffer(); | |
for (final b in bytes) { | |
final s = b.toRadixString(16); | |
if (buf.isNotEmpty && separator != null) { | |
buf.write(separator); | |
len += separator.length; | |
} | |
if (wrap != null && wrap < len + 2) { | |
buf.write('\n'); | |
len = 0; | |
} | |
buf.write('${(s.length == 1) ? '0' : ''}$s'); | |
len += 2; | |
} | |
return buf.toString(); | |
} | |
Uint8List hex2bin(String hexStr) { | |
if (hexStr.length % 2 != 0) { | |
throw const FormatException('not an even number of hexadecimal characters'); | |
} | |
final result = Uint8List(hexStr.length ~/ 2); | |
for (var i = 0; i < result.length; i++) { | |
result[i] = int.parse(hexStr.substring(2 * i, 2 * (i + 1)), radix: 16); | |
} | |
return result; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var CryptoJSAesJson = { | |
stringify: function (cipherParams) { | |
var j = {ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)}; | |
if (cipherParams.iv) j.iv = cipherParams.iv.toString(); | |
if (cipherParams.salt) j.s = cipherParams.salt.toString(); | |
return JSON.stringify(j); | |
}, | |
parse: function (jsonStr) { | |
var j = JSON.parse(jsonStr); | |
var cipherParams = CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Base64.parse(j.ct)}); | |
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv) | |
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s) | |
return cipherParams; | |
} | |
} | |
// <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script> | |
var key = "key123456"; | |
var encrypted = CryptoJS.AES.encrypt(JSON.stringify("plaintext"), key, {format: CryptoJSAesJson}).toString(); | |
console.log(encrypted); | |
var decrypted = JSON.parse(CryptoJS.AES.decrypt(encrypted, key, {format: CryptoJSAesJson}).toString(CryptoJS.enc.Utf8)); | |
console.log("decryyepted: "+decrypted); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
*-------------PHP code example----------------- | |
*/ | |
/** | |
* Decrypt data from a CryptoJS json encoding string | |
* | |
* @param mixed $passphrase | |
* @param mixed $jsonString | |
* @return mixed | |
*/ | |
function cryptoJsAesDecrypt($passphrase, $jsonString){ | |
$jsondata = json_decode($jsonString, true); | |
$salt = hex2bin($jsondata["s"]); | |
$ct = base64_decode($jsondata["ct"]); | |
$iv = hex2bin($jsondata["iv"]); | |
$concatedPassphrase = $passphrase.$salt; | |
$md5 = array(); | |
$md5[0] = md5($concatedPassphrase, true); | |
$result = $md5[0]; | |
for ($i = 1; $i < 3; $i++) { | |
$md5[$i] = md5($md5[$i - 1].$concatedPassphrase, true); | |
$result .= $md5[$i]; | |
} | |
$key = substr($result, 0, 32); | |
$data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv); | |
return json_decode($data, true); | |
} | |
/** | |
* Encrypt value to a cryptojs compatiable json encoding string | |
* | |
* @param mixed $passphrase | |
* @param mixed $value | |
* @return string | |
*/ | |
function cryptoJsAesEncrypt($passphrase, $value){ | |
$salt = openssl_random_pseudo_bytes(8); | |
$salted = ''; | |
$dx = ''; | |
while (strlen($salted) < 48) { | |
$dx = md5($dx.$passphrase.$salt, true); | |
$salted .= $dx; | |
} | |
$key = substr($salted, 0, 32); | |
$iv = substr($salted, 32,16); | |
$encrypted_data = openssl_encrypt(json_encode($value), 'aes-256-cbc', $key, true, $iv); | |
$data = array("ct" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt)); | |
return json_encode($data); | |
} | |
// Example | |
$key = "key123456"; | |
$encrypted = '{"ct":"XMW1sLzwrSv3b+aGyaT0UQ==","iv":"e4871f22e7fd444a01b624f8d27c0381","s":"aace984f071407b1"}'; | |
$decrypted = cryptoJsAesDecrypt($key, $encrypted); | |
echo "decrypted: $decrypted"; | |
?> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'aes.dart'; | |
void main(List<String> args) { | |
String ct = AES().encryptAESCryptoJS( | |
'plaintext', | |
'key123456', | |
); | |
print('ct: $ct'); | |
String pt = AES().decryptAESCryptoJS( | |
ct, | |
'key123456', | |
); | |
print('pt: $pt'); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: belajar_aes | |
description: A simple command-line application. | |
version: 1.0.0 | |
# homepage: https://www.example.com | |
environment: | |
sdk: '>=2.14.2 <3.0.0' | |
dependencies: | |
encrypt: ^5.0.1 | |
tuple: ^2.0.0 | |
dev_dependencies: | |
lints: ^1.0.0 |
Thanks its very much helpful to understand, it's very confusing to undestand Crypto js directly. I am trying to recreate in python but not able to get any luck.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is the output
Screen.Recording.2021-10-16.at.15.28.08.mov