欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。
一、引言:为什么需要在鸿蒙 Flutter 中集成 SM4 算法?
在移动应用开发中,数据安全是不可忽视的核心环节,尤其是涉及用户隐私(如手机号、身份证号)、敏感业务数据(如交易记录)的场景。国密算法(国家商用密码算法)是我国自主研发的密码标准,具备高安全性、合规性,已成为金融、政务、能源等关键领域的强制要求。
其中,SM4 算法(GB/T 32905-2016 标准)是一种分组对称加密算法,采用 128 位密钥、128 位分组长度,支持 ECB、CBC、CFB、OFB 等多种工作模式,适用于数据存储加密(如本地缓存加密)和数据传输加密(如接口请求体加密)。
而 鸿蒙(HarmonyOS)+ Flutter 的组合是跨端开发的热门方案:鸿蒙提供了分布式能力和安全子系统,Flutter 则实现了高效的跨端 UI 渲染。本文将详细讲解如何在鸿蒙 Flutter 应用中集成 SM4 算法,实现「本地加密存储」和「网络加密传输」,并附带完整可运行代码。
1.1 参考文档与依赖工具
在开始前,建议先了解以下官方文档和工具,确保开发环境合规且高效:
- 国密 SM4 官方标准:GB/T 32905-2016 信息安全技术 SM4 分组密码算法
- 鸿蒙安全子系统:HarmonyOS 安全开发指南
- Flutter 平台通信:Flutter MethodChannel 文档
- 依赖库:
- Flutter 端:
flutter_secure_storage(安全存储密钥)、dio(网络请求) - 鸿蒙端:
bcprov-jdk15on(BouncyCastle,提供 SM4 实现)
- Flutter 端:
二、前置知识:SM4 算法核心概念
在写代码前,需明确 SM4 的几个关键特性,避免开发中踩坑:
- 密钥长度:固定 128 位(16 字节),生成时需确保长度合规,不可随意设置;
- 分组长度:固定 128 位,加密时会将数据按 16 字节拆分,不足则需填充(推荐 PKCS#7 填充);
- 工作模式 :
- 不推荐
ECB模式(相同明文加密后密文相同,安全性低); - 推荐
CBC模式(需额外生成 16 字节 IV 向量,每次加密 IV 需不同,密文更安全);
- 不推荐
- 数据编码:加密后输出二进制数据,需用 Base64 编码转为字符串,方便存储和传输。
三、开发环境搭建
3.1 鸿蒙端环境(DevEco Studio)
- 下载并安装 DevEco Studio 4.0+,配置鸿蒙 SDK 8.0+;
- 在鸿蒙模块的
build.gradle中添加 BouncyCastle 依赖(提供 SM4 算法实现):
gradle
dependencies {
// 其他依赖...
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
}
- 配置权限:在
module.json5中添加网络权限和存储权限(用于加密存储):
json
{
"module": {
"abilities": [...],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET" // 网络请求权限
},
{
"name": "ohos.permission.GET_WIFI_INFO" // 可选,若需网络加密传输
},
{
"name": "ohos.permission.READ_USER_STORAGE" // 存储读取权限
},
{
"name": "ohos.permission.WRITE_USER_STORAGE" // 存储写入权限
}
]
}
}
3.2 Flutter 端环境
- 确保 Flutter 版本 ≥ 3.16.0,执行
flutter --version检查; - 在
pubspec.yaml中添加依赖:
yaml
dependencies:
flutter:
sdk: flutter
# 安全存储密钥(替代 SharedPreferences,避免密钥明文存储)
flutter_secure_storage: ^8.1.0
# 网络请求库(用于加密传输演示)
dio: ^5.4.0
# Base64 编码(可选,Flutter 自带也可,但此库更易用)
base64: ^3.1.1
- 执行
flutter pub get安装依赖。
四、核心实现:SM4 加密与解密(鸿蒙原生层)
由于 Flutter 本身没有官方的国密算法库,且直接在 Dart 层实现 SM4 可能存在性能和安全性问题,推荐通过 MethodChannel 调用鸿蒙原生层的 SM4 实现(利用鸿蒙安全子系统和 BouncyCastle 库)。
4.1 鸿蒙端 SM4 工具类(Kotlin)
首先实现鸿蒙端的 SM4 加密 / 解密工具类,封装密钥生成、CBC 模式加密、CBC 模式解密逻辑:
kotlin
import org.bouncycastle.crypto.engines.SM4Engine
import org.bouncycastle.crypto.modes.CBCBlockCipher
import org.bouncycastle.crypto.paddings.PKCS7Padding
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher
import org.bouncycastle.crypto.params.KeyParameter
import org.bouncycastle.crypto.params.ParametersWithIV
import java.security.SecureRandom
import java.util.Base64
/**
* 鸿蒙端 SM4 工具类(CBC 模式 + PKCS7 填充)
*/
object SM4Utils {
// SM4 密钥长度:128 位(16 字节)
private const val KEY_SIZE = 16
// SM4 分组长度(CBC 模式 IV 长度与分组长度一致)
private const val IV_SIZE = 16
// 字符编码
private const val CHARSET = "UTF-8"
/**
* 生成 128 位(16 字节)SM4 密钥(Base64 编码)
*/
fun generateSm4Key(): String {
val key = ByteArray(KEY_SIZE)
// 使用鸿蒙安全随机数生成器(避免伪随机)
val secureRandom = SecureRandom()
secureRandom.nextBytes(key)
// 转为 Base64 字符串,方便存储和传输
return Base64.getEncoder().encodeToString(key)
}
/**
* SM4 加密(CBC 模式 + PKCS7 填充)
* @param plainText 明文(待加密字符串)
* @param keyBase64 Base64 编码的密钥
* @return 加密结果(格式:IV_BASE64:ENCRYPTED_DATA_BASE64)
*/
fun encrypt(plainText: String, keyBase64: String): String {
try {
// 1. 解码密钥和明文
val key = Base64.getDecoder().decode(keyBase64)
val plainData = plainText.toByteArray(charset(CHARSET))
// 2. 生成随机 IV(16 字节)
val iv = ByteArray(IV_SIZE)
SecureRandom().nextBytes(iv)
val ivBase64 = Base64.getEncoder().encodeToString(iv)
// 3. 初始化 SM4 CBC 加密器(PKCS7 填充)
val sm4Engine = SM4Engine()
val cbcBlockCipher = CBCBlockCipher(sm4Engine)
val paddedCipher = PaddedBufferedBlockCipher(cbcBlockCipher, PKCS7Padding())
// 传入密钥和 IV 参数
val keyParam = KeyParameter(key)
val ivParam = ParametersWithIV(keyParam, iv)
paddedCipher.init(true, ivParam) // true 表示加密模式
// 4. 执行加密
val output = ByteArray(paddedCipher.getOutputSize(plainData.size))
var outputLen = paddedCipher.processBytes(plainData, 0, plainData.size, output, 0)
outputLen += paddedCipher.doFinal(output, outputLen)
// 5. 加密结果 = IVBase64:加密数据Base64(方便解密时提取 IV)
val encryptedDataBase64 = Base64.getEncoder().encodeToString(output.copyOf(outputLen))
return "$ivBase64:$encryptedDataBase64"
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("SM4 加密失败:${e.message}")
}
}
/**
* SM4 解密(CBC 模式 + PKCS7 填充)
* @param encryptedStr 加密结果(格式:IV_BASE64:ENCRYPTED_DATA_BASE64)
* @param keyBase64 Base64 编码的密钥
* @return 解密后的明文
*/
fun decrypt(encryptedStr: String, keyBase64: String): String {
try {
// 1. 拆分 IV 和加密数据
val parts = encryptedStr.split(":", limit = 2)
if (parts.size != 2) {
throw IllegalArgumentException("加密格式错误,需满足 IV_BASE64:ENCRYPTED_DATA_BASE64")
}
val ivBase64 = parts[0]
val encryptedDataBase64 = parts[1]
// 2. 解码密钥、IV、加密数据
val key = Base64.getDecoder().decode(keyBase64)
val iv = Base64.getDecoder().decode(ivBase64)
val encryptedData = Base64.getDecoder().decode(encryptedDataBase64)
// 3. 初始化 SM4 CBC 解密器(PKCS7 填充)
val sm4Engine = SM4Engine()
val cbcBlockCipher = CBCBlockCipher(sm4Engine)
val paddedCipher = PaddedBufferedBlockCipher(cbcBlockCipher, PKCS7Padding())
val keyParam = KeyParameter(key)
val ivParam = ParametersWithIV(keyParam, iv)
paddedCipher.init(false, ivParam) // false 表示解密模式
// 4. 执行解密
val output = ByteArray(paddedCipher.getOutputSize(encryptedData.size))
var outputLen = paddedCipher.processBytes(encryptedData, 0, encryptedData.size, output, 0)
outputLen += paddedCipher.doFinal(output, outputLen)
// 5. 解码为明文
return String(output.copyOf(outputLen), charset(CHARSET))
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("SM4 解密失败:${e.message}")
}
}
}
4.2 鸿蒙端 MethodChannel 通信实现
通过 MethodChannel 暴露「生成密钥」「加密」「解密」方法给 Flutter 端,实现跨平台通信:
kotlin
import ohos.aafwk.ability.Ability
import ohos.aafwk.content.Intent
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class MainAbility : Ability() {
// 定义 MethodChannel 名称(需与 Flutter 端一致)
private val CHANNEL_NAME = "com.example.harmony_sm4/channel"
override fun onStart(intent: Intent?) {
super.onStart(intent)
// 初始化 Flutter Engine 时注册 MethodChannel
val flutterEngine = FlutterEngine(this)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME)
.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
// 1. 生成 SM4 密钥
"generateSm4Key" -> {
val sm4Key = SM4Utils.generateSm4Key()
result.success(sm4Key)
}
// 2. SM4 加密
"sm4Encrypt" -> {
val plainText = call.argument<String>("plainText") ?: ""
val keyBase64 = call.argument<String>("keyBase64") ?: ""
try {
val encryptedStr = SM4Utils.encrypt(plainText, keyBase64)
result.success(encryptedStr)
} catch (e: Exception) {
result.error("ENCRYPT_ERROR", e.message, null)
}
}
// 3. SM4 解密
"sm4Decrypt" -> {
val encryptedStr = call.argument<String>("encryptedStr") ?: ""
val keyBase64 = call.argument<String>("keyBase64") ?: ""
try {
val plainText = SM4Utils.decrypt(encryptedStr, keyBase64)
result.success(plainText)
} catch (e: Exception) {
result.error("DECRYPT_ERROR", e.message, null)
}
}
// 未知方法
else -> result.notImplemented()
}
}
}
}
五、Flutter 端实现:调用鸿蒙 SM4 能力
Flutter 端通过 MethodChannel 调用鸿蒙原生的 SM4 方法,并结合 flutter_secure_storage 实现密钥安全存储,结合 dio 实现加密数据传输。
5.1 Flutter 端 SM4 工具类(Dart)
封装 MethodChannel 调用逻辑,对外提供简洁的 API:
dart
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
/**
* Flutter 端 SM4 工具类(通过 MethodChannel 调用鸿蒙原生能力)
*/
class FlutterSm4Utils {
// 与鸿蒙端一致的 MethodChannel 名称
static const MethodChannel _channel = MethodChannel("com.example.harmony_sm4/channel");
// 安全存储实例(用于存储 SM4 密钥,避免明文存储)
static const FlutterSecureStorage _secureStorage = FlutterSecureStorage();
// 存储密钥的 Key(本地持久化)
static const String _sm4KeyStorageKey = "harmony_sm4_key";
/**
* 初始化 SM4 密钥:
* - 首次启动时生成密钥并存储到安全存储;
* - 非首次启动时从安全存储读取密钥。
*/
static Future<String> initSm4Key() async {
try {
// 1. 尝试从安全存储读取已存在的密钥
String? existingKey = await _secureStorage.read(key: _sm4KeyStorageKey);
if (existingKey != null && existingKey.isNotEmpty) {
print("SM4 密钥已存在,直接使用");
return existingKey;
}
// 2. 首次启动,调用鸿蒙端生成密钥
String newKey = await _channel.invokeMethod("generateSm4Key");
// 3. 将新密钥存储到安全存储(持久化)
await _secureStorage.write(key: _sm4KeyStorageKey, value: newKey);
print("SM4 密钥生成并存储成功:$newKey");
return newKey;
} catch (e) {
throw Exception("SM4 密钥初始化失败:$e");
}
}
/**
* SM4 加密(调用鸿蒙端实现)
* @param plainText 明文
* @param keyBase64 SM4 密钥(Base64 编码)
* @return 加密结果(格式:IV_BASE64:ENCRYPTED_DATA_BASE64)
*/
static Future<String> encrypt(String plainText, String keyBase64) async {
try {
if (plainText.isEmpty || keyBase64.isEmpty) {
throw Exception("明文和密钥不可为空");
}
// 调用鸿蒙端加密方法
String encryptedStr = await _channel.invokeMethod(
"sm4Encrypt",
{"plainText": plainText, "keyBase64": keyBase64},
);
return encryptedStr;
} on PlatformException catch (e) {
throw Exception("SM4 加密失败:${e.message}");
}
}
/**
* SM4 解密(调用鸿蒙端实现)
* @param encryptedStr 加密结果(格式:IV_BASE64:ENCRYPTED_DATA_BASE64)
* @param keyBase64 SM4 密钥(Base64 编码)
* @return 解密后的明文
*/
static Future<String> decrypt(String encryptedStr, String keyBase64) async {
try {
if (encryptedStr.isEmpty || keyBase64.isEmpty) {
throw Exception("加密串和密钥不可为空");
}
// 调用鸿蒙端解密方法
String plainText = await _channel.invokeMethod(
"sm4Decrypt",
{"encryptedStr": encryptedStr, "keyBase64": keyBase64},
);
return plainText;
} on PlatformException catch (e) {
throw Exception("SM4 解密失败:${e.message}");
}
}
/**
* 清除本地存储的 SM4 密钥(仅用于测试,生产环境慎用)
*/
static Future<void> clearSm4Key() async {
await _secureStorage.delete(key: _sm4KeyStorageKey);
print("SM4 密钥已清除");
}
}
5.2 场景 1:SM4 加密本地存储
以存储「用户登录信息」为例,演示如何将敏感数据加密后存储到本地:
dart
import 'package:flutter/material.dart';
import 'package:harmony_sm4/flutter_sm4_utils.dart';
class LocalEncryptionDemo extends StatefulWidget {
const LocalEncryptionDemo({super.key});
@override
State<LocalEncryptionDemo> createState() => _LocalEncryptionDemoState();
}
class _LocalEncryptionDemoState extends State<LocalEncryptionDemo> {
// SM4 密钥
String? _sm4Key;
// 明文(用户信息)
final TextEditingController _userInfoController = TextEditingController(
text: '{"userId": "123456", "userName": "张三", "phone": "13800138000"}',
);
// 加密结果
String? _encryptedResult;
// 解密结果
String? _decryptedResult;
@override
void initState() {
super.initState();
// 初始化 SM4 密钥
_initSm4Key();
}
// 初始化 SM4 密钥
Future<void> _initSm4Key() async {
String key = await FlutterSm4Utils.initSm4Key();
setState(() {
_sm4Key = key;
});
}
// 加密按钮点击事件
Future<void> _onEncryptClick() async {
if (_sm4Key == null) return;
String plainText = _userInfoController.text.trim();
String encrypted = await FlutterSm4Utils.encrypt(plainText, _sm4Key!);
setState(() {
_encryptedResult = encrypted;
_decryptedResult = null; // 重置解密结果
});
}
// 解密按钮点击事件
Future<void> _onDecryptClick() async {
if (_sm4Key == null || _encryptedResult == null) return;
String decrypted = await FlutterSm4Utils.decrypt(_encryptedResult!, _sm4Key!);
setState(() {
_decryptedResult = decrypted;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("SM4 本地加密存储演示")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 明文输入框
const Text("明文(用户信息):"),
const SizedBox(height: 8),
TextField(
controller: _userInfoController,
minLines: 3,
maxLines: 5,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "请输入待加密的敏感数据",
),
),
const SizedBox(height: 16),
// 加密按钮
ElevatedButton(
onPressed: _onEncryptClick,
child: const Text("SM4 加密"),
),
const SizedBox(height: 16),
// 加密结果
if (_encryptedResult != null) ...[
const Text("加密结果(IV:密文):"),
const SizedBox(height: 8),
Expanded(
child: SingleChildScrollView(
child: Text(_encryptedResult!),
),
),
const SizedBox(height: 16),
// 解密按钮
ElevatedButton(
onPressed: _onDecryptClick,
child: const Text("SM4 解密"),
),
],
const SizedBox(height: 16),
// 解密结果
if (_decryptedResult != null) ...[
const Text("解密结果:"),
const SizedBox(height: 8),
Expanded(
child: SingleChildScrollView(
child: Text(_decryptedResult!),
),
),
],
],
),
),
);
}
}
5.3 场景 2:SM4 加密数据传输
以「用户登录接口」为例,演示如何将请求体加密后发送,接收响应后解密:
5.3.1 第一步:实现 Dio 拦截器(自动加密请求 / 解密响应)
dart
import 'package:dio/dio.dart';
import 'package:harmony_sm4/flutter_sm4_utils.dart';
/**
* SM4 网络拦截器:
* - 请求拦截:自动加密请求体(JSON 格式)
* - 响应拦截:自动解密响应体
*/
class Sm4HttpInterceptor extends Interceptor {
final String _sm4Key;
// 构造函数:传入 SM4 密钥
Sm4HttpInterceptor(this._sm4Key);
// 请求拦截(加密请求体)
@override
Future<void> onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
try {
// 仅对 POST/PUT 请求的 JSON 体进行加密
if ((options.method == "POST" || options.method == "PUT") &&
options.data != null) {
// 1. 将请求体转为 JSON 字符串
String requestBodyJson = options.data is String
? options.data
: options.data.toString(); // 若为 Map,可先转为 JSON 字符串
// 2. SM4 加密请求体
String encryptedBody = await FlutterSm4Utils.encrypt(requestBodyJson, _sm4Key);
// 3. 替换请求体为加密后的字符串
options.data = {
"encryptedData": encryptedBody, // 后端需解析此字段
"algorithm": "SM4-CBC-PKCS7" // 告知后端加密算法
};
// 4. 设置 Content-Type(避免后端解析错误)
options.contentType = Headers.jsonContentType;
print("请求体加密完成:${options.data}");
}
handler.next(options);
} catch (e) {
handler.reject(DioException(
requestOptions: options,
error: "请求体加密失败:$e",
));
}
}
// 响应拦截(解密响应体)
@override
Future<void> onResponse(
Response response,
ResponseInterceptorHandler handler,
) async {
try {
// 1. 获取后端返回的加密数据(假设后端返回格式:{"encryptedData": "..."})
if (response.data is Map && response.data["encryptedData"] != null) {
String encryptedResponse = response.data["encryptedData"];
// 2. SM4 解密响应体
String decryptedResponse = await FlutterSm4Utils.decrypt(encryptedResponse, _sm4Key);
// 3. 替换响应体为解密后的 JSON 字符串(若需转为 Map,可进一步解析)
response.data = decryptedResponse;
print("响应体解密完成:$decryptedResponse");
}
handler.next(response);
} catch (e) {
handler.reject(DioException(
requestOptions: response.requestOptions,
error: "响应体解密失败:$e",
));
}
}
}
5.3.2 第二步:使用拦截器发起加密请求
dart
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:harmony_sm4/flutter_sm4_utils.dart';
import 'package:harmony_sm4/sm4_http_interceptor.dart';
class NetworkEncryptionDemo extends StatefulWidget {
const NetworkEncryptionDemo({super.key});
@override
State<NetworkEncryptionDemo> createState() => _NetworkEncryptionDemoState();
}
class _NetworkEncryptionDemoState extends State<NetworkEncryptionDemo> {
String? _sm4Key;
final Dio _dio = Dio();
// 登录参数
final TextEditingController _phoneController = TextEditingController(text: "13800138000");
final TextEditingController _passwordController = TextEditingController(text: "12345678");
// 请求结果
String? _requestResult;
@override
void initState() {
super.initState();
// 初始化 SM4 密钥并配置 Dio 拦截器
_initSm4KeyAndDio();
}
// 初始化 SM4 密钥 + 配置 Dio 拦截器
Future<void> _initSm4KeyAndDio() async {
String key = await FlutterSm4Utils.initSm4Key();
setState(() {
_sm4Key = key;
});
// 添加 SM4 拦截器
_dio.interceptors.add(Sm4HttpInterceptor(key));
// 配置基础 URL(替换为你的后端接口地址)
_dio.options.baseUrl = "https://api.example.com";
_dio.options.connectTimeout = const Duration(seconds: 10);
}
// 发起加密登录请求
Future<void> _sendEncryptedLoginRequest() async {
if (_sm4Key == null) return;
try {
// 1. 构造登录请求体(明文)
Map<String, dynamic> loginBody = {
"phone": _phoneController.text.trim(),
"password": _passwordController.text.trim(),
"timestamp": DateTime.now().millisecondsSinceEpoch // 时间戳(防重放)
};
// 2. 发起 POST 请求(拦截器会自动加密请求体)
Response response = await _dio.post(
"/user/login",
data: loginBody, // 明文请求体(拦截器会自动加密)
);
// 3. 响应已被拦截器自动解密,直接使用
setState(() {
_requestResult = "登录成功!\n解密后的响应:\n${response.data}";
});
} on DioException catch (e) {
setState(() {
_requestResult = "登录失败:\n${e.message}";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("SM4 加密数据传输演示")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 手机号输入
TextField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: "手机号",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
// 密码输入
TextField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: "密码",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 24),
// 发起请求按钮
ElevatedButton(
onPressed: _sendEncryptedLoginRequest,
child: const Text("发起加密登录请求"),
),
const SizedBox(height: 24),
// 请求结果
const Text("请求结果:"),
const SizedBox(height: 8),
Expanded(
child: SingleChildScrollView(
child: Text(_requestResult ?? "未发起请求"),
),
),
],
),
),
);
}
}
六、常见问题与解决方案
6.1 密钥安全问题:避免硬编码或明文存储
- 问题 :将 SM4 密钥硬编码在代码中,或用
SharedPreferences明文存储,存在密钥泄露风险; - 解决方案 :使用
flutter_secure_storage(鸿蒙端底层调用鸿蒙 KeyStore)存储密钥,确保密钥仅在设备安全硬件中存储,无法被恶意读取。
6.2 加密格式错误:IV 与密文拆分失败
- 问题:解密时抛出「加密格式错误」,原因是加密结果格式不正确;
- 解决方案 :严格遵循「IV_BASE64:ENCRYPTED_DATA_BASE64」格式,拆分时用
split(":", limit = 2)(避免 IV 或密文中包含:导致拆分错误)。
6.3 鸿蒙端权限问题:存储 / 网络权限被拒
- 问题:加密存储时提示「权限不足」,或网络请求失败;
- 解决方案 :
- 在
module.json5中配置完整的存储和网络权限(见 3.1 节); - 鸿蒙 6.0+ 需动态申请危险权限(如存储权限),可通过
ohos.permission.RuntimePermission动态申请。
- 在
6.4 跨平台数据一致性问题:Base64 编码差异
- 问题:Flutter 端 Base64 编码与鸿蒙端不一致,导致解密失败;
- 解决方案 :统一使用「标准 Base64 编码」,鸿蒙端用
java.util.Base64,Flutter 端用dart:convert或base64库,避免使用 URL-Safe Base64。
七、总结与扩展
本文详细讲解了鸿蒙 Flutter 应用中集成 SM4 算法的完整流程,包括:
- 鸿蒙端 SM4 工具类实现(CBC 模式 + PKCS7 填充);
- MethodChannel 跨平台通信;
- Flutter 端密钥安全存储与加密 / 解密调用;
- 本地加密存储和网络加密传输两个核心场景。
后续扩展方向:
- 认证加密:当前 SM4 仅提供加密,可扩展为 GCM 模式(带认证标签),防止数据被篡改;
- 密钥轮换:实现密钥定期轮换机制,进一步提升安全性;
- 鸿蒙分布式能力:结合鸿蒙分布式数据管理,实现多设备间加密数据共享;
- 后端配合:本文仅覆盖客户端加密,需后端同步实现 SM4 解密逻辑(可参考客户端算法,使用相同的密钥、模式、填充方式)。
完整代码已上传至 GitHub:harmonyos-flutter-sm4-demo(替换为你的仓库地址),可直接克隆运行,如需进一步定制,可根据实际业务场景调整代码。


