I am trying to write two functions in flutter and Javascript which I can use throughout my project to encrypt or decrypt data using AES when data is exchanged. For Flutter, I am using the pointycastle package based on instructions https://gist.github.com/proteye/e54eef1713e1fe9123d1eb04c0a5cf9b?signup=true
import 'dart:convert';
import 'dart:typed_data';
import "package:pointycastle/export.dart";
import "./convert_helper.dart";
// AES key size
const KEY_SIZE = 32; // 32 byte key for AES-256
const ITERATION_COUNT = 1000;
class AesHelper {
static const CBC_MODE = 'CBC';
static const CFB_MODE = 'CFB';
static Uint8List deriveKey(dynamic password,
{String salt = '',
int iterationCount = ITERATION_COUNT,
int derivedKeyLength = KEY_SIZE}) {
if (password == null || password.isEmpty) {
throw new ArgumentError('password must not be empty');
}
if (password is String) {
password = createUint8ListFromString(password);
}
Uint8List saltBytes = createUint8ListFromString(salt);
Pbkdf2Parameters params =
new Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA256Digest(), 64));
keyDerivator.init(params);
return keyDerivator.process(password);
}
static Uint8List pad(Uint8List src, int blockSize) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = blockSize - (src.length % blockSize);
var out = new Uint8List(src.length + padLength)..setAll(0, src);
pad.addPadding(out, src.length);
return out;
}
static Uint8List unpad(Uint8List src) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = pad.padCount(src);
int len = src.length - padLength;
return new Uint8List(len)..setRange(0, len, src);
}
static String encrypt(String password, String plaintext,
{String mode = CBC_MODE}) {
Uint8List derivedKey = deriveKey(password);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var rnd = FortunaRandom();
rnd.seed(keyParam);
Uint8List iv = rnd.nextBytes(aes.blockSize);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(true, params);
Uint8List textBytes = createUint8ListFromString(plaintext);
Uint8List paddedText = pad(textBytes, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
Uint8List cipherIvBytes = new Uint8List(cipherBytes.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytes);
return base64.encode(cipherIvBytes);
}
static String decrypt(String password, String ciphertext,
{String mode = CBC_MODE}) {
Uint8List derivedKey = deriveKey(password);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
Uint8List cipherIvBytes = base64.decode(ciphertext);
Uint8List iv = new Uint8List(aes.blockSize)
..setRange(0, aes.blockSize, cipherIvBytes);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(false, params);
int cipherLen = cipherIvBytes.length - aes.blockSize;
Uint8List cipherBytes = new Uint8List(cipherLen)
..setRange(0, cipherLen, cipherIvBytes, aes.blockSize);
Uint8List paddedText = _processBlocks(cipher, cipherBytes);
Uint8List textBytes = unpad(paddedText);
return new String.fromCharCodes(textBytes);
}
static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
var out = new Uint8List(inp.lengthInBytes);
for (var offset = 0; offset < inp.lengthInBytes;) {
var len = cipher.processBlock(inp, offset, out, offset);
offset += len;
}
return out;
}
}
and class flutter convert_helper.dart
import "dart:typed_data";
import 'dart:convert';
import 'package:convert/convert.dart' as convert;
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 createUint8ListFromHexString(String hex) {
var result = new Uint8List(hex.length ~/ 2);
for (var i = 0; i < hex.length; i += 2) {
var num = hex.substring(i, i + 2);
var byte = int.parse(num, radix: 16);
result[i ~/ 2] = byte;
}
return result;
}
Uint8List createUint8ListFromSequentialNumbers(int len) {
var ret = new Uint8List(len);
for (var i = 0; i < len; i++) {
ret[i] = i;
}
return ret;
}
String formatBytesAsHexString(Uint8List bytes) {
var result = new StringBuffer();
for (var i = 0; i < bytes.lengthInBytes; i++) {
var part = bytes[i];
result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
}
return result.toString();
}
List<int> decodePEM(String pem) {
var startsWith = [
"-----BEGIN PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----",
"-----BEGIN ENCRYPTED MESSAGE-----",
"-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
"-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
];
var endsWith = [
"-----END PUBLIC KEY-----",
"-----END PRIVATE KEY-----",
"-----END ENCRYPTED MESSAGE-----",
"-----END PGP PUBLIC KEY BLOCK-----",
"-----END PGP PRIVATE KEY BLOCK-----",
];
bool isOpenPgp = pem.indexOf('BEGIN PGP') != -1;
for (var s in startsWith) {
if (pem.startsWith(s)) {
pem = pem.substring(s.length);
}
}
for (var s in endsWith) {
if (pem.endsWith(s)) {
pem = pem.substring(0, pem.length - s.length);
}
}
if (isOpenPgp) {
var index = pem.indexOf('\r\n');
pem = pem.substring(0, index);
}
pem = pem.replaceAll('\n', '');
pem = pem.replaceAll('\r', '');
return base64.decode(pem);
}
List<int> decodeHex(String hex) {
hex = hex
.replaceAll(':', '')
.replaceAll('\n', '')
.replaceAll('\r', '')
.replaceAll('\t', '');
return convert.hex.decode(hex);
}
For the Javascript solution, I am using CryptoJS
var AESKey = "20190225165436_15230006321670000"
cc = CryptoJS.AES.encrypt( ("abcdef ha ha "), AESKey, { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString()
CryptoJS.AES.decrypt(cc, AESKey).toString(CryptoJS.enc.Utf8); //return abcdef ha ha
Both solutions work well within their own environment, however, the flutter or Javascript hashes can't be exchanged, they will not decrypt. My guess is that the character encoding has something to do with it, hence why the base64 sizes differ so much. Does anyone have an idea to get this working together? Thanks!
Does anyone have an idea to get this working together?