学习的一个重要阶段是模仿。老夫学习 flutter 从移植一个现有的几乎已经被废弃的 Android 端产品开始。其中网络报文传输时有用到 3DES。dart 似乎没有官方的现成实现,在 pub.dev(的国内镜像站 https://pub.flutter-io.cn)里搜索,会有若干个插件,经过对代码风格以及 API 的肉眼考察后选择了 tripledes-dart,在 pubspec.yaml
里添加 tripledes: 2.1.0
的依赖即可。
最开头犯了个很低级的错误(尽管我经常和人说,错误其实是不分低级和高级的),照着例子把引擎初始化成了 DESEngine
而不是本应当的 TripleDESEngine
,导致结果一直有问题,后来才发现、改正。改正后有个奇怪的情况,打印出的报文中,中文部分都是乱码。
尽管在字符编码方面的知识,老夫自忖可以算是专家,但是一门编程语言中,对字符串如何进行存储可以有自己的独有设计。原始的产品,报文发出的服务端应该是 Java 实现的,Android 端接收到以后也是 Java 代码进行逆向处理,因而问题并不明显。而 Dart 显然有自己的特色。在这门语言里(刚学两三天),迄今还没有看到像 byte[]
或者 ByteArray
这样的数据类型,最接近的是 Uint8List
,它和 List<int>
以及 String
数据类型的相互转换正是需要了解的核心。
别不赘述,本文最后将附上的是本人对 tripledes-dart
项目在 GitHub 上一个长达一年无人回复的问题“Error when encrypt, decrypt text UTF8”的答案,见下,各位一看便明了。
有问题的代码片段为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import 'package:tripledes/tripledes.dart'; void main() { String text = "Ngoc create a chatroom. 👻 👻 김진학테스트 tiếng việt"; String secret = "20180531100733_userUid02_15277360532520000"; String cipherText = encryptMessDES(secret, text); print(cipherText); String newTxt = decryptMessDES(secret, cipherText); print(newTxt); } String decryptMessDES(String key, String cipherText) { var blockCipher = new BlockCipher(new DESEngine(), key); return blockCipher.decodeB64(cipherText); } String encryptMessDES(String key, String message) { var blockCipher = new BlockCipher(new DESEngine(), key); return blockCipher.encodeB64(message); } |
问题的根源在于两个方面:1. Dart 的 Stiring 对象中,字符是 UTF-16 格式组织的;2. tripledes-dart
在其按块处理的过程中,把每个字符的每个字节均按照 LATIN-1
进行处理。修订后的功能正常的代码其实仅需增加两行,为了读者复制以及测试的便利,以下仍然附上 main
函数的完整代码块(其余部分均不需任何改动)。因此到底多了哪两行,请自行辨识。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void main() { String text = "Ngoc create a chatroom. 👻 👻 김진학테스트 tiếng việt"; String secret = "20180531100733_userUid02_15277360532520000"; text = String.fromCharCodes(Utf8Encoder().convert(text)); // THE POINT 1 String cipherText = encryptMessDES(secret, text); print(cipherText); String newTxt = decryptMessDES(secret, cipherText); newTxt = Utf8Decoder().convert(newTxt.codeUnits); // THE POINT 2 print(newTxt); } |
善哉。老夫这也算做了一件善事。