
編者按密碼明文存本地、Token 寫進 SharedPreference、身份證號直接 JSON 序列化扔沙箱目錄——這些操作在小項目里太常見了。用戶根本不在乎你的數據安不安全但一旦出了事鍋全是你的。HarmonyOS 6.0 提供了一整套從軟件加密到硬件級密鑰管理的安全體系但文檔分散、API 鏈路長很多人看了半天還是不知道怎么落地。開發者Frame Not Work把整個鏈路串起來講從最基礎的哈希計算一直講到 HUKS 硬件級密鑰管理配合實際可跑的 ArkTS 代碼看完你就能直接用到項目里。一、cryptoFramework 模塊總覽HarmonyOS 6.0 的加解密能力主要由kit.CryptoArchitectureKit提供這套 API 覆蓋了三大領域哈希消息摘要SHA-256、SHA-384、SHA-512、MD5 等用于數據完整性校驗和指紋生成對稱加密AES-128/192/256支持 CBC、GCM、ECB、CTR 等模式適合大數據量加解密非對稱加密RSA、ECC、SM2 等用于密鑰協商、數字簽名、小數據加密這套 API 的設計模式非常統一創建實例 → 初始化 → 更新數據 → 獲取結果。不管你用哪種算法流程都是這個套路上手成本不高。另外還有一套kit.UniversalKeystoreKitHUKS專門做密鑰管理密鑰全程不離開 TEE 可信執行環境安全性比 cryptoFramework 高一個級別。后面會詳細講。二、哈希計算數據指紋的第一道關哈希不是加密但它是安全存儲的基礎設施。文件完整性校驗、密碼存儲配合鹽值、數據去重都離不開哈希。HarmonyOS 支持的哈希算法SHA-25632 字節、SHA-38448 字節、SHA-51264 字節、MD516 字節。MD5 已經不推薦用于安全場景了但做文件去重、緩存 key 之類非安全用途還是挺好使的。調用流程就三步createMd→update→digest。import { cryptoFramework } from kit.CryptoArchitectureKit; import { buffer } from kit.ArkTS; async function computeSha256(input: string): Promisestring { let md cryptoFramework.createMd(SHA256); let inputBytes: cryptoFramework.DataBlob { data: new Uint8Array(buffer.from(input, utf-8).buffer) }; await md.update(inputBytes); let result await md.digest(); let hexStr ; for (let i 0; i result.data.length; i) { let hex result.data[i].toString(16).padStart(2, 0); hexStr hex; } return hexStr; }數據量大的場景可以分段 update結果不受影響async function computeSha256BySegment(longText: string): Promisestring { let md cryptoFramework.createMd(SHA256); let bytes new Uint8Array(buffer.from(longText, utf-8).buffer); let segmentSize 4096; for (let i 0; i bytes.length; i segmentSize) { let end Math.min(i segmentSize, bytes.length); let segment: cryptoFramework.DataBlob { data: bytes.subarray(i, end) }; await md.update(segment); } let result await md.digest(); let hexStr ; for (let i 0; i result.data.length; i) { hexStr result.data[i].toString(16).padStart(2, 0); } return hexStr; }這里有個細節要注意update接口對單次傳入的數據量沒有限制分段只是為了控制內存占用。對于文件哈希計算建議用 4KB 或更大的分段避免頻繁的異步調用開銷。三、AES 對稱加密主力加密方案對稱加密是應用層加密的絕對主力。AES 速度快、安全強度高加密大文件也不在話下。完整流程createSymKeyGenerator→generateSymKey→createCipher→init→update→doFinal。AES-128-CBC 模式CBC 是最經典的分組模式需要 IV初始化向量參與運算import { cryptoFramework } from kit.CryptoArchitectureKit; import { buffer } from kit.ArkTS; async function aesCbcEncrypt(plainText: string): PromisecryptoFramework.DataBlob { let keyGenerator cryptoFramework.createSymKeyGenerator(AES128); let symKey await keyGenerator.generateSymKey(); let ivBytes cryptoFramework.createRandom().generateRandomSync(16); let ivParamsSpec: cryptoFramework.IvParamsSpec { algName: IvParamsSpec, iv: { data: ivBytes.data } }; let cipher cryptoFramework.createCipher(AES128|CBC|PKCS7); await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, ivParamsSpec); let input: cryptoFramework.DataBlob { data: new Uint8Array(buffer.from(plainText, utf-8).buffer) }; let encryptResult await cipher.doFinal(input); return encryptResult; }解密時用同一個 key 和 IV模式換成DECRYPT_MODEasync function aesCbcDecrypt( symKey: cryptoFramework.SymKey, cipherData: cryptoFramework.DataBlob, ivData: Uint8Array ): Promisestring { let ivParamsSpec: cryptoFramework.IvParamsSpec { algName: IvParamsSpec, iv: { data: ivData } }; let decoder cryptoFramework.createCipher(AES128|CBC|PKCS7); await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, ivParamsSpec); let decryptResult await decoder.doFinal(cipherData); let output buffer.from(decryptResult.data).toString(utf-8); return output; }CBC 模式有幾個坑要注意IV 必須隨機生成不能硬編碼IV 需要和密文一起存儲解密時要用PKCS7 填充模式下doFinal會自動處理末尾不滿一個分塊的情況。AES-256-GCM 模式推薦GCM 是我更推薦的模式。它不僅能加密還帶認證標簽AuthTag能同時保證數據的機密性和完整性。CBC 模式只能加密如果你需要驗證數據有沒有被篡改還得自己算 HMAC而 GCM 一步到位。function buildGcmParamsSpec(): cryptoFramework.GcmParamsSpec { let ivBytes cryptoFramework.createRandom().generateRandomSync(12); let aadBytes new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); let tagBytes new Uint8Array(16); let gcmParams: cryptoFramework.GcmParamsSpec { algName: GcmParamsSpec, iv: { data: ivBytes.data }, aad: { data: aadBytes }, authTag: { data: tagBytes } }; return gcmParams; } async function aesGcmEncrypt( symKey: cryptoFramework.SymKey, plainText: string ): PromisecryptoFramework.DataBlob { let gcmParams buildGcmParamsSpec(); let cipher cryptoFramework.createCipher(AES128|GCM|PKCS7); await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams); let input: cryptoFramework.DataBlob { data: new Uint8Array(buffer.from(plainText, utf-8).buffer) }; let encryptResult await cipher.doFinal(input); // GCM 模式下 doFinal 返回密文authTag 需要從 gcmParams.authTag 中讀取 // 解密時必須使用加密階段生成的 authTag return encryptResult; }解密時需要把加密階段生成的 authTag 放進 GcmParamsSpec 傳給 init如果 authTag 不匹配解密直接失敗這就實現了完整性校驗async function aesGcmDecrypt( symKey: cryptoFramework.SymKey, cipherData: cryptoFramework.DataBlob, gcmParams: cryptoFramework.GcmParamsSpec ): Promisestring { let decoder cryptoFramework.createCipher(AES128|GCM|PKCS7); await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, gcmParams); let decryptResult await decoder.doFinal(cipherData); return buffer.from(decryptResult.data).toString(utf-8); }AES-128-CBC vs AES-256-GCM 怎么選維度AES-128-CBCAES-256-GCM密鑰長度128 位256 位認證能力無需額外 HMAC內置 AuthTagIV 長度16 字節12 字節推薦安全等級夠用更高推薦新項目使用我的建議新項目一律用 AES-256-GCM。CBC 模式最大的問題是缺乏認證能力密文被篡改了你都不知道。GCM 自帶認證標簽篡改即失敗省心太多。四、RSA 非對稱加密公鑰加密、私鑰解密RSA 的典型場景不是直接加密業務數據——它太慢了而且有長度限制1024 位密鑰最多加密 117 字節2048 位最多 245 字節。RSA 真正的價值在于密鑰協商、數字簽名、加密小數據比如 AES 密鑰。RSA 加解密import { cryptoFramework } from kit.CryptoArchitectureKit; import { buffer } from kit.ArkTS; async function rsaEncryptDemo(): Promisevoid { // 生成 RSA 2048 密鑰對 let keyGenerator cryptoFramework.createAsyKeyGenerator(RSA2048); let keyPair await keyGenerator.generateKeyPair(); let message SensitiveData123; let input: cryptoFramework.DataBlob { data: new Uint8Array(buffer.from(message, utf-8).buffer) }; // 公鑰加密 let cipher cryptoFramework.createCipher(RSA2048|PKCS1); await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null); let encryptResult await cipher.doFinal(input); // 私鑰解密必須創建新的 Cipher 實例 let decoder cryptoFramework.createCipher(RSA2048|PKCS1); await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null); let decryptResult await decoder.doFinal(encryptResult); let decrypted buffer.from(decryptResult.data).toString(utf-8); console.info(Decrypted: decrypted); }注意兩點一是 RSA 的 Cipher 實例不支持重復 init每次加解密都要 new 一個二是非對稱加密的 params 參數傳 null 就行不像 AES 那樣要傳 IvParamsSpec 或 GcmParamsSpec。RSA 簽名驗證簽名是 RSA 另一個核心用途——用私鑰簽名用公鑰驗證證明數據確實來自持有私鑰的一方async function rsaSignVerifyDemo(): Promisevoid { let keyGenerator cryptoFramework.createAsyKeyGenerator(RSA2048); let keyPair await keyGenerator.generateKeyPair(); let message Contract content here; let input: cryptoFramework.DataBlob { data: new Uint8Array(buffer.from(message, utf-8).buffer) }; // 私鑰簽名 let signer cryptoFramework.createSign(RSA2048|PKCS1|SHA256); await signer.init(keyPair.priKey); let signResult await signer.sign(input); // 公鑰驗簽 let verifier cryptoFramework.createVerify(RSA2048|PKCS1|SHA256); await verifier.init(keyPair.pubKey); let isValid await verifier.verify(input, signResult); console.info(Signature valid: isValid); }簽名和驗簽的算法字符串必須一致RSA2048|PKCS1|SHA256里的每一項都得對上。另外 RSA 密鑰長度建議至少 2048 位1024 位在當前算力下已經不安全了。五、HUKS 密鑰管理硬件級安全的天花板cryptoFramework 做加解密沒問題但密鑰的管理是個軟肋。你在軟件層生成的 AES 密鑰最終還是存在內存里root 設備或者內存 dump 理論上能拿到。HUKSUniversal Keystore Kit解決的就是這個問題——密鑰生成、存儲、使用全在 TEE可信執行環境里完成密鑰永遠不出 TEE你的應用代碼也拿不到密鑰明文。HUKS 生成密鑰import { huks } from kit.UniversalKeystoreKit; const AES_KEY_ALIAS my_app_aes_key; function getAesGenerateProperties(): Arrayhuks.HuksParam { return [ { tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_256 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }, { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_NONE }, { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_GCM } ]; } async function generateHuksAesKey(): Promisevoid { let properties getAesGenerateProperties(); let options: huks.HuksOptions { properties: properties }; await huks.generateKeyItem(AES_KEY_ALIAS, options); console.info(HUKS AES key generated); }注意看這里沒有generateSymKey返回密鑰對象的步驟。HUKS 的密鑰由系統管理你拿到的是一個別名alias后續所有操作都通過別名引用。密鑰本身你永遠接觸不到。HUKS 加密HUKS 加密是三段式操作initSession→updateSession可選→finishSessionasync function huksEncryptData(plainText: string): PromiseUint8Array { let iv cryptoFramework.createRandom().generateRandomSync(12).data; let encryptProps: Arrayhuks.HuksParam [ // ... 配置屬性 { tag: huks.HuksTag.HUKS_TAG_NONCE, value: iv }, { tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA, value: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]) } ]; let options: huks.HuksOptions { properties: encryptProps, inData: new util.TextEncoder().encode(plainText) }; let initResult await huks.initSession(AES_KEY_ALIAS, options); let finishResult await huks.finishSession(initResult.handle, options); return finishResult.outData as Uint8Array; }HUKS 解密解密流程和加密一模一樣只是 PURPOSE 換成 DECRYPT并且 GCM 模式下需要傳入 AEAD 標簽async function huksDecryptData( cipherData: Uint8Array, iv: Uint8Array, aeadTag: Uint8Array ): Promisestring { let decryptProps: Arrayhuks.HuksParam [ // ... 配置屬性 { tag: huks.HuksTag.HUKS_TAG_NONCE, value: iv }, { tag: huks.HuksTag.HUKS_TAG_AE_TAG, value: aeadTag } ]; let options: huks.HuksOptions { properties: decryptProps, inData: cipherData }; let initResult await huks.initSession(AES_KEY_ALIAS, options); let finishResult await huks.finishSession(initResult.handle, options); let plainBytes finishResult.outData as Uint8Array; return new util.TextDecoder().decodeToString(plainBytes); }HUKS 的核心價值加密解密操作在 TEE 內完成密鑰明文永遠不會出現在普通執行環境REE的內存中。即使攻擊者拿到了設備的 root 權限也無法提取 HUKS 管理的密鑰。這是軟件層加密做不到的。六、安全存儲策略選擇HarmonyOS 6.0 提供了三層安全方案安全性從低到高排列Base64 編碼不是加密import { util } from kit.ArkTS; function base64Encode(input: string): string { let encoder new util.Base64Helper(); let bytes new util.TextEncoder().encode(input); return encoder.encodeToString(bytes); }Base64 只是編碼不是加密。任何人都能解碼沒有任何安全性可言。cryptoFramework 軟件加密適合中等敏感度數據用戶設置項、非關鍵業務數據、需要跨設備傳輸的加密數據。密鑰在軟件層管理安全性取決于密鑰存儲方式。HUKS 硬件級加密適合高敏感數據密碼、Token、身份證號、金融信息、健康數據。密鑰由 TEE 管理不可提取。這是目前 HarmonyOS 上你能拿到的最高安全等級。七、沙箱隔離與 CE/ECE 加密存儲區HarmonyOS 的應用沙箱機制是安全存儲的基礎。每個應用有自己獨立的沙箱目錄應用 A 默認無法訪問應用 B 的文件。這個隔離是系統強制的不需要你做任何額外工作。沙箱目錄結構context.filesDir應用私有文件目錄context.cacheDir緩存目錄context.tempDir臨時文件目錄context.preferencesDir偏好設置目錄context.databaseDir數據庫目錄這些目錄在 el2 加密分區下默認開機后首次解鎖才能訪問。HarmonyOS 按加密強度把沙箱目錄分成了四個等級等級說明適用場景el1設備級加密開機即可訪問鬧鐘、壁紙、通知el2用戶級加密首次解鎖后可訪問默認檔位大多數應用數據el3文件關閉后鎖屏再次打開需重新解鎖即時通訊消息、郵件el4鎖屏 10 秒后密鑰丟棄重新解鎖才能訪問金融應用、密碼管理器八、RDB 加密數據庫結構化數據的安全存儲如果你的敏感數據是結構化的比如用戶信息表、交易記錄表用文件加密存儲解析起來太麻煩直接用加密的 RDB 數據庫是更好的選擇。創建加密數據庫只需要在 StoreConfig 里設置encrypt: trueimport { relationalStore } from kit.ArkData; import { common } from kit.AbilityKit; async function createEncryptedDb(context: common.UIAbilityContext): PromiserelationalStore.RdbStore { const STORE_CONFIG: relationalStore.StoreConfig { name: SecureApp.db, securityLevel: relationalStore.SecurityLevel.S3, encrypt: true }; let store await relationalStore.getRdbStore(context, STORE_CONFIG); const CREATE_TABLE_SQL CREATE TABLE IF NOT EXISTS user_credentials (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, encrypted_password TEXT NOT NULL, salt TEXT NOT NULL); await store.executeSql(CREATE_TABLE_SQL); return store; }幾個重要細節encrypt參數只在首次創建數據庫時生效securityLevel要和你的數據敏感度匹配系統默認加密的數據庫不支持跨設備打開或卸載重裝后打開九、實戰HUKS el2 二次加密方案對于最高敏感度的數據S4 級別官方推薦的做法是二次加密先用 HUKS 在 TEE 內加密數據再把密文寫入 el2 加密目錄。兩層獨立缺一不可。async function secureWriteData( context: common.UIAbilityContext, fileName: string, plainData: string ): Promisevoid { // 1. 確保 HUKS 密鑰存在 await initSecureKey(); // 2. 用 HUKS 加密數據 let iv cryptoFramework.createRandom().generateRandomSync(12).data; let plainBytes new util.TextEncoder().encode(plainData); // ... HUKS 加密操作得到 cipherData // 3. 將 IV 密文拼接后寫入 el2 目錄 let fileData new Uint8Array(iv.length cipherData.length); fileData.set(iv, 0); fileData.set(cipherData, iv.length); let filePath context.filesDir / fileName; let file fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY); fileIo.writeSync(file.fd, fileData.buffer); fileIo.closeSync(file.fd); }這段代碼做了什么明文數據經過 HUKS在 TEE 內用 AES-256-GCM 加密IV 和密文拼接后寫入 el2 目錄。攻擊者就算拿到了文件面對的是兩層加密HUKS 的 AES-256-GCM 和 el2 的磁盤級加密。密鑰在 TEE 里文件在加密分區里兩把鎖缺一把都打不開。十、常見坑與實操建議坑正確做法IV 硬編碼每次加密隨機生成 IV和密文一起存儲密鑰寫在代碼里用 HUKS 管理密鑰至少也要用安全的密鑰派生方案Base64 當加密Base64 只用于數據格式轉換不要當作安全手段encrypt 參數后改建庫時就想好要不要加密首次創建就指定GCM 解密不傳 AuthTag加密時保存 AuthTag解密時必須傳入HUKS 密鑰不判斷是否存在先isKeyItemExist檢查不存在再創建el4 目錄后臺讀寫后臺需要持續訪問的數據放 el2別放 el4RSA 直接加密大文件大文件用 AES 加密RSA 只加密 AES 密鑰混合加密HUKS session 不 finish三段式操作必須走完init → update(可選) → finish十一、寫在最后安全存儲不是一道選擇題而是一道必答題。HarmonyOS 6.0 給了你從軟件加密到硬件級密鑰管理的完整工具鏈cryptoFramework解決日常加密需求HUKS兜底高敏感數據el2/el4分級目錄做系統層防護RDB加密數據庫處理結構化數據。工具都在這了用不用、怎么用就看你對自己用戶數據的態度了。最后說一句大實話安全方案沒有絕對的安全只有成本和收益的權衡。HUKS el2 的二次加密方案已經是目前 HarmonyOS 上你能做到的極限了。別想著自己造輪子搞什么更安全的方案密碼學的東西用經過驗證的標準實現比自己瞎折騰靠譜一萬倍。 文基于開發者 Frame Not Work 創作的文章整理感謝開發者的精彩分享。原文指路HarmonyOS 6.0 文件加密與安全存儲從哈希到硬件級密鑰管理全鏈路實戰你在 HarmonyOS 安全存儲中還遇到過哪些難題歡迎在評論區留言交流