当前位置: 首页 > news >正文

flutter下Aes加密算法CFB128的nopadding实现修改源码

有网友问关于cfb128的nopadding实现

java下应该是:

Cipher.getInstance("AES/CFB/NoPadding");

python下应该是:

cipher = AES.new(bytes.fromhex(key), AES.MODE_CFB,iv=bytes.fromhex(iv),segment_size=128)

不需要pad 填充 因为cfb可以不固定block_size 长度,当然你也可以填充

而flutter下只有cfb64的pkcs7填充,nopadding是没有的,填 null 而且会报错

类似

Output buffer too short
Input buffer too short

Output buffer too short
Input buffer too short

看了看源码,分析一下cfb的实现,我们修改一下程序可以实现

我用的是加密库encrypt-5.0.1

默认的flutter只有aes  cfb64 ,

默认填充是 null 或者 PKCS7

话不多说:

首先修改位置D:\android_workspace\flutter\.pub-cache\hosted\pub.dartlang.org\encrypt-5.0.1\lib\src\algorithms\aes.dart这是我的位置根据自己的修改)下

enum AESMode {
  cbc,
  cfb64,
  cfb128,
  cfb8,
  ctr,
  ecb,
  ofb64Gctr,
  ofb64,
  sic,
}

const Map<AESMode, String> _modes = {
  AESMode.cbc: 'CBC',
  AESMode.cfb64: 'CFB-64',
  AESMode.cfb8: 'CFB-8',
  AESMode.cfb128: 'CFB-128',
  AESMode.ctr: 'CTR',
  AESMode.ecb: 'ECB',
  AESMode.ofb64Gctr: 'OFB-64/GCTR',
  AESMode.ofb64: 'OFB-64',
  AESMode.sic: 'SIC',
};

在AESMode 添加中的cfb128 

_modes中添加 AESMode.cfb128: 'CFB-128'

其次修改:D:\android_workspace\flutter\.pub-cache\hosted\pub.dartlang.org\pointycastle-3.6.2\lib\block\modes\cfb.dart这是我的位置根据自己的修改

修改_encryptBlock 下 和 _decryptBlock的源码,我把修改后的贴上了,请自己先备份在修改,以免出问题

// See file LICENSE for more information.

library impl.block_cipher.modes.cfb;

import 'dart:typed_data';

import 'package:pointycastle/api.dart';
import 'package:pointycastle/src/registry/registry.dart';
import 'package:pointycastle/src/impl/base_block_cipher.dart';

/// Implementation of Cipher Feedback Mode (CFB) on top of a [BlockCipher].
class CFBBlockCipher extends BaseBlockCipher {
  /// Intended for internal use.
  static final FactoryConfig factoryConfig = DynamicFactoryConfig.regex(
      BlockCipher,
      r'^(.+)/CFB-([0-9]+)$',
      (_, final Match match) => () {
            var underlying = BlockCipher(match.group(1)!);
            var blockSizeInBits = int.parse(match.group(2)!);
            if ((blockSizeInBits % 8) != 0) {
              throw RegistryFactoryException.invalid(
                  'Bad CFB block size: $blockSizeInBits (must be a multiple of 8)');
            }
            return CFBBlockCipher(underlying, blockSizeInBits ~/ 8);
          });

  @override
  final int blockSize;

  final BlockCipher _underlyingCipher;

  late Uint8List _iv;
  Uint8List? _cfbV;
  Uint8List? _cfbOutV;
  late bool _encrypting;

  CFBBlockCipher(this._underlyingCipher, this.blockSize) {
    _iv = Uint8List(_underlyingCipher.blockSize);
    _cfbV = Uint8List(_underlyingCipher.blockSize);
    _cfbOutV = Uint8List(_underlyingCipher.blockSize);
  }

  @override
  String get algorithmName =>
      '${_underlyingCipher.algorithmName}/CFB-${blockSize * 8}';

  @override
  void reset() {
    _cfbV!.setRange(0, _iv.length, _iv);
    _underlyingCipher.reset();
  }

