更新记录

1.0.0(2026-11-10) 下载此版本

变更

  • 统一异步 Promise API:initialize、setItem、getItem、removeItem、generateKeyBase64。
  • Android、iOS、HarmonyOS 统一使用应用层认证加密,系统安全存储用于保护主密钥。
  • 增加旧 Android、iOS、HarmonyOS 数据的按条目迁移入口,遵循先写新数据、验证后删除旧数据。
  • HarmonyOS 写入、读取、删除和初始化等待真实 Asset Store 操作完成。
  • 主密钥缺失且仍存在新格式密文时拒绝生成新密钥,避免静默丢失旧数据。
  • 修复 HarmonyOS 空字符串读取,并为旧数据迁移增加写入后回读校验。
  • HarmonyOS 支持使用 CryptoArchitectureKit 生成 32 字节随机 Base64 密钥,三端 API 行为统一。
  • 初始化结果移除无法准确表达惰性迁移状态的 migrated 字段。
  • 三端错误统一为 UniError 扩展对象,提供统一的 errSubject、errCode 和 errMsg,并在错误消息中保留稳定字符串错误码。
  • Android 示例页补充结构化错误码 Smoke 检查;独立 instrumentation 测试和 iOS/HarmonyOS 验证仍未完成。

注意

  • 本版本为插件首次公开发布,支持 Android、iOS 和 HarmonyOS。当前仅完成 Android 编译、真机验证和迁移验收;iOS、HarmonyOS 未进行本次发布前的编译和真机验证,使用方应将这两个平台视为未验证平台。
  • Android 本地 Backup Transport 恢复验证通过:恢复密文但缺少 Keystore 主密钥时返回 MASTER_KEY_MISSING,不会静默生成新密钥。
  • Web 不提供实现。
  • 正式发布前必须完成仓库根目录 RELEASE_PLAN.md 中仍适用于本次发布范围的迁移、测试和元数据任务。
  • iOS Privacy Manifest 的官方 reason code、资源打包和 Xcode Archive 结果本次未核验。

平台兼容性

uni-app x(4.0)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × 6.0 13 11.0 -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
√ √ √ √

Diboy Secure Storage

面向 uni-app x 的本地加密键值存储 UTS 插件,用于安全保存 Token、数据库密钥、设备标识等少量敏感字符串。插件使用 Android Keystore、iOS Keychain 和 HarmonyOS Asset Store 保护应用主密钥,并使用 AES-GCM 加密业务 value。

当前版本:1.0.0。插件支持 Android、iOS 和 HarmonyOS;本次发布已完成 Android 编译、真机验证和迁移验收,iOS、HarmonyOS 已提供实现但未进行本次发布前的编译和真机验证,使用方应将这两个平台视为未验证平台。

插件支持 uni-app x Vapor 模式;Android 已完成 Vapor 编译验证,iOS 和 HarmonyOS 尚未完成本次发布前验证。

支持范围

平台 系统安全存储 应用层加密 最低版本
Android Android Keystore AES-GCM API 23
iOS Keychain CryptoKit AES-GCM iOS 13
HarmonyOS Asset Store CryptoArchitectureKit AES-GCM API 11
Web 不支持 不支持 -

插件不提供明文存储降级。业务 value 不应存放大文件,当前上限为 1MB;key 上限为 UTF-8 256 字节。

功能概览

  • 初始化本地安全存储,并检测当前设备是否具备所需安全能力。
  • 加密写入、读取和删除字符串键值。
  • 对已有旧格式条目进行逐条迁移,迁移成功后才删除旧数据。
  • Android、iOS 和 HarmonyOS 生成 32 字节 Base64 密钥,可用于本地数据库加密。
  • 不连接远程服务,不采集数据,不申请额外系统权限。

适合保存 refresh token、session token、本地数据库密钥和少量配置密钥;不适合保存对象、二进制数据、文件或大文本。

安装

在 HBuilderX 中通过 uni_modules 安装插件,或将 uni_modules/diboy-secure-storage 目录放入项目的 uni_modules 目录。插件仅支持 uni-app x App,不支持传统 uni-app 和 Web。

导入

import {
  initialize,
  getSecureStorageCapabilities,
  setItem,
  getItem,
  removeItem,
  generateKeyBase64,
} from '@/uni_modules/diboy-secure-storage'

应用启动时先初始化一次,初始化是幂等的:

async function useSecureStorage(): Promise<void> {
  await initialize()

  await setItem('example.key', 'example value')
  const value = await getItem('example.key')
  console.log(`stored value: ${value == null ? 'not found' : value}`)

  await removeItem('example.key')
}

