【maaath】Flutter for OpenHarmony 跨平台工程集成密码加密能力

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 跨平台应用提供了企业级的密码加密与安全存储能力。

相关推荐
yeziyfx1 小时前
Flutter 纯色矩形
flutter
liulian09161 小时前
Flutter for OpenHarmony 混合开发实践:用户反馈功能的实现与适配
flutter·华为·学习方法·harmonyos
Hello__77773 小时前
开源鸿蒙 Flutter 实战|文章分类标签功能全流程实现
flutter·开源·harmonyos
xiaoyan20153 小时前
2026爆肝!Flutter3.41纯手撸微信聊天APP原生应用
android·flutter·dart
GitCode官方4 小时前
一声唤醒 万物响应|AtomGit 首款开源鸿蒙 AI 硬件「小鸿」发布会圆满落幕 定义智能交互新入口
人工智能·开源·harmonyos
程序员老刘4 小时前
当全网都在喊“程序员要被AI取代了”,Flutter给了另一种答案
flutter·ai编程·客户端
国医中兴4 小时前
Flutter 三方库 nhost_graphql_adapter 的鸿蒙化适配指南 - 云端数据实时对齐、GraphQL 架构实战、鸿蒙级全栈交互专家
flutter·harmonyos·graphql
nashane4 小时前
HarmonyOS 6学习:RCP远场通信流式返回实战——告别“一次性”数据阻塞
学习·华为·harmonyos
李游Leo4 小时前
别把耗时任务都丢进 async:HarmonyOS 里 TaskPool 和 Worker 的边界感
harmonyos