發(fā)者實(shí)戰(zhàn)】HarmonyOS 6.0 文件加密與安全存儲(chǔ):從哈希到硬件級(jí)密鑰管理,一篇打通全鏈路)
編者按密碼明文存本地、Token 寫進(jìn) SharedPreference、身份證號(hào)直接 JSON 序列化扔沙箱目錄——這些操作在小項(xiàng)目里太常見(jiàn)了。用戶根本不在乎你的數(shù)據(jù)安不安全但一旦出了事鍋全是你的。HarmonyOS 6.0 提供了一整套從軟件加密到硬件級(jí)密鑰管理的安全體系但文檔分散、API 鏈路長(zhǎng)很多人看了半天還是不知道怎么落地。開(kāi)發(fā)者Frame Not Work把整個(gè)鏈路串起來(lái)講從最基礎(chǔ)的哈希計(jì)算一直講到 HUKS 硬件級(jí)密鑰管理配合實(shí)際可跑的 ArkTS 代碼看完你就能直接用到項(xiàng)目里。一、cryptoFramework 模塊總覽HarmonyOS 6.0 的加解密能力主要由kit.CryptoArchitectureKit提供這套 API 覆蓋了三大領(lǐng)域哈希消息摘要SHA-256、SHA-384、SHA-512、MD5 等用于數(shù)據(jù)完整性校驗(yàn)和指紋生成對(duì)稱加密AES-128/192/256支持 CBC、GCM、ECB、CTR 等模式適合大數(shù)據(jù)量加解密非對(duì)稱加密RSA、ECC、SM2 等用于密鑰協(xié)商、數(shù)字簽名、小數(shù)據(jù)加密這套 API 的設(shè)計(jì)模式非常統(tǒng)一創(chuàng)建實(shí)例 → 初始化 → 更新數(shù)據(jù) → 獲取結(jié)果。不管你用哪種算法流程都是這個(gè)套路上手成本不高。另外還有一套kit.UniversalKeystoreKitHUKS專門做密鑰管理密鑰全程不離開(kāi) TEE 可信執(zhí)行環(huán)境安全性比 cryptoFramework 高一個(gè)級(jí)別。后面會(huì)詳細(xì)講。二、哈希計(jì)算數(shù)據(jù)指紋的第一道關(guān)哈希不是加密但它是安全存儲(chǔ)的基礎(chǔ)設(shè)施。文件完整性校驗(yàn)、密碼存儲(chǔ)配合鹽值、數(shù)據(jù)去重都離不開(kāi)哈希。HarmonyOS 支持的哈希算法SHA-25632 字節(jié)、SHA-38448 字節(jié)、SHA-51264 字節(jié)、MD516 字節(jié)。MD5 已經(jīng)不推薦用于安全場(chǎng)景了但做文件去重、緩存 key 之類非安全用途還是挺好使的。調(diào)用流程就三步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; }數(shù)據(jù)量大的場(chǎng)景可以分段 update結(jié)果不受影響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; }這里有個(gè)細(xì)節(jié)要注意update接口對(duì)單次傳入的數(shù)據(jù)量沒(méi)有限制分段只是為了控制內(nèi)存占用。對(duì)于文件哈希計(jì)算建議用 4KB 或更大的分段避免頻繁的異步調(diào)用開(kāi)銷。三、AES 對(duì)稱加密主力加密方案對(duì)稱加密是應(yīng)用層加密的絕對(duì)主力。AES 速度快、安全強(qiáng)度高加密大文件也不在話下。完整流程createSymKeyGenerator→generateSymKey→createCipher→init→update→doFinal。AES-128-CBC 模式CBC 是最經(jīng)典的分組模式需要 IV初始化向量參與運(yùn)算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; }解密時(shí)用同一個(gè) 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 模式有幾個(gè)坑要注意IV 必須隨機(jī)生成不能硬編碼IV 需要和密文一起存儲(chǔ)解密時(shí)要用PKCS7 填充模式下doFinal會(huì)自動(dòng)處理末尾不滿一個(gè)分塊的情況。AES-256-GCM 模式推薦GCM 是我更推薦的模式。它不僅能加密還帶認(rèn)證標(biāo)簽AuthTag能同時(shí)保證數(shù)據(jù)的機(jī)密性和完整性。CBC 模式只能加密如果你需要驗(yàn)證數(shù)據(jù)有沒(méi)有被篡改還得自己算 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 中讀取 // 解密時(shí)必須使用加密階段生成的 authTag return encryptResult; }解密時(shí)需要把加密階段生成的 authTag 放進(jìn) GcmParamsSpec 傳給 init如果 authTag 不匹配解密直接失敗這就實(shí)現(xiàn)了完整性校驗(yàn)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密鑰長(zhǎng)度128 位256 位認(rèn)證能力無(wú)需額外 HMAC內(nèi)置 AuthTagIV 長(zhǎng)度16 字節(jié)12 字節(jié)推薦安全等級(jí)夠用更高推薦新項(xiàng)目使用我的建議新項(xiàng)目一律用 AES-256-GCM。CBC 模式最大的問(wèn)題是缺乏認(rèn)證能力密文被篡改了你都不知道。GCM 自帶認(rèn)證標(biāo)簽篡改即失敗省心太多。四、RSA 非對(duì)稱加密公鑰加密、私鑰解密RSA 的典型場(chǎng)景不是直接加密業(yè)務(wù)數(shù)據(jù)——它太慢了而且有長(zhǎng)度限制1024 位密鑰最多加密 117 字節(jié)2048 位最多 245 字節(jié)。RSA 真正的價(jià)值在于密鑰協(xié)商、數(shù)字簽名、加密小數(shù)據(jù)比如 AES 密鑰。RSA 加解密import { cryptoFramework } from kit.CryptoArchitectureKit; import { buffer } from kit.ArkTS; async function rsaEncryptDemo(): Promisevoid { // 生成 RSA 2048 密鑰對(duì) 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); // 私鑰解密必須創(chuàng)建新的 Cipher 實(shí)例 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); }注意兩點(diǎn)一是 RSA 的 Cipher 實(shí)例不支持重復(fù) init每次加解密都要 new 一個(gè)二是非對(duì)稱加密的 params 參數(shù)傳 null 就行不像 AES 那樣要傳 IvParamsSpec 或 GcmParamsSpec。RSA 簽名驗(yàn)證簽名是 RSA 另一個(gè)核心用途——用私鑰簽名用公鑰驗(yàn)證證明數(shù)據(jù)確實(shí)來(lái)自持有私鑰的一方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); // 公鑰驗(yàn)簽 let verifier cryptoFramework.createVerify(RSA2048|PKCS1|SHA256); await verifier.init(keyPair.pubKey); let isValid await verifier.verify(input, signResult); console.info(Signature valid: isValid); }簽名和驗(yàn)簽的算法字符串必須一致RSA2048|PKCS1|SHA256里的每一項(xiàng)都得對(duì)上。另外 RSA 密鑰長(zhǎng)度建議至少 2048 位1024 位在當(dāng)前算力下已經(jīng)不安全了。五、HUKS 密鑰管理硬件級(jí)安全的天花板cryptoFramework 做加解密沒(méi)問(wèn)題但密鑰的管理是個(gè)軟肋。你在軟件層生成的 AES 密鑰最終還是存在內(nèi)存里root 設(shè)備或者內(nèi)存 dump 理論上能拿到。HUKSUniversal Keystore Kit解決的就是這個(gè)問(wèn)題——密鑰生成、存儲(chǔ)、使用全在 TEE可信執(zhí)行環(huán)境里完成密鑰永遠(yuǎn)不出 TEE你的應(yīng)用代碼也拿不到密鑰明文。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); }注意看這里沒(méi)有g(shù)enerateSymKey返回密鑰對(duì)象的步驟。HUKS 的密鑰由系統(tǒng)管理你拿到的是一個(gè)別名alias后續(xù)所有操作都通過(guò)別名引用。密鑰本身你永遠(yuǎn)接觸不到。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 標(biāo)簽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 的核心價(jià)值加密解密操作在 TEE 內(nèi)完成密鑰明文永遠(yuǎn)不會(huì)出現(xiàn)在普通執(zhí)行環(huán)境REE的內(nèi)存中。即使攻擊者拿到了設(shè)備的 root 權(quán)限也無(wú)法提取 HUKS 管理的密鑰。這是軟件層加密做不到的。六、安全存儲(chǔ)策略選擇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 只是編碼不是加密。任何人都能解碼沒(méi)有任何安全性可言。cryptoFramework 軟件加密適合中等敏感度數(shù)據(jù)用戶設(shè)置項(xiàng)、非關(guān)鍵業(yè)務(wù)數(shù)據(jù)、需要跨設(shè)備傳輸?shù)募用軘?shù)據(jù)。密鑰在軟件層管理安全性取決于密鑰存儲(chǔ)方式。HUKS 硬件級(jí)加密適合高敏感數(shù)據(jù)密碼、Token、身份證號(hào)、金融信息、健康數(shù)據(jù)。密鑰由 TEE 管理不可提取。這是目前 HarmonyOS 上你能拿到的最高安全等級(jí)。七、沙箱隔離與 CE/ECE 加密存儲(chǔ)區(qū)HarmonyOS 的應(yīng)用沙箱機(jī)制是安全存儲(chǔ)的基礎(chǔ)。每個(gè)應(yīng)用有自己獨(dú)立的沙箱目錄應(yīng)用 A 默認(rèn)無(wú)法訪問(wèn)應(yīng)用 B 的文件。這個(gè)隔離是系統(tǒng)強(qiáng)制的不需要你做任何額外工作。沙箱目錄結(jié)構(gòu)context.filesDir應(yīng)用私有文件目錄context.cacheDir緩存目錄context.tempDir臨時(shí)文件目錄context.preferencesDir偏好設(shè)置目錄context.databaseDir數(shù)據(jù)庫(kù)目錄這些目錄在 el2 加密分區(qū)下默認(rèn)開(kāi)機(jī)后首次解鎖才能訪問(wèn)。HarmonyOS 按加密強(qiáng)度把沙箱目錄分成了四個(gè)等級(jí)等級(jí)說(shuō)明適用場(chǎng)景el1設(shè)備級(jí)加密開(kāi)機(jī)即可訪問(wèn)鬧鐘、壁紙、通知el2用戶級(jí)加密首次解鎖后可訪問(wèn)默認(rèn)檔位大多數(shù)應(yīng)用數(shù)據(jù)el3文件關(guān)閉后鎖屏再次打開(kāi)需重新解鎖即時(shí)通訊消息、郵件el4鎖屏 10 秒后密鑰丟棄重新解鎖才能訪問(wèn)金融應(yīng)用、密碼管理器八、RDB 加密數(shù)據(jù)庫(kù)結(jié)構(gòu)化數(shù)據(jù)的安全存儲(chǔ)如果你的敏感數(shù)據(jù)是結(jié)構(gòu)化的比如用戶信息表、交易記錄表用文件加密存儲(chǔ)解析起來(lái)太麻煩直接用加密的 RDB 數(shù)據(jù)庫(kù)是更好的選擇。創(chuàng)建加密數(shù)據(jù)庫(kù)只需要在 StoreConfig 里設(shè)置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; }幾個(gè)重要細(xì)節(jié)encrypt參數(shù)只在首次創(chuàng)建數(shù)據(jù)庫(kù)時(shí)生效securityLevel要和你的數(shù)據(jù)敏感度匹配系統(tǒng)默認(rèn)加密的數(shù)據(jù)庫(kù)不支持跨設(shè)備打開(kāi)或卸載重裝后打開(kāi)九、實(shí)戰(zhàn)HUKS el2 二次加密方案對(duì)于最高敏感度的數(shù)據(jù)S4 級(jí)別官方推薦的做法是二次加密先用 HUKS 在 TEE 內(nèi)加密數(shù)據(jù)再把密文寫入 el2 加密目錄。兩層獨(dú)立缺一不可。async function secureWriteData( context: common.UIAbilityContext, fileName: string, plainData: string ): Promisevoid { // 1. 確保 HUKS 密鑰存在 await initSecureKey(); // 2. 用 HUKS 加密數(shù)據(jù) 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); }這段代碼做了什么明文數(shù)據(jù)經(jīng)過(guò) HUKS在 TEE 內(nèi)用 AES-256-GCM 加密IV 和密文拼接后寫入 el2 目錄。攻擊者就算拿到了文件面對(duì)的是兩層加密HUKS 的 AES-256-GCM 和 el2 的磁盤級(jí)加密。密鑰在 TEE 里文件在加密分區(qū)里兩把鎖缺一把都打不開(kāi)。十、常見(jiàn)坑與實(shí)操建議坑正確做法IV 硬編碼每次加密隨機(jī)生成 IV和密文一起存儲(chǔ)密鑰寫在代碼里用 HUKS 管理密鑰至少也要用安全的密鑰派生方案Base64 當(dāng)加密Base64 只用于數(shù)據(jù)格式轉(zhuǎn)換不要當(dāng)作安全手段encrypt 參數(shù)后改建庫(kù)時(shí)就想好要不要加密首次創(chuàng)建就指定GCM 解密不傳 AuthTag加密時(shí)保存 AuthTag解密時(shí)必須傳入HUKS 密鑰不判斷是否存在先isKeyItemExist檢查不存在再創(chuàng)建el4 目錄后臺(tái)讀寫后臺(tái)需要持續(xù)訪問(wèn)的數(shù)據(jù)放 el2別放 el4RSA 直接加密大文件大文件用 AES 加密RSA 只加密 AES 密鑰混合加密HUKS session 不 finish三段式操作必須走完init → update(可選) → finish十一、寫在最后安全存儲(chǔ)不是一道選擇題而是一道必答題。HarmonyOS 6.0 給了你從軟件加密到硬件級(jí)密鑰管理的完整工具鏈cryptoFramework解決日常加密需求HUKS兜底高敏感數(shù)據(jù)el2/el4分級(jí)目錄做系統(tǒng)層防護(hù)RDB加密數(shù)據(jù)庫(kù)處理結(jié)構(gòu)化數(shù)據(jù)。工具都在這了用不用、怎么用就看你對(duì)自己用戶數(shù)據(jù)的態(tài)度了。最后說(shuō)一句大實(shí)話安全方案沒(méi)有絕對(duì)的安全只有成本和收益的權(quán)衡。HUKS el2 的二次加密方案已經(jīng)是目前 HarmonyOS 上你能做到的極限了。別想著自己造輪子搞什么更安全的方案密碼學(xué)的東西用經(jīng)過(guò)驗(yàn)證的標(biāo)準(zhǔn)實(shí)現(xiàn)比自己瞎折騰靠譜一萬(wàn)倍。 文基于開(kāi)發(fā)者 Frame Not Work 創(chuàng)作的文章整理感謝開(kāi)發(fā)者的精彩分享。原文指路HarmonyOS 6.0 文件加密與安全存儲(chǔ)從哈希到硬件級(jí)密鑰管理全鏈路實(shí)戰(zhàn)你在 HarmonyOS 安全存儲(chǔ)中還遇到過(guò)哪些難題歡迎在評(píng)論區(qū)留言交流