getItem 找不到条目时返回 null。写入、读取、删除、初始化失败时 Promise reject SecureStorageErrorImpl。这是标准 UniError 扩展对象:使用 error.errCode 获取数字错误码,使用 error.errMsg 获取 错误码:错误描述;稳定字符串错误码是 error.errMsg 中冒号前的部分。业务日志不得输出 key、value、Token、密钥或密文。

API

调用顺序

  1. 应用启动时调用一次 initialize()。
  2. 初始化成功后调用 setItem()、getItem() 或 removeItem()。
  3. generateKeyBase64() 可独立调用,不依赖插件内部主密钥初始化。

initialize()

const result = await initialize()
  • 参数:无
  • 返回:Promise<SecureStorageInitializationResult>
  • initialized:是否初始化成功
  • capabilities:本机安全能力,结构见下文
  • 失败:Promise reject Error
  • 常见错误:UNSUPPORTED_API_LEVEL、KEYSTORE_UNAVAILABLE、KEY_RETRIEVAL_FAILED、KEY_GENERATION_FAILED、MASTER_KEY_MISSING、MASTER_KEY_INVALIDATED、LEGACY_MIGRATION_FAILED

初始化是幂等的,可以重复调用,但建议在应用启动流程中集中调用一次。主密钥丢失或失效时会失败,不会自动生成新主密钥覆盖已有密文。

getSecureStorageCapabilities()

const capabilities = getSecureStorageCapabilities()
  • 参数:无
  • 返回:SecureStorageCapabilities,同步返回
  • supported:当前设备是否支持插件
  • platform:平台名称
  • minApiLevel:插件要求的最低系统 API 级别
  • apiLevel:当前设备系统 API 级别
  • keystoreAvailable:系统安全存储是否可用
  • aesGcmAvailable:AES-GCM 是否可用
  • error:不支持时的错误对象,否则为 null

这是可选的预检查和诊断 API,不负责初始化,也不负责创建主密钥。普通业务直接调用 initialize() 即可;HarmonyOS 建议在初始化成功后再读取该结果,以获得完整的能力状态。

setItem(key, value)

await setItem('session.refreshToken', refreshToken)
  • key:string,不能为空,UTF-8 长度不超过 256 字节
  • value:string,UTF-8 长度不超过 1 MiB
  • 返回:Promise<void>,成功时无返回值
  • 前置条件:必须先成功调用 initialize()
  • 常见错误:INITIALIZATION_REQUIRED、INVALID_KEY、KEY_TOO_LONG、VALUE_TOO_LARGE、ENCRYPTION_FAILED、PREFERENCES_WRITE_FAILED、MASTER_KEY_MISSING

getItem(key)

const refreshToken = await getItem('session.refreshToken')
  • key:string,不能为空,UTF-8 长度不超过 256 字节
  • 返回:Promise<string | null>;条目存在时返回明文字符串,不存在时返回 null
  • 前置条件:必须先成功调用 initialize()
  • 常见错误:INITIALIZATION_REQUIRED、INVALID_KEY、KEY_TOO_LONG、DECRYPTION_FAILED、AUTHENTICATION_FAILED、ENVELOPE_INVALID、MASTER_KEY_MISSING、PREFERENCES_READ_FAILED、LEGACY_MIGRATION_FAILED

removeItem(key)

await removeItem('session.refreshToken')
  • key:string,不能为空,UTF-8 长度不超过 256 字节
  • 返回:Promise<void>,成功时无返回值;条目不存在时也视为成功
  • 前置条件:必须先成功调用 initialize()
  • 常见错误:INITIALIZATION_REQUIRED、INVALID_KEY、KEY_TOO_LONG、PREFERENCES_REMOVE_FAILED、MASTER_KEY_MISSING

generateKeyBase64()

const databaseKey = await generateKeyBase64()
  • 参数:无
  • 返回:Promise<string>,32 字节随机密钥的 Base64 字符串,通常长度为 44 个字符(包含结尾 =)
  • 前置条件:无,可独立调用
  • 用途:生成业务侧本地数据库或其他本地加密场景使用的密钥
  • 常见错误:KEY_GENERATION_FAILED、UNSUPPORTED_PLATFORM

该密钥与插件内部主密钥相互独立。插件不会自动保存或恢复此密钥;业务应使用 setItem() 将其安全保存,并在使用数据库前先读取并校验。

所有异步 API 都必须使用 await 或 Promise 链处理。捕获异常时应读取 error.errMsg 前缀或 error.errCode;业务应根据错误码采取重试、重新登录或恢复流程,不应因为任意错误直接清空安全存储。

