0

On web, I'm using CryptoJS for decrypto JS:

CryptoJS.AES.decrypt(inputBase64, key).toString(CryptoJS.enc.Utf8);

Example:

input: "tzfwnxVwE/qNoaWRRfqLp11ZyhB4UtKO+0/Lvv5B7eE=" key: "20190225165436_15230006321670000_15510884759030000"

On flutter I can't find any library to decrypt with a key of any length.

I know "For AES, NIST selected three members of the Rijndael family, each with a block size of 128 bits, but three different key lengths: 128, 192 and 256 bits. "

But I don't know how to convert any length key to 128 bit format?

Amit Jangid
  • 2,741
  • 1
  • 22
  • 28
Nguyen Phạm
  • 45
  • 1
  • 2
  • 7
  • Possible duplicate of [How is an AES key processed when calling CryptoJS.AES.encrypt/decrypt with a non-standard key length?](https://stackoverflow.com/questions/18049802/how-is-an-aes-key-processed-when-calling-cryptojs-aes-encrypt-decrypt-with-a-non) – Richard Heap Feb 25 '19 at 15:28
  • Please refer to below post for solution https://stackoverflow.com/a/60648119/13049237 – Ching Sue Hok Mar 12 '20 at 10:05

3 Answers3

2
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;

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(plainText, iv: iv);
    Uint8List encryptedBytesWithSalt = Uint8List.fromList(
        createUint8ListFromString("Salted__") + salt + encrypted.bytes);
    return base64.encode(encryptedBytesWithSalt);
} catch (error) {
    throw error;
}
}

String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
    Uint8List encryptedBytesWithSalt = base64.decode(encrypted);

    Uint8List encryptedBytes =
        encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
    final salt = encryptedBytesWithSalt.sublist(8, 16);
    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 decrypted =
        encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
    return 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 = 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;
}

Usage

import 'package:app/utils/cryptojs_aes_encryption_helper.dart'; String plainText = 'PlainText is Me'; var encrypted = encryptAESCryptoJS(plainText, "password"); var decrypted = decryptAESCryptoJS(encrypted, "password");

Ching Sue Hok
  • 101
  • 1
  • 4
  • 2
    This is no longer working in Flutter **sdk: ">=2.12.0 <3.0.0"** **Affected Line:** currentHash = md5.convert(preHash).bytes; **Error:** A value of type 'List*' can't be assigned to a variable of type 'Uint8List' – Lefty Aug 04 '21 at 12:16
  • 1
    currentHash = Uint8List.fromList(md5.convert(preHash).bytes); to fix the above mentioned issue – NirmalCode Mar 01 '22 at 05:10
1

When you pass CryptoJS a string as the key it treats it as a passphrase and generates the key from it using a key derivation function - in this case PBKDF2. It generates a 256 bit key and a 128 bit initialization vector (IV). It then uses those for the encryption/decryption. You also need to find out what chaining method CryptoJS uses (probably cipher block chaining (CBC)) and what padding method it uses (to make sure that the plain text is a round number of 128 bit blocks - probably PKCS#7).

CryptoJS has this "works out of the box" mode, but it isn't particularly clear what it's doing under the hood - you'd need to read the source code or scour the documentation.

When trying to inter-operate between two systems/languages it's best if you remain in charge of things, rather than letting one end make arbitrary decisions. That way you can make sure that you have the settings the same at each end.

So, you might choose to:

  • Use PBKDF2 to generate a 128 bit key and 128 bit IV from the string passphrase - with, say, 9999 rounds
  • Use PKCS#7 padding
  • Use AES in CBC mode

The pointycastle package supports all the above in Dart. It looks like CryptoJS supports all of those too.

Start with a passphrase and make sure you can generate the same key and IV in JS and Dart. Then move onto creating the ciphers.

Remember, too, never to encrypt two messages with the same key/IV pair. Use a message sequence number, for example, to slightly change the IV for each message.

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
0

Sample encrypt using nodejs script:

var key = CryptoJS.PBKDF2("123456", "123456", {
  keySize: 256 / 32
});
var iv = CryptoJS.PBKDF2("123456", "123456", {
  keySize: 128 / 32
});

var encrypted = CryptoJS.AES.encrypt('my message', key, { iv: iv,
  mode: CryptoJS.mode.CBC,
  padding: CryptoJS.pad.Pkcs7
}).toString();

decript using dart


import 'package:encrypt/encrypt.dart' as aes;
import 'package:crypto/crypto.dart';
import 'package:hex/hex.dart';
import 'package:password_hash/pbkdf2.dart';

void main(List<String> arguments) {
  String encrypted = 'HbsmGAigiIWmU3MNZAf8+w==';

  final generator = PBKDF2(hashAlgorithm: sha1);
  final key = aes.Key.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 32)));
  final iv = aes.IV.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 16)));

  final encrypter = aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));

  final decrypted = encrypter.decrypt64(encrypted, iv:iv);
  print(decrypted);
}