  /// Initialise the cipher and, possibly, the initialisation vector (IV).
  /// If an IV isn't passed as part of the parameter, the IV will be all zeros.
  /// An IV which is too short is handled in FIPS compliant fashion.
  ///
  /// @param encrypting if true the cipher is initialised for
  ///  encryption, if false for decryption.
  /// @param params the key and other data required by the cipher.
  /// @exception IllegalArgumentException if the params argument is
  /// inappropriate.
  @override
  void init(bool encrypting, CipherParameters? params) {
    _encrypting = encrypting;

    if (params is ParametersWithIV) {
      var ivParam = params;
      var iv = ivParam.iv;

      if (iv.length < _iv.length) {
        // prepend the supplied IV with zeros (per FIPS PUB 81)
        var offset = _iv.length - iv.length;
        _iv.fillRange(0, offset, 0);
        _iv.setRange(offset, _iv.length, iv);
      } else {
        _iv.setRange(0, _iv.length, iv);
      }

      reset();

      // if null it's an IV changed only.
      if (ivParam.parameters != null) {
        _underlyingCipher.init(true, ivParam.parameters);
      }
    } else {
      reset();
      _underlyingCipher.init(true, params);
    }
  }

  /// Process one block of input from the array in and write it to
  /// the out array.
  ///
  /// @param in the array containing the input data.
  /// @param inOff offset into the in array the data starts at.
  /// @param out the array the output data will be copied into.
  /// @param outOff the offset into the out array the output will start at.
  /// @exception DataLengthException if there isn't enough data in in, or
  /// space in out.
  /// @exception IllegalStateException if the cipher isn't initialised.
  /// @return the number of bytes processed and produced.
  @override
  int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) =>
      _encrypting
          ? _encryptBlock(inp, inpOff, out, outOff)
          : _decryptBlock(inp, inpOff, out, outOff);

  /// Do the appropriate processing for CFB mode encryption.
  ///
  /// @param in the array containing the data to be encrypted.
  /// @param inOff offset into the in array the data starts at.
  /// @param out the array the encrypted data will be copied into.
  /// @param outOff the offset into the out array the output will start at.
  /// @exception DataLengthException if there isn't enough data in in, or
  /// space in out.
  /// @exception IllegalStateException if the cipher isn't initialised.
  /// @return the number of bytes processed and produced.
  int _encryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {

    // print(inp.length);
    // print(inp);
    // print("inpOff $inpOff");
    // print("blockSize $blockSize");
    // print(out.length);
    // print("outOff $outOff");
    // if ((inpOff + blockSize) > inp.length) {
    //   throw ArgumentError('Input buffer too short');
    // }
    //
    // if ((outOff + blockSize) > out.length) {
    //   throw ArgumentError('Output buffer too short');
    // }

    _underlyingCipher.processBlock(_cfbV!, 0, _cfbOutV!, 0);

    // XOR the cfbV with the plaintext producing the ciphertext
    int counter = 0;
    for (var i = 0; i < blockSize; i++) {
      // print(inpOff + i);
      if(inpOff + i>=inp.length){
        break;
      }
      counter+=1;
      out[outOff + i] = _cfbOutV![i] ^ inp[inpOff + i];
    }

    // change over the input block.
    var offset = _cfbV!.length - counter;
    _cfbV!.setRange(0, offset, _cfbV!.sublist(counter));
    _cfbV!.setRange(offset, _cfbV!.length, out.sublist(outOff));
    // print("2blockSize $blockSize");
    return blockSize;
  }

  /// Do the appropriate processing for CFB mode decryption.
  ///
  /// @param in the array containing the data to be decrypted.
  /// @param inOff offset into the in array the data starts at.
  /// @param out the array the encrypted data will be copied into.
  /// @param outOff the offset into the out array the output will start at.
  /// @exception DataLengthException if there isn't enough data in in, or
  /// space in out.
  /// @exception IllegalStateException if the cipher isn't initialised.
  /// @return the number of bytes processed and produced.
  int _decryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
    // if ((inpOff + blockSize) > inp.length) {
    //   throw ArgumentError('Input buffer too short');
    // }
    //
    // if ((outOff + blockSize) > out.length) {
    //   throw ArgumentError('Output buffer too short');
    // }
    // print(inp.length);
    // print(inp);
    // print("inpOff $inpOff");
    // print("blockSize $blockSize");
    // print(out.length);
    // print("outOff $outOff");
    _underlyingCipher.processBlock(_cfbV!, 0, _cfbOutV!, 0);

    // change over the input block.
    var offset = _cfbV!.length - blockSize;
    // print("voffset $offset");
    // print(inp.sublist(inpOff));

    _cfbV!.setRange(0, offset, _cfbV!.sublist(blockSize));
    // print(_cfbV!.length);
    if(inp.length-inpOff<blockSize){
      _cfbV!.setRange(offset, inp.length-inpOff, inp.sublist(inpOff));
    }else{
      _cfbV!.setRange(offset, _cfbV!.length, inp.sublist(inpOff));
    }


    // XOR the cfbV with the ciphertext producing the plaintext
    int counter = 0;
    for (var i = 0; i < blockSize; i++) {
      if(inpOff + i>=inp.length){
        break;
      }
      counter+=1;
      out[outOff + i] = _cfbOutV![i] ^ inp[inpOff + i];
    }

    return blockSize;
  }
}