建议统一捕获异常并根据错误码处理:

try {
  await initialize()
  const token = 'your-token'
  await setItem('session.token', token)
} catch (error) {
  // 生产环境只记录错误码,不要记录 token 或密文。
  console.error(`secure storage failed: ${error.errMsg}`)
}

常见错误码包括:INITIALIZATION_REQUIRED、INVALID_KEY、KEY_TOO_LONG、VALUE_TOO_LARGE、MASTER_KEY_MISSING、MASTER_KEY_INVALIDATED、STORAGE_LOCKED、AUTHENTICATION_FAILED 和 UNSUPPORTED_PLATFORM。完整错误码以 SecureStorageErrorCode 导出类型为准。

错误码按场景可分为:

  • 参数错误:INVALID_KEY、KEY_TOO_LONG、VALUE_TOO_LARGE
  • 平台能力错误:UNSUPPORTED_PLATFORM、UNSUPPORTED_API_LEVEL、KEYSTORE_UNAVAILABLE、CAPABILITY_CHECK_FAILED
  • 主密钥错误:KEY_GENERATION_FAILED、KEY_RETRIEVAL_FAILED、KEY_INVALIDATED、MASTER_KEY_MISSING、MASTER_KEY_INVALIDATED
  • 数据完整性错误:INVALID_IV、INVALID_CIPHERTEXT、AUTHENTICATION_FAILED、ENVELOPE_INVALID、DECRYPTION_FAILED
  • 持久化错误:PREFERENCES_OPEN_FAILED、PREFERENCES_READ_FAILED、PREFERENCES_WRITE_FAILED、PREFERENCES_REMOVE_FAILED
  • 迁移和状态错误:INITIALIZATION_REQUIRED、STORAGE_LOCKED、LEGACY_MIGRATION_FAILED、LEGACY_DATA_INVALID、INTERNAL_ERROR

生成数据库密钥

Android、iOS 和 HarmonyOS 均支持生成随机数据库密钥:

const databaseSecret = await generateKeyBase64()

generateKeyBase64() 生成的是供业务使用的独立随机密钥,不是插件内部主密钥。插件内部主密钥仍由各平台安全存储独立管理。

安全模型

  • 三端生成随机应用主密钥,并由系统安全存储保护。
  • 业务 value 使用版本化 AES-GCM Envelope 保存,不直接保存明文。
  • Envelope 绑定格式版本、主密钥版本和业务 key,篡改或跨 key 替换会认证失败。
  • 此前版本产生的 Android、iOS、HarmonyOS 数据按条目迁移:读取旧值、写入新格式、回读验证成功后删除旧数据。
  • 主密钥丢失或失效不会静默生成新密钥覆盖旧数据;调用方应根据错误码进入恢复或重新登录流程。

兼容和限制

  • 设备首次解锁后可用;设备安全存储暂不可用时应重试,不应清空业务数据。
  • Android 备份、iOS Keychain 卸载保留和 HarmonyOS 设备迁移行为由平台决定,业务不应把安全存储当作跨设备备份。Android 本地 Backup Transport 已验证:恢复密文但缺少 Keystore 主密钥时返回 MASTER_KEY_MISSING,不会静默生成新密钥。
  • 当前不支持 Web、对象序列化、二进制 API 和大文件存储。

数据和安全边界

  • 插件不采集数据、不连接远程服务、不包含广告,也不申请额外系统权限。
  • 插件保护的是本地存储数据,不承诺防止已完全控制设备的攻击,也不是跨设备备份方案。
  • 主密钥丢失或失效时会返回错误,不会静默生成新密钥覆盖已有密文;业务应进入恢复或重新登录流程。
  • 卸载重装、系统备份恢复和设备迁移行为由系统平台决定,业务应自行设计恢复策略。

发布状态

作者:Diboy

版权:diboy.cn

许可证:MIT

仓库:https://cnb.cool/diboycn/Diboy.SecureStorage

问题反馈:https://cnb.cool/diboycn/Diboy.SecureStorage/-/issues

发布验证状态:Android 已完成编译、真机迁移、篡改、锁屏/解锁、进程终止和备份恢复验证,示例页包含最小 Smoke 流程;iOS、HarmonyOS 的编译和真机验证尚未完成,使用方应将这两个平台视为未验证平台。iOS Privacy Manifest 的官方 reason code 和最终打包结果尚未核验。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无额外系统权限。插件使用 Android Keystore、iOS Keychain 和 HarmonyOS Asset Store。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据,不连接远程服务。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT License

Copyright (c) 2026 diboy.cn

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

暂无用户评论。