鸿蒙 Flutter 国密算法应用:SM4 加密存储与数据传输

欢迎大家加入[开源鸿蒙跨平台开发者社区](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 算法核心概念

在写代码前,需明确 SM4 的几个关键特性,避免开发中踩坑:

  1. 密钥长度:固定 128 位(16 字节),生成时需确保长度合规,不可随意设置;
  2. 分组长度:固定 128 位,加密时会将数据按 16 字节拆分,不足则需填充(推荐 PKCS#7 填充);
  3. 工作模式
    • 不推荐 ECB 模式(相同明文加密后密文相同,安全性低);
    • 推荐 CBC 模式(需额外生成 16 字节 IV 向量,每次加密 IV 需不同,密文更安全);
  4. 数据编码:加密后输出二进制数据,需用 Base64 编码转为字符串,方便存储和传输。

三、开发环境搭建

3.1 鸿蒙端环境(DevEco Studio)

  1. 下载并安装 DevEco Studio 4.0+,配置鸿蒙 SDK 8.0+;
  2. 在鸿蒙模块的 build.gradle 中添加 BouncyCastle 依赖(提供 SM4 算法实现):

gradle

复制代码
dependencies {
    // 其他依赖...
    implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
}
  1. 配置权限:在 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 端环境

  1. 确保 Flutter 版本 ≥ 3.16.0,执行 flutter --version 检查;
  2. pubspec.yaml 中添加依赖:

yaml

复制代码
dependencies:
  flutter:
    sdk: flutter
  # 安全存储密钥(替代 SharedPreferences,避免密钥明文存储)
  flutter_secure_storage: ^8.1.0
  # 网络请求库(用于加密传输演示)
  dio: ^5.4.0
  # Base64 编码(可选,Flutter 自带也可,但此库更易用)
  base64: ^3.1.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 鸿蒙端权限问题:存储 / 网络权限被拒

  • 问题:加密存储时提示「权限不足」,或网络请求失败;
  • 解决方案
    1. module.json5 中配置完整的存储和网络权限(见 3.1 节);
    2. 鸿蒙 6.0+ 需动态申请危险权限(如存储权限),可通过 ohos.permission.RuntimePermission 动态申请。

6.4 跨平台数据一致性问题:Base64 编码差异

  • 问题:Flutter 端 Base64 编码与鸿蒙端不一致,导致解密失败;
  • 解决方案 :统一使用「标准 Base64 编码」,鸿蒙端用 java.util.Base64,Flutter 端用 dart:convertbase64 库,避免使用 URL-Safe Base64。

七、总结与扩展

本文详细讲解了鸿蒙 Flutter 应用中集成 SM4 算法的完整流程,包括:

  1. 鸿蒙端 SM4 工具类实现(CBC 模式 + PKCS7 填充);
  2. MethodChannel 跨平台通信;
  3. Flutter 端密钥安全存储与加密 / 解密调用;
  4. 本地加密存储和网络加密传输两个核心场景。

后续扩展方向:

  1. 认证加密:当前 SM4 仅提供加密,可扩展为 GCM 模式(带认证标签),防止数据被篡改;
  2. 密钥轮换:实现密钥定期轮换机制,进一步提升安全性;
  3. 鸿蒙分布式能力:结合鸿蒙分布式数据管理,实现多设备间加密数据共享;
  4. 后端配合:本文仅覆盖客户端加密,需后端同步实现 SM4 解密逻辑(可参考客户端算法,使用相同的密钥、模式、填充方式)。

完整代码已上传至 GitHub:harmonyos-flutter-sm4-demo(替换为你的仓库地址),可直接克隆运行,如需进一步定制,可根据实际业务场景调整代码。

相关推荐
Chasing__Dreams33 分钟前
kafka--基础知识点--3.2--消息的磁盘存储文件结构
分布式·kafka
程序员-King.34 分钟前
day120—二分查找—统计公平数对的数目(LeetCode-2563)
算法·leetcode·二分查找·双指针
Yupureki36 分钟前
《算法竞赛从入门到国奖》算法基础:入门篇-枚举
c语言·数据结构·c++·算法·visual studio
脸大是真的好~37 分钟前
Kafka相关面试题
分布式·kafka
雨季66638 分钟前
蓝桥杯试题及详解文档:统计子矩阵的和等于目标值的数量
算法
怪侠Kevin38 分钟前
seata事务集成kafka
数据库·分布式·kafka
子夜江寒42 分钟前
线性回归与KNN算法的核心原理及实践应用
算法·回归·线性回归
中杯可乐多加冰43 分钟前
深度解析openFuyao核心组件:从NUMA亲和调度看云原生算力释放新思路
华为·云原生·k8s·gpu算力·openfuyao
MicroTech202543 分钟前
微算法科技(NASDAQ MLGO)采用混合深度学习赋能区块链:打造智慧城市安全新范式
科技·深度学习·算法