flutter_des 适配 HarmonyOS 实战:以 DES 加密解密为例
背景介绍:flutter_des 是一个跨平台的 DES 加密解密 Flutter 插件,支持 Android、iOS、macOS 平台,可在各平台获得一致的加密结果。随着 HarmonyOS 生态的发展,将 Flutter 插件适配到 HarmonyOS 平台成为开发者关注的重点。适配目标。
flutter_des 适配 HarmonyOS 实战:以 DES 加密解密为例

前言
背景介绍:flutter_des 是一个跨平台的 DES 加密解密 Flutter 插件,支持 Android、iOS、macOS 平台,可在各平台获得一致的加密结果。随着 HarmonyOS 生态的发展,将 Flutter 插件适配到 HarmonyOS 平台成为开发者关注的重点。
适配目标:本文以 flutter_des 为例,详细介绍如何将基于 MethodChannel 的 Flutter 插件适配到 HarmonyOS 平台,重点解决 HarmonyOS 原生不支持 DES 算法、ArkTS 严格模式限制等实际问题。
文章价值:为 Flutter 插件开发者提供一份可复用的 HarmonyOS 适配实战指南,涵盖环境配置、架构对比、核心实现、常见问题及最佳实践。
一、背景介绍
1.1 插件概述
flutter_des 是 OctMon 开源的 Flutter DES 加密解密插件,采用 DES/CBC 模式,在 Android 使用 PKCS5Padding,在 iOS/macOS 使用 kCCOptionPKCS7Padding(8 字节分组时两者等效),确保跨平台加密结果一致。
1.2 插件功能
| 功能 | 说明 |
|---|---|
| encrypt | 加密为字节数组 |
| encryptToHex | 加密为十六进制字符串 |
| encryptToBase64 | 加密为 Base64 字符串 |
| decrypt | 从字节数组解密 |
| decryptFromHex | 从十六进制字符串解密 |
| decryptFromBase64 | 从 Base64 字符串解密 |
1.3 适配目标
- 在 HarmonyOS 平台实现与 Android/iOS 一致的 DES 加密解密结果
- 保持 Dart 层 API 不变,无需修改调用方代码
- 解决 HarmonyOS 原生 cryptoFramework 不支持 DES 的问题
- 满足 ArkTS 严格模式的语法和类型约束
二、环境准备与项目初始化
2.1 环境要求
| 工具 | 版本要求 | 说明 |
|---|---|---|
| Flutter SDK | 3.35.8-ohos-0.0.2+ | 支持 HarmonyOS 的 Flutter 版本 |
| Dart SDK | 3.9.2+ | 随 Flutter SDK 一起安装 |
| DevEco Studio | 6.1.0+ | HarmonyOS 官方 IDE |
| HarmonyOS SDK | 5.1.0(18)+ | HarmonyOS 开发工具包 |
| ohpm | 最新版 | OpenHarmony 包管理器 |
2.2 创建 HarmonyOS 平台支持
在项目根目录执行:
flutter create . --template=plugin --platforms=ohos
执行后会产生以下目录结构:
项目根目录/
├── ohos/
│ ├── src/main/ets/components/plugin/
│ │ └── FlutterDesPlugin.ets # 主要实现文件
│ ├── src/main/module.json5
│ ├── index.ets
│ ├── oh-package.json5
│ └── build-profile.json5
└── example/
└── ohos/ # 示例项目
2.3 配置 pubspec.yaml
在 pubspec.yaml 的 flutter.plugin.platforms 部分添加 ohos 配置:
flutter:
plugin:
platforms:
android:
package: com.octmon.flutter_des
pluginClass: FlutterDesPlugin
ios:
pluginClass: FlutterDesPlugin
macos:
pluginClass: FlutterDesPlugin
ohos:
pluginClass: FlutterDesPlugin # 必须与实现类名一致
三、HarmonyOS Flutter 插件架构
3.1 插件生命周期
flutter_des 属于纯 MethodChannel 插件,不涉及 UI 或窗口操作,因此只需实现 FlutterPlugin 和 MethodCallHandler 两个接口,无需实现 AbilityAware。
Flutter Engine 启动
↓
onAttachedToEngine() → 创建 MethodChannel,注册 MethodCallHandler
↓
onMethodCall() → 处理 encrypt/decrypt 等方法调用
↓
Flutter Engine 销毁
↓
onDetachedFromEngine() → 解绑 MethodChannel
3.2 关键接口详解
| 接口 | 是否必需 | 说明 |
|---|---|---|
| FlutterPlugin | 必需 | 绑定/解绑 Flutter Engine |
| MethodCallHandler | 必需 | 处理方法调用 |
| AbilityAware | 可选 | 仅当需要访问 UIAbility/窗口时实现,flutter_des 不需要 |
四、Android vs HarmonyOS 实现对比
4.1 架构差异对比
| 特性 | Android | HarmonyOS | 说明 |
|---|---|---|---|
| 加密库 | javax.crypto.Cipher | @ohos/crypto-js | HarmonyOS 原生不支持 DES,需用第三方库 |
| 算法 | DES/CBC/PKCS5Padding | DES/CBC/Pkcs7 | PKCS5 与 PKCS7 在 8 字节分组时等效 |
| 参数访问 | call.arguments | call.args | OHOS MethodCall 使用 args 属性 |
| 语言 | Java | ArkTS | ArkTS 有严格模式限制 |
4.2 代码结构对比
Android 实现(Java)
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
ArrayList arguments = (ArrayList) call.arguments; // 注意:arguments
String key = (String) arguments.get(1);
String iv = (String) arguments.get(2);
switch (call.method) {
case "encrypt":
result.success(encrypt((String) arguments.get(0), key, iv));
break;
// ...
}
}
private static byte[] encrypt(String data, String key, String iv) {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes()));
return cipher.doFinal(data.getBytes());
}
HarmonyOS 实现(ArkTS)
onMethodCall(call: MethodCall, result: MethodResult): void {
let args = call.args as Array<Object>; // 注意:args,不是 arguments
let key = args[1] as string;
let iv = args[2] as string;
switch (call.method) {
case "encrypt":
result.success(this.doEncrypt(args[0] as string, key, iv));
break;
// ...
}
}
private doEncrypt(data: string, key: string, iv: string): Uint8Array | null {
let keyWA = CryptoJS.enc.Utf8.parse(key);
let ivWA = CryptoJS.enc.Utf8.parse(iv);
let encrypted = CryptoJS.DES.encrypt(data, keyWA, { iv: ivWA });
return this.wordArrayToBytes(encrypted.ciphertext);
}
4.3 关键差异总结
- 参数访问:OHOS 使用
call.args,Android 使用call.arguments - 加密实现:Android 用原生 Cipher,OHOS 用 @ohos/crypto-js
- 类型系统:ArkTS 严格模式禁止无类型对象字面量、禁止将命名空间赋给变量
五、核心实现详解
5.1 完整的 HarmonyOS 实现
以下是完整的 FlutterDesPlugin.ets 实现,包含详细注释:
import {
FlutterPlugin,
FlutterPluginBinding,
MethodCall,
MethodCallHandler,
MethodChannel,
MethodResult,
} from '@ohos/flutter_ohos';
import { CryptoJS } from '@ohos/crypto-js';
/**
* FlutterDesPlugin - DES 加密解密 HarmonyOS 实现
* 实现 FlutterPlugin 和 MethodCallHandler,无需 AbilityAware
*/
export default class FlutterDesPlugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return "FlutterDesPlugin"; // 必须与 pubspec.yaml 中 pluginClass 一致
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_des");
this.channel.setMethodCallHandler(this);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
if (this.channel != null) {
this.channel.setMethodCallHandler(null);
this.channel = null;
}
}
onMethodCall(call: MethodCall, result: MethodResult): void {
try {
// 关键:OHOS 使用 call.args,不是 call.arguments
let args = call.args as Array<Object>;
if (args == null || args.length < 3) {
result.error("INVALID_ARGS", "Arguments must be [data, key, iv]", null);
return;
}
let key = args[1] as string;
let iv = args[2] as string;
switch (call.method) {
case "encrypt":
result.success(this.doEncrypt(args[0] as string, key, iv));
break;
case "encryptToHex":
result.success(this.doEncryptToHex(args[0] as string, key, iv));
break;
case "decrypt":
result.success(this.doDecryptBytes(args[0] as Uint8Array, key, iv));
break;
case "decryptFromHex":
result.success(this.doDecryptFromHex(args[0] as string, key, iv));
break;
default:
result.notImplemented();
}
} catch (err) {
result.error("CRYPTO_ERROR", `Error: ${(err as Error).message}`, null);
}
}
// 加密:CryptoJS 默认 CBC + Pkcs7,与 Android PKCS5Padding 等效
private doEncrypt(data: string, key: string, iv: string): Uint8Array | null {
try {
let keyWA = CryptoJS.enc.Utf8.parse(key);
let ivWA = CryptoJS.enc.Utf8.parse(iv);
let encrypted = CryptoJS.DES.encrypt(data, keyWA, { iv: ivWA });
return this.wordArrayToBytes(encrypted.ciphertext);
} catch (e) {
return null;
}
}
// 解密:通过 HEX→Base64 转换避免 ArkTS 创建 CipherParams 对象字面量
private doDecryptBytes(data: Uint8Array, key: string, iv: string): string | null {
try {
let hexStr = this.toHexString(data);
let wordArray = CryptoJS.enc.Hex.parse(hexStr);
let base64Str = CryptoJS.enc.Base64.stringify(wordArray);
let keyWA = CryptoJS.enc.Utf8.parse(key);
let ivWA = CryptoJS.enc.Utf8.parse(iv);
let decrypted = CryptoJS.DES.decrypt(base64Str, keyWA, { iv: ivWA });
return decrypted.toString(CryptoJS.enc.Utf8);
} catch (e) {
return "";
}
}
// WordArray 转 Uint8Array(CryptoJS 内部使用 32 位字数组)
private wordArrayToBytes(wa: CryptoJS.lib.WordArray): Uint8Array {
let words = wa.words;
let sigBytes = wa.sigBytes;
let u8 = new Uint8Array(sigBytes);
let offset = 0;
for (let i = 0; i < words.length && offset < sigBytes; i++) {
let word = words[i];
u8[offset++] = (word >>> 24) & 0xff;
if (offset < sigBytes) u8[offset++] = (word >>> 16) & 0xff;
if (offset < sigBytes) u8[offset++] = (word >>> 8) & 0xff;
if (offset < sigBytes) u8[offset++] = word & 0xff;
}
return u8;
}
private toHexString(bytes: Uint8Array): string {
let hex = "";
for (let i = 0; i < bytes.length; i++) {
let h = (bytes[i] & 0xff).toString(16);
hex += h.length === 1 ? "0" + h : h;
}
return hex.toUpperCase(); // 与 Android byte2hex 保持一致
}
}
5.2 关键实现点深度解析
5.2.1 使用 @ohos/crypto-js 替代原生加密
原因:HarmonyOS 原生 @ohos.security.cryptoFramework 不支持 DES,仅支持 3DES 及以上算法。
方案:在 oh-package.json5 中添加依赖:
{
"dependencies": {
"@ohos/crypto-js": "^2.0.4"
}
}
5.2.2 ArkTS 严格模式注意事项
- 禁止
let crypto = CryptoJS(arkts-no-ns-as-obj):命名空间不能赋给变量,需直接调用CryptoJS.DES.encrypt(...) - 允许
{ iv: ivWA }作为函数参数:仅包含显式声明属性的对象字面量可接受 - 解密:使用 Base64 字符串传入
CryptoJS.DES.decrypt,避免创建CipherParams对象字面量
5.2.3 错误处理
所有 onMethodCall 逻辑包裹在 try-catch 中,异常时调用 result.error() 返回错误码和消息,便于 Dart 层统一处理。
六、适配步骤总结
6.1 完整适配 Checklist
- 执行
flutter create . --template=plugin --platforms=ohos - 在 pubspec.yaml 中添加
ohos.pluginClass - 在 oh-package.json5 中添加 @ohos/crypto-js 依赖
- 实现 FlutterDesPlugin.ets(FlutterPlugin + MethodCallHandler)
- 使用
call.args获取参数 - 实现 encrypt、encryptToHex、decrypt、decryptFromHex 四个方法
- 执行
ohpm install和flutter pub get - 真机签名并运行
flutter run验证
6.2 pubspec.yaml 配置详解
ohos:
pluginClass: FlutterDesPlugin # 必须与 getUniqueClassName() 返回值完全一致
6.3 适配流程图
开始 → 创建 OHOS 平台 → 配置 pubspec.yaml → 添加 crypto-js 依赖
→ 实现 FlutterDesPlugin.ets → 处理 ArkTS 限制 → 真机测试 → 完成
七、常见问题与解决方案
7.1 Property ‘arguments’ does not exist on type ‘MethodCall’
错误信息:Did you mean 'argument'?
原因:OHOS Flutter 的 MethodCall 使用 args 属性,而非 arguments。
解决方案:
// 错误
const args = call.arguments;
// 正确
const args = call.args as Array<Object>;
调试技巧:查阅 @ohos/flutter_ohos 中 MethodCall 的类型定义,确认属性名。
7.2 Object literal must correspond to some explicitly declared class or interface
错误信息:arkts-no-untyped-obj-literals
原因:ArkTS 禁止无类型对象字面量作为类字段初始值或独立变量。
解决方案:避免 = {} 初始化,使用 { iv: ivWA } 作为函数参数直接传递(CipherOption 接口已声明 iv 属性)。
7.3 Namespaces cannot be used as objects (arkts-no-ns-as-obj)
错误信息:arkts-no-ns-as-obj
原因:将 CryptoJS 命名空间赋给变量,如 let crypto = CryptoJS。
解决方案:直接调用 CryptoJS.DES.encrypt(...)、CryptoJS.enc.Utf8.parse(...) 等,不要将命名空间赋给变量。
7.4 HarmonyOS 原生不支持 DES
问题:cryptoFramework.createCipher('DES|CBC|PKCS7') 报错或不支持。
原因:HarmonyOS 出于安全考虑,cryptoFramework 仅支持 3DES 及以上算法。
解决方案:使用 @ohos/crypto-js 第三方库实现 DES,确保与 Android/iOS 结果一致。
7.5 解密时 CipherParams 对象字面量报错
错误信息:CipherParams.create({ ciphertext: wordArray }) 触发对象字面量限制。
解决方案:将密文转为 Base64 字符串,直接调用 CryptoJS.DES.decrypt(base64Str, key, { iv }),decrypt 支持字符串输入。
7.6 Plugin not found: FlutterDesPlugin
原因:pluginClass 与 getUniqueClassName() 返回值不一致,或大小写错误。
解决方案:确保 pubspec.yaml 中 pluginClass: FlutterDesPlugin 与实现类 getUniqueClassName(): string { return "FlutterDesPlugin"; } 完全一致。
八、最佳实践与开发建议
8.1 代码组织最佳实践
- 将加密/解密逻辑封装为私有方法(doEncrypt、doDecryptBytes 等),便于维护和单元测试
- WordArray 与 Uint8Array 的转换单独成方法,避免重复代码
8.2 错误处理最佳实践
- 所有 onMethodCall 逻辑包裹在 try-catch 中
- 使用
result.error("ERROR_CODE", "message", null)返回结构化错误 - 加密失败返回 null,解密失败返回空字符串,与 Android 行为保持一致
8.3 性能优化建议
- @ohos/crypto-js 为纯 JS 实现,大数据量加密时可能较慢,可考虑在 Dart 层做分块或异步处理
- 密钥和 IV 的 UTF8 解析可缓存(若同一会话多次调用)
8.4 测试建议
- 使用与 Android/iOS 相同的测试向量,验证加密结果一致
- 覆盖空字符串、特殊字符、长文本等边界情况
8.5 调试技巧
- 在 DevEco Studio 中打开 example/ohos 项目,可单步调试 ArkTS 代码
- 使用
console.info()输出中间变量(需在调试构建中) - 对比 Android 与 OHOS 的 hex 输出,排查编码差异
九、总结
9.1 核心要点回顾
- 参数访问:OHOS 使用
call.args,不是call.arguments - 加密库:HarmonyOS 原生不支持 DES,使用 @ohos/crypto-js
- ArkTS 限制:禁止命名空间赋变量、注意对象字面量类型
- 接口选择:纯 MethodChannel 插件只需 FlutterPlugin + MethodCallHandler,无需 AbilityAware
9.2 适配价值
- 扩展 flutter_des 至 HarmonyOS 生态,实现四端一致加密
- 为其他 MethodChannel 类插件提供可复用的适配模式
- 积累 ArkTS 与第三方 JS 库集成的实战经验
9.3 后续建议
- 在真机上完成完整功能测试
- 编写 README.OpenHarmony_CN.md / README.OpenHarmony.md 适配说明
- 考虑在安全性要求高的场景推荐使用 AES 替代 DES
9.4 学习资源
更多推荐


所有评论(0)