I have NestJS api which crypt and decrypt string with next code
export const encryptIV = async (data:string)=>{
try{
const iv = Buffer.from('q2Q@w3W#').toString('hex');
const secretKey = 'H4WtkvK4qyehIe2kjQfH7we1xIHFK67e';
const key = (await promisify(scrypt)(secretKey, 'salt', 32)) as Buffer;
const cipher = createCipheriv(alg, key, iv);
let encryptedText = cipher.update(data,'utf-8','hex');
encryptedText += cipher.final('hex');
return `${encryptedText}`;
} catch (e){
return '';
}
}
export const decryptIV = async (data:string)=>{
try{
if (data.length>0){
const iv = Buffer.from('q2Q@w3W#').toString('hex');
const secretKey = 'H4WtkvK4qyehIe2kjQfH7we1xIHFK67e';
const key = (await promisify(scrypt)(secretKey, 'salt', 32)) as Buffer;
const decipher = createDecipheriv(alg, key, iv);
let decryptedText = decipher.update(data,'hex','utf-8');
decryptedText += decipher.final('utf-8');
return decryptedText;
} else {
return '';
}
} catch (e){
return '';
}
}
Decryption in JS:
IV="7132514077335723"
SecretKey="H4WtkvK4qyehIe2kjQfH7we1xIHFK67e"
EncryptedData="222ef6e4c66c3dfdea9cf1f4828f436e"
AfterDecryption="28c9c0e555b71fda"
ALG="aes-256-ctr"
I'm trying decrypt my encryptedData in dart using "encrypt: ^5.0.3" package with next code:
void decrypt() {
try{
final hexIv = "222ef6e4c66c3dfdea9cf1f4828f436e";
final ivHexIv = enc.IV.fromUtf8("7132514077335723");
final ivKey = enc.Key.fromUtf8('H4WtkvK4qyehIe2kjQfH7we1xIHFK67e');
final enc.Encrypter encrypterIv = enc.Encrypter(enc.AES(ivKey,mode: enc.AESMode.ctr));
final String decrypted =encrypterIv.decrypt64(hexIv, iv: ivHexIv);
setState(() {
text=decrypted;
});
} catch (e){
debugPrint(e.toString());
}
}
In this code I'm geting "Input data length must be a multiple of cipher's block size" error
There are three bugs in the Dart code:
The key derivation with scrypt used in the NodeJS code is missing.
The encrypt package does not support scrypt. One possible Dart package that provides scrypt is the pointycastle package.
Note that the encrypt package is just a pointycastle wrapper, so it probably makes more sense to omit the encrypt package altogether and use the pointycastle package directly instead.
In the NodeJS code, the default values for cost (N: 16384), blocksize (r: 8) and parallelization (p: 1) are used, see crypto.scrypt()
. These values must also to be applied in the Dart code.
The disabling of the default PKCS#7 padding is missing. Unlike NodeJS's crypto module, the encrypt package does not automatically disable padding for stream cipher modes like CTR.
The ciphertext must be hex decoded and not Base64 decoded.
A possible fix is:
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as enc;
import 'package:pointycastle/export.dart';
...
final hexIv = "222ef6e4c66c3dfdea9cf1f4828f436e";
final ivHexIv = enc.IV.fromUtf8("7132514077335723");
final scryptParams = ScryptParameters(16384, 8, 1, 32, utf8.encode('salt')); // Fix 1: apply scrypt KDF
final scrypt = KeyDerivator('scrypt')..init(scryptParams);
final rawKey = scrypt.process(utf8.encode('H4WtkvK4qyehIe2kjQfH7we1xIHFK67e'));
final ivKey = enc.Key(rawKey);
final enc.Encrypter encrypterIv = enc.Encrypter(enc.AES(ivKey,mode: enc.AESMode.ctr, padding: null)); // Fix 2: disable padding
final String decrypted = encrypterIv.decrypt16(hexIv, iv: ivHexIv); // Fix 3: hex decode
print("Decrypted:" + decrypted); // Decrypted:28c9c0e555b71fda