先说一件事
把手机递给别人的瞬间,你有没有想过备忘录里那些「密码123456」?
我有过。那种感觉促使我做了密语盒子——一个加密之后输出结果看起来像正常汉字的工具。不是乱码,不是 Base64,是一串你可以抄在便签上、发到朋友圈、截图给别人看,对方完全不会觉得「这是密文」的汉字序列。
核心设计:字谱替换 + AES-GCM 双层结构
密语盒子有两层机制,缺一不可。
第一层:强加密底层
用 AES-GCM + PBKDF2 做真正的密码学加密,完全离线,密钥不上传任何服务器。HarmonyOS 的 @ohos.security.cryptoFramework 模块提供了完整的对称加密支持,AES-GCM 的初始化向量每次随机生成,规避重放攻击。
// AES-GCM 加密参数,IV 每次随机生成
import cryptoFramework from '@ohos.security.cryptoFramework';
async function buildGcmParams(): Promise<cryptoFramework.GcmParamsSpec> {
const rand = cryptoFramework.createRandom();
const ivBlob = await rand.generateRandom(12); // 96-bit IV
return {
algName: 'GcmParamsSpec',
iv: ivBlob,
aad: { data: new Uint8Array(0) },
authTag: { data: new Uint8Array(16) }
};
}
```
**第二层:字谱替换**
这才是「视觉隐身」的关键。
AES 加密完的结果是二进制数据。我没有直接输出 Base64,而是先把原始字节做 Base64 编码——这一步把任意字节值收敛到 64 个 ASCII 字符范围内,再把这 64 个字符一一映射到你自定义的汉字集合上。你挑 64 个常用汉字,配成一套「字谱」,加密输出就变成了一串汉字。
```arkts
// 字谱编码:Base64字符 → 汉字序列
function encodeWithCipher(base64Str: string, cipher: string[]): string {
// cipher 长度 64,对应标准 Base64 字符集
if (cipher.length < 64) throw new Error('字谱字符数不足');
const B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
return base64Str.split('').map(ch => {
const idx = B64_CHARS.indexOf(ch);
return idx >= 0 ? cipher[idx] : ch; // '=' padding 直接保留
}).join('');
}
```
解密反向操作:输入同一套字谱 + 密码,汉字序列还原成 Base64 字符串,再走 AES-GCM 解密,得到明文。
字谱在 App 里可以自由编辑、保存多套。私密日记用一套《千字文》里选出来的字,和朋友约定聊天用另一套。外人看到的永远只是一段汉字。
---
## 鸿蒙开发踩坑实录
ArkUI 的声明式 UI 写起来和 SwiftUI 很像,`@State`、`@Link`、`@Observed` 这套响应式机制上手不难。密语盒子里的字谱编辑器是一个可滚动的汉字网格,用 `Grid` + `GridItem` 实现,点击选中/取消,状态全靠 `@State` 数组驱动,没有额外引状态管理库,这块写得挺顺。
真正费时间的是 `cryptoFramework` 里的 PBKDF2 派生密钥。具体踩的坑是这样的:`iterations` 参数的类型在文档里写的是 `number`,但我最初从另一个示例里抄来的代码用了 `bigint`,编译器报错指向的位置是完全不相关的 `GcmParamsSpec` 构造,跟 PBKDF2 没有半点关系。对着那个报错找了很久,最后才发现根源在 `iterations` 的类型上。
还有一个是 `Uint8Array` 和 `string` 之间的转换。不同 API 版本里 `TextEncoder` / `TextDecoder` 的行为有细微差异,在处理加密输出转字符串这条链路上翻车过一次,最后改成手动操作字节数组才稳定下来。
说实话,`cryptoFramework` 的能力已经够用,就是边界情况文档描述不够,DevEco Studio 的报错定位有时候会带歪方向。
---
## 几个真实场景,直接说
**账号密码明文备注**
这是我最常用的场景。密码用密语形式写在备忘录里,即使手机被人解锁翻到,看到的只是一段汉字,不会触发「这里有密码」的敏感信号。
**私密日记发公开平台**
在小红书写日记,有些段落不想被人看懂但又不想删。用密语盒子加密那段,输出汉字粘贴进去,外人读到只会以为是某种诗意表达。
**和朋友约定字谱聊天**
两人各自在 App 里导入同一套字谱,发密语原文,截图扩散出去也没人看得懂。有点「地下暗号」的感觉,我觉得这个用法最有意思。
---
## 现在的状态
1.2.0 刚上线,从后台看,进来的用户基本都是搜索「HarmonyOS 加密」或者「鸿蒙隐私工具」找到的,说明这个需求是真实存在的,不是我一个人的焦虑。
接下来想做的功能:字谱导入/导出(方便两个人共享同一套字谱),以及一个「字谱生成器」——输入一段文本,自动从中提取不重复汉字组成字谱配置,降低自定义门槛。
有一个方向考虑过但暂时放掉了:加「伪装文本模板」,让加密结果嵌入一段看起来像诗词的句子里。实现不难,但怎么让模板足够自然不露馅,设计成本比较高,先留着。
App 包名 com.xun.xxsecret,版本 1.2.0,鸿蒙应用市场搜「密语盒子」可以找到。
---
## 如果你也在用 ArkTS 处理原始字节
我现在最头疼的一个问题:`Uint8Array` 和 `string` 之间的转换,在不同 HarmonyOS API 版本里行为不一致,尤其是涉及非 ASCII 字节时,`TextDecoder` 的处理结果会出乎意料。你们有没有遇到过类似的情况?或者有更稳定的转换方案?欢迎在评论区说说。
1242



被折叠的 条评论
为什么被折叠?



