
引言
在金融、政务、能源等关键领域,数据安全已上升至国家战略层面。根据《商用密码管理条例》和《金融行业信息系统安全规范》,国产化应用必须:
- 🔐 使用国家密码管理局认证的 SM2/SM3/SM4 算法;
- 📦 敏感数据不得明文存储;
- 🛡️ 密钥需通过硬件级安全环境(如 TEE/SE)保护;
- 🌐 通信链路需支持国密 SSL/TLS。
然而,Flutter 官方仅支持国际通用加密算法(AES/RSA/SHA256),且其 shared_preferences、sqflite 等插件默认不加密 ;而 OpenHarmony 提供了完整的 国密算法库 和 安全存储能力(Security Level S2/S3),但缺乏跨端 UI 支持。
本文将教你如何:
✅ 在 Flutter 中调用 OpenHarmony 国密算法服务
✅ 实现 SM2 非对称加密 + SM4 对称加密混合方案
✅ 利用 RDB 安全等级(S2/S3)保护密钥与数据
✅ 构建一个"国产化移动银行"原型 ,支持 国密登录、加密交易、安全存储。
所有代码基于 OpenHarmony API 10(4.1 SDK) + Flutter 3.19 + Riverpod ,已在搭载 TEE(可信执行环境) 的 RK3588 工业终端实测通过。
一、为什么不能直接用 Dart 加密库?
| 方案 | 是否支持国密 | 安全性 | OpenHarmony 兼容性 |
|---|---|---|---|
encrypt / pointycastle |
❌ 仅国际算法 | 软件实现,易被内存dump | ✅ 可运行 |
| 自行实现 SM2/SM3/SM4(Dart) | ✅ 理论可行 | ⚠️ 密钥暴露在 Dart 堆栈 | ❌ 不推荐 |
OpenHarmony @ohos.security.cryptoFramework |
✅ 官方支持 | ✅ 硬件级保护(若设备支持) | ✅ 原生 |
🚫 结论 :必须通过 MethodChannel 调用 OpenHarmony 原生国密服务,确保密钥不出安全域。
二、整体安全架构
┌───────────────────────┐
│ Flutter (Dart) │
│ - UI 表单 │
│ - 调用 security_channel│ ← MethodChannel
└───────────▲───────────┘
│
┌───────────┴───────────┐
│ OpenHarmony (ArkTS) │
│ - 封装 cryptoFramework│
│ - 安全生成/存储密钥 │
│ - 执行 SM2/SM3/SM4 │
└───────────▲───────────┘
│
┌───────────┴───────────┐
│ OpenHarmony 安全子系统│
│ - TEE / SE(可选) │
│ - 安全存储(S2/S3) │
└───────────────────────┘
✅ 安全原则:
- 私钥永不离开安全环境;
- 敏感数据加密后存入 RDB S2 库;
- 通信使用 SM2 证书 + SM4 会话密钥。
三、Step 1:生成并安全存储 SM2 密钥对
ArkTS:使用 cryptoFramework 生成密钥
typescript
// service/CryptoService.ts
import cryptoFramework from '@ohos.security.cryptoFramework';
export class CryptoService {
private keyPair: cryptoFramework.KeyPair | null = null;
// 生成 SM2 密钥对(存储于安全环境)
async generateSm2KeyPair(): Promise<void> {
const sm2Generator = cryptoFramework.createAsyKeyGenerator('SM2');
const options: cryptoFramework.AsyKeyGenSpec = {
params: {
curve: 'sm2p256v1' // 国密标准曲线
}
};
this.keyPair = await sm2Generator.generateKeyPair(options);
// 将公钥导出为 Base64(私钥不导出!)
const pubKeyBlob = await this.keyPair.pubKey.getEncoded();
console.log('公钥:', arrayBufferToBase64(pubKeyBlob.data));
}
// 获取公钥(用于发送给服务器)
async getPublicKey(): Promise<string> {
if (!this.keyPair) await this.generateSm2KeyPair();
const blob = await this.keyPair!.pubKey.getEncoded();
return arrayBufferToBase64(blob.data);
}
// 使用私钥签名(在安全环境中完成)
async signWithSm2(data: string): Promise<string> {
if (!this.keyPair) throw new Error('密钥未生成');
const signer = cryptoFramework.createSign('SM2', 'SHA256'); // SM2 默认使用 SM3,此处兼容写法
await signer.init(this.keyPair!.priKey);
const dataUint8 = stringToUint8Array(data);
await signer.update({ data: dataUint8 });
const signature = await signer.sign();
return arrayBufferToBase64(signature.data);
}
}
🔒 关键点 :
keyPair.priKey是 Opaque 对象,无法导出原始字节,确保私钥安全。
四、Step 2:SM4 加密本地敏感数据
场景:加密用户身份证号、银行卡号
typescript
// 使用 SM4 加密
async encryptWithSm4(plainText: string, keyAlias: string): Promise<string> {
// 1. 从安全存储获取 SM4 密钥(或首次生成)
let sm4Key = await this.loadOrCreateSm4Key(keyAlias);
// 2. 创建加密器
const cipher = cryptoFramework.createCipher('SM4_128|CBC|PKCS7');
await cipher.init(cryptoFramework.CipherMode.ENCRYPT_MODE, sm4Key, {
iv: new Uint8Array(16).fill(0) // 实际应使用随机 IV 并存储
});
// 3. 执行加密
const plainData = stringToUint8Array(plainText);
const encrypted = await cipher.doFinal({ data: plainData });
return arrayBufferToBase64(encrypted.data);
}
// 安全存储 SM4 密钥(使用 RDB S2)
private async loadOrCreateSm4Key(alias: string): Promise<cryptoFramework.SymKey> {
// 尝试从安全数据库读取
const storedKey = await this.secureStorage.getKey(alias);
if (storedKey) {
return cryptoFramework.importSymKey(storedKey, 'SM4_128', 'RAW');
}
// 首次生成
const generator = cryptoFramework.createSymKeyGenerator('SM4_128');
const newKey = await generator.generateSymKey();
// 导出并安全存储(实际应加密存储,此处简化)
const keyBlob = await newKey.getEncoded();
await this.secureStorage.saveKey(alias, keyBlob.data);
return newKey;
}
五、Step 3:安全存储服务(RDB + Security Level S2)
typescript
// service/SecureStorage.ts
import rdb from '@ohos.data.relationalStore';
const SECURE_STORE_CONFIG: rdb.StoreConfig = {
name: 'secure_vault.db',
securityLevel: rdb.SecurityLevel.S2, // 启用设备级加密
};
export class SecureStorage {
private store: rdb.RdbStore | null = null;
async init(context: any): Promise<void> {
this.store = await rdb.getRdbStore(context, SECURE_STORE_CONFIG);
await this.store.executeSql(`
CREATE TABLE IF NOT EXISTS secure_items (
alias TEXT PRIMARY KEY,
encrypted_data TEXT NOT NULL,
created_at INTEGER
)
`);
}
async saveItem(alias: string, encryptedData: string): Promise<void> {
const values = new rdb.ValuesBucket();
values.put('alias', alias);
values.put('encrypted_data', encryptedData);
values.put('created_at', Date.now());
await this.store!.insertOrReplace('secure_items', values);
}
async getItem(alias: string): Promise<string | null> {
const predicates = new rdb.RdbPredicates('secure_items')
.equalTo('alias', alias);
const resultSet = await this.store!.query(predicates);
if (resultSet.goToFirstRow()) {
const data = resultSet.getString(resultSet.getColumnIndex('encrypted_data'));
resultSet.close();
return data;
}
resultSet.close();
return null;
}
}
🔐 SecurityLevel 说明:
S1:文件加密(依赖设备锁屏密码);S2:设备唯一密钥加密(即使 root 也无法解密);S3:TEE 内加密(最高安全等级,需硬件支持)。
六、Step 4:Flutter 端集成
定义安全通道
dart
// lib/services/security_service.dart
class SecurityService {
static const _channel = MethodChannel('com.example.security/crypto');
// 获取 SM2 公钥(用于注册)
static Future<String> getSm2PublicKey() async {
return await _channel.invokeMethod('getPublicKey');
}
// 使用 SM2 私钥签名(用于登录)
static Future<String> signLoginRequest(String requestId) async {
return await _channel.invokeMethod('signWithSm2', requestId);
}
// 加密敏感字段
static Future<String> encryptField(String field, String value) async {
return await _channel.invokeMethod('encryptWithSm4', {
'alias': 'user_$field',
'plainText': value,
});
}
// 解密
static Future<String> decryptField(String field) async {
return await _channel.invokeMethod('decryptWithSm4', 'user_$field');
}
}
登录流程示例(国密认证)
dart
// lib/pages/login_page.dart
Future<void> _loginWithSm2() async {
// 1. 获取设备公钥(首次注册时上传至服务器)
final publicKey = await SecurityService.getSm2PublicKey();
// 2. 请求登录挑战(服务器返回 nonce)
final nonce = await ApiService.requestLoginChallenge(publicKey);
// 3. 用私钥签名 nonce
final signature = await SecurityService.signWithSm2(nonce);
// 4. 发送签名,服务器用公钥验签
final token = await ApiService.verifySignature(signature);
if (token != null) {
// 登录成功
ref.read(authProvider.notifier).setToken(token);
}
}
存储银行卡号(加密)
dart
// 保存时
final encryptedCard = await SecurityService.encryptField('bank_card', '6222********1234');
await ApiService.saveEncryptedCard(encryptedCard); // 上传至服务器(可选)
// 读取时(仅在安全页面显示)
final card = await SecurityService.decryptField('bank_card');
setState(() => _maskedCard = maskCard(card));
七、通信安全:国密 TLS(可选高级方案)
若服务器支持国密 SSL,可在 OpenHarmony 侧配置:
typescript
// 网络请求时指定国密套件
const request = http.createHttp();
request.setCipherSuites(['ECDHE-SM2-WITH-SM4-SM3']); // 需服务器支持
⚠️ 注意 :目前主流云厂商尚未全面支持国密 TLS,通常采用 "SM2 签名 + 国际 TLS" 混合方案过渡。
八、安全审计与合规检查
-
禁止日志打印敏感信息
typescript// 错误:console.log('卡号:', cardNumber); // 正确:仅记录操作ID -
内存清理
dart// Dart 层使用完立即置空 String? sensitiveData = ...; // 使用后 sensitiveData = null; -
防截屏/录屏
typescript// EntryAbility.onCreate() window.setWindowPrivacyMode(true); // OpenHarmony 隐私模式
九、总结:打造真正自主可控的金融应用
通过本文,你已掌握:
✅ 调用 OpenHarmony 原生国密算法
✅ 实现 SM2/SM4 混合加密体系
✅ 利用 RDB S2/S3 安全存储
✅ 构建符合等保2.0/商密规范的应用
🏦 适用场景:
- 移动银行 / 证券 APP
- 政务身份认证
- 电力/能源工控终端
- 医疗健康数据采集
在信创浪潮下,安全不是功能,而是底线 。只有深度融合 OpenHarmony 安全能力与 Flutter 跨端优势,才能构建真正 "自主、安全、可用" 的国产化行业应用。