然后运行程序:

import 'dart:convert';
import 'dart:typed_data';
import 'package:encrypt/encrypt.dart';


class a {

  static String _KEY = "000000000000000000000000000000";
  static String _OFFSET = "000000000000000000000000000000";
  static Uint8List  aesEncode(List<int> byteList) {
    final key = Key.fromBase16(_KEY);
    final iv = IV.fromBase16(_OFFSET);
    final encrypter = Encrypter(AES(key, mode: AESMode.cfb128, padding: null));
    Encrypted encrypted = encrypter.encryptBytes(byteList,iv:iv);
    return Uint8List.fromList(encrypted.bytes);
  }

  /// 解密函数
  static Uint8List  aesDecrypted(Uint8List byteList){
    final key = Key.fromBase16(_KEY);
    final iv = IV.fromBase16(_OFFSET);
    final encrypter = Encrypter(AES(key ,mode:  AESMode.cfb128, padding: null));
    var decrypted = encrypter.decryptBytes(Encrypted(byteList), iv: iv);
    return Uint8List.fromList(decrypted);
  }
  static Uint8List createUint8ListFromHexString(String hex) {
    if (hex == null) throw new ArgumentError("hex is null");

    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;
  }
  static String formatBytesAsHexString(Uint8List bytes) {
    if (bytes == null) throw new ArgumentError("The list is null");

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




void main() {
  Uint8List byteList= Uint8List.fromList([1, 65, 100, 109, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 1, 227]);
  // var s = "b152bc9f913b1490f54f4c32b6b80d79dccb838af332e58a72e87f9a00a59736e37b63f2b3a51dc7e84de78242895fad36973e";
  // var s = "0141646D696E000000000000000000000000000000000000003030303030303030000000000000000078000001E3";
  print("Uint8List(初始数组):${byteList}");
  Uint8List c= a.aesEncode(byteList);
  print("Uint8List(加密结果):${c}");
  print("hex(加密结果):${a.formatBytesAsHexString(c)}");
  print("string(加密结果):${utf8.decode(c,allowMalformed: true)}");
  c = a.aesDecrypted(c);
  print("Uint8List(解密结果):${c}");
  print("hex(解密结果):${a.formatBytesAsHexString(c)}");
  print("string(解密结果):${utf8.decode(c,allowMalformed: true)}");
}

_KEY和_OFFESET修改成自己的就行

进群sososo

 

相关文章:

  • LatinIME默认打开各国语言
  • flask搭建一个图书管理平台01-环境配置和项目初始化
  • 基于STM32F1-C8T6无人机(二)——舵机/电调/空心杯电机/飞控/机架/subs接收机/充电器和电池(给出链接和思考)
  • Linux文件链接
  • 机器学习从零到入门 GBDT 梯度提升决策树
  • 手撕前端面试题【javascript~ 总成绩排名、子字符串频次统计、继承、判断斐波那契数组等】
  • ast语法树初探
  • idea 如何不重启服务进行修改-(热部署)