Flutter for OpenHarmony 跨平台工程实战:原生密码加密能力集成
作者:maaaath
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
前言
在 Flutter 跨平台应用开发中,用户密码安全是不可回避的课题。明文存储、简单 MD5/SHA-1 哈希早已无法满足安全需求,企业级应用必须采用业界认可的标准加密方案:SHA-256 加盐哈希 防彩虹表攻击、PBKDF2 高迭代派生 防暴力破解、AES-256-CBC 对称加密 保护数据机密性、HMAC-SHA256 认证防止密文篡改。
Flutter 本身不提供原生加密 API,这些能力需要通过 MethodChannel 桥接到 OpenHarmony 原生层实现。本文以 Flutter for OpenHarmony (Flutter Ohos)跨平台工程为载体,完整展示从 Flutter 侧 Dart 桥接封装到 OpenHarmony 原生 ArkTS 能力调用的全链路方案,重点呈现 Flutter Dart 端的完整实现,包括密码加密服务封装、MethodChannel 调用封装以及演示页面的构建。
一、整体架构
┌──────────────────────────────────────────────────────┐
│ Flutter 侧(Dart) │
│ CryptoBridge ──── MethodChannel ──── 调用结果返回 │
│ 密码加密演示页面(TabBar) │
└───────────────────────┬──────────────────────────────┘
│ Channel: com.example.oh_demo10/crypto
┌───────────────────────▼──────────────────────────────┐
│ OpenHarmony 原生层(ArkTS) │
│ CryptoBridge ──── CryptoService ──── SecureStorage │
└──────────────────────────────────────────────────────┘
Flutter 侧负责 UI 展示与 MethodChannel 调用,原生 ArkTS 层负责实际加密运算,两者通过标准 MethodChannel 协议通信。
二、Flutter 侧 MethodChannel 桥接封装
Flutter 侧是最核心的部分,我们首先构建 CryptoBridge Dart 类,将所有原生加密能力以类型安全的方式暴露给业务代码使用。
2.1 结果数据模型定义
为了统一各方法的返回格式,我们定义了若干结果类:
dart
// 密码强度等级枚举
enum PasswordStrengthLevel {
veryWeak, // 极弱
weak, // 弱
fair, // 一般
strong, // 强
veryStrong // 极强
}
// 密码强度评估结果(包含分数、等级、文字描述、改进建议)
class PasswordStrengthResult {
final int score;
final PasswordStrengthLevel level;
final String levelText;
final List<String> suggestions;
PasswordStrengthResult({
required this.score,
required this.level,
required this.levelText,
required this.suggestions,
});
factory PasswordStrengthResult.fromMap(Map<String, dynamic> map) {
final levelIndex = map['level'] as int? ?? 0;
final levelTexts = ['极弱', '弱', '一般', '强', '极强'];
return PasswordStrengthResult(
score: map['score'] as int? ?? 0,
level: PasswordStrengthLevel.values[levelIndex.clamp(0, 4)],
levelText: levelTexts[levelIndex.clamp(0, 4)],
suggestions: (map['suggestions'] as List<dynamic>?)
?.map((e) => e.toString())
.toList() ?? [],
);
}
// 强度对应的颜色(用于 UI 展示)
Color get levelColor {
switch (level) {
case PasswordStrengthLevel.veryWeak:
return const Color(0xFFF44336);
case PasswordStrengthLevel.weak:
return const Color(0xFFFF9800);
case PasswordStrengthLevel.fair:
return const Color(0xFFFFC107);
case PasswordStrengthLevel.strong:
return const Color(0xFF8BC34A);
case PasswordStrengthLevel.veryStrong:
return const Color(0xFF4CAF50);
}
}
// 进度条百分比
double get progress {
switch (level) {
case PasswordStrengthLevel.veryWeak: return 20;
case PasswordStrengthLevel.weak: return 40;
case PasswordStrengthLevel.fair: return 60;
case PasswordStrengthLevel.strong: return 80;
case PasswordStrengthLevel.veryStrong: return 100;
}
}
}
// 密码哈希结果
class PasswordHashResult extends CryptoResult {
final String? salt;
final String? hash;
PasswordHashResult({
required bool success,
this.salt,
this.hash,
String? error,
}) : super(success: success, error: error);
factory PasswordHashResult.fromMap(Map<String, dynamic> map) {
return PasswordHashResult(
success: map['success'] as bool? ?? false,
hash: map['hash'] as String?,
salt: map['salt'] as String?,
error: map['error'] as String?,
);
}
}
// 密码比对结果
class PasswordMatchResult {
final bool success;
final bool? matched;
final String? error;
PasswordMatchResult({
required this.success,
this.matched,
this.error,
});
factory PasswordMatchResult.fromMap(Map<String, dynamic> map) {
return PasswordMatchResult(
success: map['success'] as bool? ?? false,
matched: map['matched'] as bool?,
error: map['error'] as String?,
);
}
}
// 用户信息
class UserInfo {
final String username;
final String createdAt;
final String updatedAt;
factory UserInfo.fromMap(Map<String, dynamic> map) {
return UserInfo(
username: map['username'] as String? ?? '',
createdAt: map['createdAt'] as String? ?? '',
updatedAt: map['updatedAt'] as String? ?? '',
);
}
}
2.2 MethodChannel 桥接类
CryptoBridge 是 Flutter 侧的通信核心,通过 MethodChannel 与 OpenHarmony 原生层双向通信:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// OpenHarmony 密码加密桥接器
// 统一封装所有密码加密相关 MethodChannel 调用
class CryptoBridge {
// Channel 名称,与原生 ArkTS 侧保持一致
static const _channel = MethodChannel('com.example.oh_demo10/crypto');
CryptoBridge._();
// ==================== 密码强度评估 ====================
/// 评估密码强度(实时多维度评分)
/// 从长度、字符种类、常见模式三个维度评估
/// 分数 0-4 映射到 极弱/弱/一般/强/极强
static Future<PasswordStrengthResult> evaluateStrength(String password) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'evaluatePasswordStrength',
{'password': password},
);
if (result == null) {
return PasswordStrengthResult(
score: 0,
level: PasswordStrengthLevel.veryWeak,
levelText: '极弱',
suggestions: ['评估失败'],
);
}
return PasswordStrengthResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
debugPrint('[CryptoBridge] evaluateStrength error: ${e.message}');
return PasswordStrengthResult(
score: 0,
level: PasswordStrengthLevel.veryWeak,
levelText: '评估失败',
suggestions: ['原生能力调用失败: ${e.message}'],
);
}
}
// ==================== 密码哈希与比对 ====================
/// 生成密码哈希(用于存储)
/// 格式: base64(salt) + "$" + base64(SHA256(password + salt))
/// 密码永不存储明文,每次调用生成独立随机盐
static Future<PasswordHashResult> hashPassword(String password) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'hashPassword',
{'password': password},
);
if (result == null) {
return PasswordHashResult(success: false, error: '原生返回为空');
}
return PasswordHashResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return PasswordHashResult(success: false, error: e.message);
} catch (e) {
return PasswordHashResult(success: false, error: e.toString());
}
}
/// 验证密码(安全比对)
/// 将输入密码与存储的哈希值进行恒定时间比对,防止时序攻击
static Future<PasswordMatchResult> verifyPassword(
String password,
String storedHash,
) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'verifyPassword',
{'password': password, 'key': storedHash},
);
if (result == null) {
return PasswordMatchResult(success: false, error: '原生返回为空');
}
return PasswordMatchResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return PasswordMatchResult(success: false, error: e.message);
} catch (e) {
return PasswordMatchResult(success: false, error: e.toString());
}
}
// ==================== 密钥派生 ====================
/// PBKDF2 密钥派生
/// 从密码派生高熵密钥,高迭代次数使暴力破解成本极高
/// iterations 默认为 100000,OWASP 2023 推荐值 >= 120000
static Future<CryptoResult> deriveKey(
String password,
String saltB64, {
int iterations = 100000,
}) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'deriveKey',
{'password': password, 'salt': saltB64, 'iterations': iterations},
);
if (result == null) return CryptoResult(success: false, error: '原生返回为空');
final map = Map<String, dynamic>.from(result);
return CryptoResult(
success: map['success'] as bool? ?? false,
data: map['data'] as String?,
error: map['error'] as String?,
);
} on PlatformException catch (e) {
return CryptoResult(success: false, error: e.message);
} catch (e) {
return CryptoResult(success: false, error: e.toString());
}
}
/// 生成随机盐值(Base64 编码)
static Future<CryptoResult> generateSalt() async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('generateSalt');
if (result == null) return CryptoResult(success: false, error: '原生返回为空');
final map = Map<String, dynamic>.from(result);
return CryptoResult(
success: map['success'] as bool? ?? false,
data: map['data'] as String?,
error: map['error'] as String?,
);
} on PlatformException catch (e) {
return CryptoResult(success: false, error: e.message);
} catch (e) {
return CryptoResult(success: false, error: e.toString());
}
}
// ==================== 数据加密/解密 ====================
/// AES-256-CBC 数据加密
/// 使用 HMAC-SHA256 提供完整性认证
/// 格式: base64(iv) + "$" + base64(ciphertext) + "$" + base64(mac)
static Future<CryptoResult> encrypt(String data, String key) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'encrypt',
{'data': data, 'key': key},
);
if (result == null) return CryptoResult(success: false, error: '原生返回为空');
final map = Map<String, dynamic>.from(result);
return CryptoResult(
success: map['success'] as bool? ?? false,
data: map['data'] as String?,
error: map['error'] as String?,
);
} on PlatformException catch (e) {
return CryptoResult(success: false, error: e.message);
} catch (e) {
return CryptoResult(success: false, error: e.toString());
}
}
/// AES-256-CBC 数据解密
/// 先验证 HMAC 完整性,再解密;若数据被篡改则解密失败
static Future<CryptoResult> decrypt(String encryptedData, String key) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'decrypt',
{'data': encryptedData, 'key': key},
);
if (result == null) return CryptoResult(success: false, error: '原生返回为空');
final map = Map<String, dynamic>.from(result);
return CryptoResult(
success: map['success'] as bool? ?? false,
data: map['data'] as String?,
error: map['error'] as String?,
);
} on PlatformException catch (e) {
return CryptoResult(success: false, error: e.message);
} catch (e) {
return CryptoResult(success: false, error: e.toString());
}
}
// ==================== 安全存储 ====================
/// 设置主密码(首次使用时调用)
/// 主密码用于派生数据加密密钥
static Future<StorageResult> setupMasterPassword(String masterPassword) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'setupMasterPassword',
{'password': masterPassword},
);
if (result == null) return StorageResult(success: false, error: '原生返回为空');
return StorageResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return StorageResult(success: false, error: e.message);
} catch (e) {
return StorageResult(success: false, error: e.toString());
}
}
/// 解锁存储(验证主密码后调用)
static Future<StorageResult> unlockStorage(String masterPassword) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'unlockStorage',
{'password': masterPassword},
);
if (result == null) return StorageResult(success: false, error: '原生返回为空');
return StorageResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return StorageResult(success: false, error: e.message);
} catch (e) {
return StorageResult(success: false, error: e.toString());
}
}
/// 锁定存储(清除内存中的密钥)
static Future<StorageResult> lockStorage() async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('lockStorage');
if (result == null) return StorageResult(success: false, error: '原生返回为空');
return StorageResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return StorageResult(success: false, error: e.message);
} catch (e) {
return StorageResult(success: false, error: e.toString());
}
}
/// 保存用户凭据(加密存储)
/// 密码使用 SHA-256 + 盐值哈希,额外数据使用 AES-256-CBC 加密
static Future<StorageResult> saveCredential(
String username,
String password, {
Map<String, String>? extraData,
}) async {
try {
final args = <String, dynamic>{
'username': username,
'password': password,
};
if (extraData != null) {
args['extraData'] = extraData;
}
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'saveCredential',
args,
);
if (result == null) return StorageResult(success: false, error: '原生返回为空');
return StorageResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return StorageResult(success: false, error: e.message);
} catch (e) {
return StorageResult(success: false, error: e.toString());
}
}
/// 验证用户密码
static Future<PasswordMatchResult> verifyCredential(
String username,
String password,
) async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'verifyCredential',
{'username': username, 'password': password},
);
if (result == null) {
return PasswordMatchResult(success: false, error: '原生返回为空');
}
return PasswordMatchResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return PasswordMatchResult(success: false, error: e.message);
} catch (e) {
return PasswordMatchResult(success: false, error: e.toString());
}
}
/// 获取所有用户名列表
static Future<List<UserInfo>> listUsers() async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('listUsers');
if (result == null) return [];
final users = result['users'] as List<dynamic>?;
if (users == null) return [];
return users
.map((e) => UserInfo.fromMap(Map<String, dynamic>.from(e as Map)))
.toList();
} on PlatformException catch (e) {
debugPrint('[CryptoBridge] listUsers error: ${e.message}');
return [];
} catch (e) {
return [];
}
}
/// 清除所有数据
static Future<StorageResult> clearAll() async {
try {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('clearAll');
if (result == null) return StorageResult(success: false, error: '原生返回为空');
return StorageResult.fromMap(Map<String, dynamic>.from(result));
} on PlatformException catch (e) {
return StorageResult(success: false, error: e.message);
} catch (e) {
return StorageResult(success: false, error: e.toString());
}
}
}
三、Flutter 侧密码加密演示页面
演示页面采用 TabBar + TabBarView 结构,提供 5 个功能标签页,涵盖所有加密能力。页面通过 StatefulWidget 管理各 Tab 的状态与数据流:
dart
import 'package:flutter/material.dart';
import '../bridge/crypto_bridge.dart';
/// 密码加密演示页面
class CryptoDemoPage extends StatefulWidget {
const CryptoDemoPage({super.key});
@override
State<CryptoDemoPage> createState() => _CryptoDemoPageState();
}
class _CryptoDemoPageState extends State<CryptoDemoPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// 密码强度相关状态
final TextEditingController _strengthController = TextEditingController();
PasswordStrengthResult? _strengthResult;
// 哈希与比对相关状态
final TextEditingController _hashPasswordController = TextEditingController();
final TextEditingController _verifyPasswordController = TextEditingController();
String _storedHash = '';
String _hashLog = '';
// PBKDF2 相关状态
final TextEditingController _pbkdf2PasswordController = TextEditingController();
final TextEditingController _pbkdf2SaltController = TextEditingController();
final TextEditingController _pbkdf2IterationsController = TextEditingController(text: '100000');
String _derivedKey = '';
// AES 加密/解密相关状态
final TextEditingController _encryptDataController = TextEditingController();
final TextEditingController _encryptKeyController = TextEditingController();
final TextEditingController _decryptInputController = TextEditingController();
final TextEditingController _decryptKeyController = TextEditingController();
String _encryptResult = '';
// 安全存储相关状态
final TextEditingController _masterPasswordController = TextEditingController();
final TextEditingController _credUsernameController = TextEditingController();
final TextEditingController _credPasswordController = TextEditingController();
String _storageStatus = '初始化中...';
Color _storageStatusColor = Colors.grey;
bool _storageLoggedIn = false;
List<UserInfo> _userList = [];
String _storageLog = '';
@override
void initState() {
super.initState();
_tabController = TabController(length: 5, vsync: this);
_initStorage();
}
@override
void dispose() {
_tabController.dispose();
_strengthController.dispose();
// ... 释放所有 TextEditingController
super.dispose();
}
Future<void> _initStorage() async {
// 检查主密码状态,解锁状态等
final hasMaster = await CryptoBridge.hasMasterPassword();
final unlocked = await CryptoBridge.isStorageUnlocked();
setState(() {
if (unlocked) {
_storageStatus = '已解锁';
_storageStatusColor = Colors.green;
_storageLoggedIn = true;
} else if (hasMaster) {
_storageStatus = '已锁定';
_storageStatusColor = Colors.orange;
} else {
_storageStatus = '未设置主密码';
_storageStatusColor = Colors.grey;
}
});
if (_storageLoggedIn) {
await _loadUsers();
}
}
3.1 密码强度检测 Tab
通过 onChanged 实时调用加密能力,动态更新强度指示:
dart
Future<void> _evaluateStrength(String password) async {
if (password.isEmpty) {
setState(() => _strengthResult = null);
return;
}
final result = await CryptoBridge.evaluateStrength(password);
setState(() => _strengthResult = result);
}
Widget _buildStrengthTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _strengthController,
decoration: const InputDecoration(
labelText: '输入密码',
border: OutlineInputBorder(),
hintText: '实时评估密码强度',
),
obscureText: true,
onChanged: (v) => _evaluateStrength(v), // 实时触发评估
),
const SizedBox(height: 16),
if (_strengthResult != null) ...[
Row(
children: [
Text(
_strengthResult!.levelText,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _strengthResult!.levelColor,
),
),
const SizedBox(width: 12),
Text('(${_strengthResult!.score}/4)',
style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
const SizedBox(height: 8),
// 强度进度条
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: _strengthResult!.progress / 100,
backgroundColor: Colors.grey[200],
color: _strengthResult!.levelColor,
minHeight: 8,
),
),
const SizedBox(height: 12),
// 改进建议列表
...(_strengthResult!.suggestions.map(
(s) => Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
children: [
const Icon(Icons.lightbulb_outline, size: 14, color: Colors.blue),
const SizedBox(width: 6),
Expanded(child: Text(s, style: const TextStyle(fontSize: 13))),
],
),
),
)),
],
],
),
);
}
3.2 SHA-256 哈希与安全比对 Tab
展示密码哈希生成和恒定时间比对的完整流程:
dart
Future<void> _doHash() async {
final password = _hashPasswordController.text;
if (password.isEmpty) {
setState(() => _hashLog = '[错误] 请输入密码');
return;
}
// 调用原生 SHA-256 + 盐值哈希
final result = await CryptoBridge.hashPassword(password);
if (result.success && result.hash != null) {
setState(() {
_storedHash = result.hash!;
_hashLog = '[SHA-256] 哈希生成成功\n'
'格式: base64(salt)\$base64(hash)\n'
'(密码永不存储明文)';
});
}
}
Future<void> _doVerify() async {
final password = _verifyPasswordController.text;
if (password.isEmpty || _storedHash.isEmpty) {
setState(() => _hashLog = '[错误] 请输入密码并先生成哈希');
return;
}
// 恒定时间比对,防止时序攻击
final result = await CryptoBridge.verifyPassword(password, _storedHash);
if (result.success) {
setState(() {
_hashLog = '[验证] ${result.matched == true ? "密码正确" : "密码错误"}\n'
'使用恒定时间比较,防时序攻击';
});
}
}
3.3 PBKDF2 密钥派生 Tab
展示密钥派生的完整流程,包括盐值生成、迭代次数配置、派生结果展示:
dart
Future<void> _doDeriveKey() async {
final password = _pbkdf2PasswordController.text;
if (password.isEmpty) return;
// 盐值:留空则自动生成
String saltB64 = _pbkdf2SaltController.text.trim();
if (saltB64.isEmpty) {
final saltResult = await CryptoBridge.generateSalt();
if (saltResult.success && saltResult.data != null) {
saltB64 = saltResult.data!;
_pbkdf2SaltController.text = saltB64;
}
}
final iterations = int.tryParse(_pbkdf2IterationsController.text) ?? 100000;
// PBKDF2-HMAC-SHA256 密钥派生,100000 次迭代
final result = await CryptoBridge.deriveKey(
password,
saltB64,
iterations: iterations,
);
if (result.success && result.data != null) {
setState(() {
_derivedKey = result.data!;
});
}
}
3.4 AES 加密/解密 Tab
展示完整的数据加密/解密流程,含 HMAC 认证校验:
dart
Future<void> _doEncrypt() async {
final data = _encryptDataController.text;
final key = _encryptKeyController.text;
if (data.isEmpty || key.isEmpty) return;
// AES-256-CBC + HMAC-SHA256 加密
final result = await CryptoBridge.encrypt(data, key);
if (result.success && result.data != null) {
setState(() => _encryptResult = result.data!);
}
}
Future<void> _doDecrypt() async {
final data = _decryptInputController.text;
final key = _decryptKeyController.text;
if (data.isEmpty || key.isEmpty) return;
// 先验证 HMAC 完整性,再解密
final result = await CryptoBridge.decrypt(data, key);
if (result.success && result.data != null) {
setState(() {});
}
}
// 加密结果一键复制到解密输入
void _copyEncryptResult() {
setState(() {
_decryptInputController.text = _encryptResult;
_decryptKeyController.text = _encryptKeyController.text;
});
}
3.5 安全存储 Tab
展示主密码保护机制下的凭据管理,包括设置/解锁/锁定、凭据的增删查:
dart
Future<void> _setupMaster() async {
final password = _masterPasswordController.text;
if (password.isEmpty) return;
// 设置主密码:派生存储密钥 + 密码哈希存储
final result = await CryptoBridge.setupMasterPassword(password);
if (result.success) {
setState(() {
_storageLoggedIn = true;
_storageStatus = '已解锁';
_storageStatusColor = Colors.green;
_masterPasswordController.clear();
});
}
}
Future<void> _saveCredential() async {
if (!_storageLoggedIn) return;
// 密码 SHA-256 + 盐值哈希存储,额外数据 AES-256-CBC 加密
final result = await CryptoBridge.saveCredential(
_credUsernameController.text,
_credPasswordController.text,
extraData: {'note': _credExtraDataController.text},
);
if (result.success) {
await _loadUsers();
}
}
Future<void> _loadUsers() async {
final users = await CryptoBridge.listUsers();
setState(() => _userList = users);
}
四、路由注册
在 main.dart 中注册密码加密演示页面的路由:
dart
import 'package:flutter/material.dart';
import 'pages/dialog_demo_page.dart';
import 'pages/clipboard_demo_page.dart';
import 'pages/countdown_demo_page.dart';
import 'pages/crypto_demo_page.dart'; // 新增导入
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter on HarmonyOS',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
fontFamily: 'HarmonyOS Sans',
),
home: const DialogDemoPage(),
routes: {
'/clipboard': (context) => const ClipboardDemoPage(),
'/countdown': (context) => const CountdownDemoPage(),
'/crypto': (context) => const CryptoDemoPage(), // 新增路由
},
);
}
}
在首页(DialogDemoPage)中新增入口按钮:
dart
_buildDemoCard(
title: "密码加密能力演示",
description: "SHA-256哈希、PBKDF2派生、AES加密、本地安全存储,"
"通过 MethodChannel 与 OpenHarmony ArkTS 原生层交互",
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/crypto');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE91E63),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: const Text("打开密码加密演示"),
),
),
),
五、OpenHarmony 原生层桥接(ArkTS)
Flutter 侧通过 MethodChannel 发起的调用,最终由 OpenHarmony 原生 ArkTS 层处理。以下为原生桥接的注册与分发逻辑的核心部分(完整实现在 ohos/entry/src/main/ets/bridge/CryptoBridge.ets 中):
typescript
// 通道名称与 Flutter 侧保持一致
const BRIDGE_CHANNEL = "com.example.oh_demo10/crypto";
// 注册 MethodChannel 处理器
registerWith(engine: FlutterEngine, context?: common.UIAbilityContext): void {
const binaryMessenger = engine.getDartExecutor().getBinaryMessenger();
this.channel = new MethodChannel(binaryMessenger, BRIDGE_CHANNEL);
this.channel.setMethodCallHandler({
onMethodCall: (call: ESObject, result: MethodResult): void => {
const c = call as MethodCallLike;
const methodName = c.method;
const args = c.args as CryptoArgs | null;
switch (methodName) {
case "hashPassword":
this.handleHashPassword(args, result);
break;
case "verifyPassword":
this.handleVerifyPassword(args, result);
break;
case "evaluatePasswordStrength":
this.handleEvaluateStrength(args, result);
break;
case "encrypt":
this.handleEncrypt(args, result);
break;
case "decrypt":
this.handleDecrypt(args, result);
break;
case "setupMasterPassword":
this.handleSetupMaster(args, result);
break;
// ... 更多方法
default:
result.notImplemented();
break;
}
}
});
}
原生层调用 CryptoService(纯 ArkTS 实现,包含完整 SHA-256、AES-256-CBC、PBKDF2、HMAC-SHA256 算法)完成实际加密运算,结果通过 result.success(map) 返回给 Flutter 侧。
六、安全设计要点
| 安全措施 | 实现方式 | 防护目标 |
|---|---|---|
| 密码加盐哈希 | SHA-256 + 16字节随机盐 | 防彩虹表攻击 |
| 恒定时间比较 | constantTimeCompare 全字节 XOR |
防时序攻击 |
| PBKDF2 密钥派生 | 100000 次 HMAC-SHA256 迭代 | 防暴力破解 |
| AES-256-CBC | 每次加密独立随机 IV | 防选择密文攻击 |
| HMAC-SHA256 认证 | 加密后计算 MAC 再解密 | 防密文篡改 |
| 主密码保护 | 主密码派生存储密钥 | 保护存储层根密钥 |
| 用户名哈希存储 | 用户名 SHA-256 后作为键 | 防用户名泄露 |
七、仓库地址
本文涉及的完整源码已开源至 AtomGit:
Flutter 侧代码位于 lib/ 目录下:
lib/
├── main.dart # 入口 + 路由注册
├── bridge/
│ └── crypto_bridge.dart # MethodChannel 桥接封装(~400行)
└── pages/
└── crypto_demo_page.dart # 密码加密演示页面(5标签,~700行)
OpenHarmony 原生层代码位于 ohos/entry/src/main/ets/ 目录下:
ets/
├── bridge/
│ └── CryptoBridge.ets # MethodChannel 桥接层
├── service/
│ ├── CryptoService.ets # 密码加密核心(~1300行,纯 ArkTS)
│ └── SecureStorageService.ets # 本地安全存储
└── pages/
└── PasswordDemoPage.ets # 原生侧密码演示页面
截图验证(小窗界面):

八、结语
本文围绕 Flutter for OpenHarmony 跨平台技术,以 Flutter Dart 侧代码为主线 ,完整展示了从 MethodChannel 桥接封装、结果模型定义,到演示页面各功能 Tab 的完整实现。Flutter 侧通过类型安全的 Dart 类封装了所有原生加密能力调用,业务代码只需要 CryptoBridge.hashPassword(password) 或 CryptoBridge.encrypt(data, key) 即可完成复杂的加密操作,无需关心底层实现细节。
OpenHarmony 原生层 CryptoService 以纯 ArkTS 编写,不依赖任何外部加密库,确保了代码的可移植性和可审计性;SecureStorageService 借助 HarmonyOS 原生 dataPreferences 实现本地持久化,两者共同为 Flutter 跨平台应用提供了企业级的密码加密与安全存储能力。