有网友问关于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