本文介绍了Flutter和javascript兼容的AES加密和解密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图用flutter和Javascript编写两个函数,在交换数据时,我可以在整个项目中使用这两个函数来使用AES加密或解密数据.对于Flutter,我正在根据说明使用pointycastle软件包 https://gist.github.com/proteye/e54eef1713e1fe9123d1eb04c0a5cf9b?signup=true >

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 instructionshttps://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;
  }
}

和类flutter convert_helper.dart

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);
}

对于Javascript解决方案,我正在使用CryptoJS

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

这两种解决方案都可以在自己的环境中很好地工作,但是flutter或Javascript哈希无法交换,它们也不会解密.我的猜测是字符编码与它有关,因此为什么base64大小相差很大.有谁有想法将其一起使用?谢谢!

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?

推荐答案

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

请参考下面的链接以获取解决方案.

Please refer below link for solution.

https://medium.com/@ chingsuehok/cryptojs-aes-encryption-decryption-for-flutter-dart-7ca123bd7464

这篇关于Flutter和javascript兼容的AES加密和解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 09:20