Flutter 匠心千刃 | SHA256 加密

avatar
作者
猴君
阅读量:1

theme: cyanosis

像素编辑器#05.png

0. 本文目标

本文介绍一下如何在 Flutter 中进行 SHA256加密。并结合TolyUI 在 匠心千刃 中搭建 sha256加密的交互界面 ,本文目标如下所示:

可以在输入框中输入字符串,会自动计算输入内容的sha256值:

1.gif

也可以选择文件,计算该文件的sha256值:

2.gif

1. SHA256能干什么

每个人的指纹是独一无二的,采集的指纹可以和犯罪嫌疑人对比是否一致,来检验凶手的身份。而不必对比案发现场的凶手和嫌疑人的每个细胞是否一致。

那对于一个文件,或说字节数组来说,有什么可以像指纹一样,能标识它的身份。让数据的唯一性既可以简洁地表达,又可以跨越时间与空间呢?sha256 加密就可以做到,当然也可以选取其他的加密方式。

理解了这一点,sha256的价值就非常明显了。比如下载文件的网站,一般都会提供该文件对应的sha值。这样在现在后,就可以根据sha值校验下载的文件是否完整。编程开发中文件上传、下载涉及前后端通信,两者可以通过sha值,来检验数据传输过程中的完整性。


2.Dart中如何使用 SHA256加密

像这样的通用算法,一般都会有三方库的支持。可以使用 crypto 库非常方便地计算一个字节数组的 sha256值。目前我的 Dart 命令行工具 toly 已经支持了 SHA256 的命令,如下所示:toly sha256 -s 可以加密若干个字符串:

toly sha256 -s 张风捷特烈 TolyUI 'Flutter Unit'

image.png

sha256.convert 方法即可加密字符数组,所以需要将文本通过 utf8 进行编码,得到 Unit8List ,然后将其作为入参进行转换。代码如下:

dart String arg = '张风捷特烈'; Uint8List data = utf8.encode(arg); Digest digest = sha256.convert(data);


toly sha256 -f 可以计算若干个文件的 sha256 值,其中 -f 可以省略:

image.png

任何文件都是字节数组,可以通过 File 对象读取得到 Uint8List 对象,然后使用 sha256.convert 即可:

dart File file = File(r'D:\Files\add.zip'); Uint8List data = await file.readAsBytes(); Digest digest = sha256.convert(data);


3. 使用Flutter搭建交互界面

命令行毕竟操作不是很方便,交互界面可以有更大的使用场景。对于点点选选的交互,不会编程的人也可以使用。在布局上,界面由两块独立的面板构成,所以可以拆分为两个组件: StringCalcPanelFileCalcPanel ,分别处理字符串和文件的 sha256 计算:

image.png

然后将两者拼装在一起,放入 Sha256Page 中,作为局部路由的展示组件:

```dart class Sha256Page extends StatelessWidget { const Sha256Page({super.key});

@override Widget build(BuildContext context) { return const Scaffold( body: SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 12), child: Column( children: [ StringCalcPanel(), Divider(), FileCalcPanel(), ], ), ), ); } } ```


另外,可以注意到两个面板中的右上角图标按钮是相同的视图,可以进行封装以便复用。这里定义了 BladeToolBar 组件对动作按钮进行构建,按钮的具体交互事件是由外界决定的,需要通过回调处理。我将按键行为有枚举进行管理,如下所示:

```dart enum ToolAction { copy(TolyIcon.iconcopy), clear(TolyIcon.iconclear), ; final IconData icon; const ToolAction(this.icon);

String tooltip(BuildContext context) { return switch (this) { ToolAction.copy => '复制', ToolAction.clear => '清空', }; } } ```

这样 BladeToolBar 点击了那个按钮,就可以通过回调函数访问到对应的 ToolAction 对象,关键代码如下,通过 Wrap 组件包裹图标,并将 ToolAction 元素列表映射为 TolyAction 组件:

```dart class BladeToolBar extends StatelessWidget { final ValueChanged onTap;

const BladeToolBar({super.key, required this.onTap});

@override Widget build(BuildContext context) { return Wrap( spacing: 8, children: ToolAction.values .map((action) => TolyAction( tooltip: action.tooltip(context), child: Icon(action.icon, size: 20), onTap: () => onTap(action))) .toList(), ); } } ```


面板是上中下的竖向布局,由于面板内容需要随输入更新加密结果:

  • 也就是说需要主动改变界面状态,派生 StatefulWidget 组件。
  • 其中状态量有加密结果 _result 和输入框控制器 _input
  • 状态类中监听 _input 的变化,触发解析和更新 _result 结果。
  • 可拉伸的输入面板使用 TolyUI 中的 TolyInputArea 组件。

image.png

```dart class StringCalcPanel extends StatefulWidget { const StringCalcPanel({super.key});

@override State createState() => _StringCalcPanelState(); }

class _StringCalcPanelState extends State { final TextEditingController _input = TextEditingController();

String _result = '';

@override void initState() { input.addListener(parser); super.initState(); }

@override void dispose() { _input.dispose(); super.dispose(); }

void parser() { if(input.text.isEmpty) return; Uint8List bytes = utf8.encode(_input.text); var digest = sha256.convert(bytes); _result = digest.toString(); setState(() {}); } ```

文件计算的面板也是类似,这里就不赘述了,通过 file_picker 插件选择文件,读取数据,计算即可。


4. 大文件文件的异步计算

仔细看一下会发现,sha256.convert 是一个同步方法,这对于大文件来说是灾难性的。这就表示 1G 的文件将会被一次性读取加入内存,一方面会阻塞程序,另一方面会造成很大的内存负担。对于大文件来说,一点点地 蚕食 是非常必要的。通过源码可以看出处理 convert 方法,还有一个 startChunkedConversion 的方法:

image.png

我们可以定义内存块的大小,来一块块读取计算,如下所示,通过 async 包中的 ChunkedStreamReader 来一块块读取文件内容:

```dart import 'dart:convert'; import 'dart:io'; import 'package:async/async.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart';

Future getFileSha256(String path) async { final reader = ChunkedStreamReader(File(path).openRead()); const chunkSize = 1024 * 1024 * 2; // 2MB AccumulatorSink output = AccumulatorSink (); ByteConversionSink input = sha256.startChunkedConversion(output); try { while (true) { List chunk = await reader.readChunk(chunkSize); if (chunk.isEmpty) { break; } input.add(chunk); } } finally { reader.cancel(); } input.close(); return output.events.single; } ```


本文介绍了 Flutter 中计算 SHA256 的方式,并通过 匠心千刃 提供了交互界面。那本文就到这里,后期匠心千刃还会带来更多有趣的实用工具,敬请期待~

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!