-
-
Save proteye/e54eef1713e1fe9123d1eb04c0a5cf9b to your computer and use it in GitHub Desktop.
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; | |
} | |
} |
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); | |
} |
CryptoJS.AES.decrypt("U2FsdGVkX1+8Brkcl7ZjxWVc4L6WYbn6MGzvtFn8Zu0=", "20190225165436_15230006321670000").toString(CryptoJS.enc.Utf8);
CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(txtMsg), AESKey, { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString()
i use CryptoJS on de/ecript message
Looks like the pad and pading process is different with the CryptoJS library
can you modify it to decript and encript it like CryptoJS?
i tried using your code for the purpose of decryption
I see you have imported the following dart file(import 'package:convert/convert.dart' ) in the converter_helper.dart file.
gives me the error ( Target of URI doesn't exist package:convert/convert.dart')
please advise as to where I can get this file, or alternatively provide the file.
I think you have to install this Flutter Package: Convert
Hi, I'm trying to add my own salt string. I modified the deriveKey() function to add it but, I got this error message:
Invalid argument(s): Fortuna PRNG can only be used with 256 bits keys
I understand that this happens inside the encrypt() function, when it tries to generate the iv (line 66)...
Could you please help?
I use your code
AesHelper.decrypt ("201908101111", "U2FsdGVkX1 / gsfs95wfQU1BjwPIlhS6LH0zDOqsz2io =");
but I found it failed when running to unpad ()
int padLength = pad.padCount (src);
thow: "Invalid or corrupted pad block"
please give me solution what I do.
Hi,
I wanted to use this for my own mobile project and It found that the ITERATION_COUNT is too large in the mobile operation and seems to block execution time(for like at least like1-2 sec) even when used with future async. So, I reduced it to 250 which didn't block the execution that long.
My question is, is it alright to reduce the ITERATION_COUNT?
Hellow
Why use it
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
The decrypted data is preceded by IV
I use your code to test it
My input data is as follows
AesHelper.decrypt ("20190222141644_15230006321670000_15508198044180000", "U2FsdGVkX1 / gsfs95wfQU1BjwPIlhS6LH0zDOqsz2io =");
but I found it failed when running to unpad ()
int padLength = pad.padCount (src);
thow: "Invalid or corrupted pad block"
Can you help me, fix and run the same input as above?
It took me 2 days to learn the AES, but it didn't seem right
On the web client I use CryptoJS