2. 平台原生安全存储 API 选型

移动端与桌面端操作系统各自提供了专用的安全存储接口,用于处理密钥、令牌、证书等敏感数据。直接使用这些原生 API 而非自行实现加密存储,能有效降低密钥管理漏洞与侧信道攻击的风险。本章对四大主流平台的原生方案进行技术对比,帮助开发者在跨平台组件中做出合理选型。

2.1 选型决策框架

选择安全存储 API 时需从以下维度评估:

  • 加密机制:是否依赖硬件安全模块(HSM/TEE/SE);
  • 数据粒度:支持键值对、二进制 blob 还是结构化记录;
  • 跨进程访问:是否允许同一设备上的多个应用共享;
  • 备份与迁移:数据是否随系统备份导出,影响安全边界;
  • 平台耦合度:是否要求特定的系统版本或硬件特性。
选型核心原则:优先使用平台级 API 而非自行封装加密文件。平台 API 的密钥派生、存储介质和生命周期由操作系统统一管理,可规避大多数开发者常见的密钥硬编码和 IV 重用问题。

2.2 各平台 API 详解

2.2.1 Android · EncryptedSharedPreferences

Android 从 API 23 起在 AndroidX Security 库中提供 EncryptedSharedPreferences,底层使用 AES-256 GCM 加密键值,密钥由 Android Keystore 保管。

典型初始化代码:

MasterKey masterKey = new MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build();

SharedPreferences prefs = EncryptedSharedPreferences.create(
        context,
        "secure_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
prefs.edit().putString("access_", ).apply();

适用场景:应用内小型键值对存储(OAuth 、session ID、设备指纹);多进程场景需谨慎,默认不跨进程共享。

常见错误:

  • 使用 commit() 而非 apply() 导致 UI 线程阻塞;
  • 未处理 KeyStoreException(如锁屏密码变更导致密钥不可用);
  • 在 API 23 以下设备直接调用,未做兼容。

2.2.2 iOS · Keychain Services

iOS Keychain 使用硬件加密引擎,数据以加密记录形式存储在安全隔区(Secure Enclave)管理的区域,iOS 10.3 后支持 iCloud 同步。

写入操作示例(Swift):

let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: "com.app.account.",
    kSecValueData as String: tokenData,
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
let status = SecItemAdd(query as CFDictionary, nil)
if status != errSecSuccess && status != errSecDuplicateItem {
    // 错误处理
}

适用场景:密码、密钥、证书、持久化会话;需要 iCloud 同步时使用 kSecAttrSynchronizable;跨应用共享通过 Keychain Access Group 实现。

性能优化:避免频繁读写单条大记录;使用 kSecUseDataProtectionKeychain 属性启用文件级保护以降低 I/O 开销。

2.2.3 Windows · Credential Manager

Windows 凭据管理器将数据加密存储在用户配置文件中,支持 Web 凭据和通用凭据两种类型,加密依赖于 DPAPI。

写入 API(Win32):

CREDENTIAL cred = {0};
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = L"MyApp/SecureKey";
cred.CredentialBlobSize = (DWORD)dataSize;
cred.CredentialBlob = (LPBYTE)data;
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
BOOL result = CredWrite(&cred, 0);

适用场景:Windows 桌面应用和服务;企业环境中与 Active Directory 集成;不支持跨平台移动端共享。

常见错误:

  • 未检查 CredWrite 返回值和 GetLastError();
  • CredentialBlobSize 设置错误导致截断;
  • 使用 CRED_PERSIST_SESSION 导致凭据在注销后丢失。

2.2.4 Linux · secret-tool (libsecret)

Linux 通过 D-Bus 服务 secret-service 提供安全存储,命令行工具 secret-tool 和 libsecret 库封装了与 GNOME Keyring / KDE Wallet 的交互。

命令行存储示例:

secret-tool store --label="MyApp " service myapp  access_

读取示例:

secret-tool lookup service myapp  access_

libsecret C API 示例片段:

SecretSchema schema = {
    "com.myapp.secret",
    SECRET_SCHEMA_NONE,
    {
        { "service", SECRET_SCHEMA_ATTRIBUTE_STRING },
        { "",   SECRET_SCHEMA_ATTRIBUTE_STRING },
    }
};
gchar *password = secret_password_lookup_sync(
        &schema, NULL, NULL,
        "service", "myapp",
        "", "access_",
        NULL);

适用场景:Linux 桌面和嵌入式应用;需要与 GNOME/KDE 环境集成;密钥存储依赖桌面环境的密钥环守护进程。

无头服务器环境(无 D-Bus session bus)无法直接使用 secret-tool。此时建议退而使用 systemd-homed 或硬件 TPM 加密文件。

2.3 API 能力对比表

维度Android EncryptedSPiOS KeychainWindows CredManLinux libsecret
硬件依赖Keystore(TEE/SE)Secure EnclaveDPAPI(TPM)无硬件要求
加密算法AES-256 GCMAES-256 + SHA-5123DES/AESAES-256 GCM
数据模型键值对分层记录 + ACL凭据 Blob键值属性
跨进程不支持Access Group支持D-Bus 共享
备份行为跟随应用备份可选 iCloud不自动备份跟随密钥环备份
最低 API 版本API 23+iOS 9+Vista+GLib 2.44+

2.4 选型决策流程

下图展示了在组件内集成敏感数据持久化时的平台 API 推荐选型路径:

平台安全存储 API 选型决策流程 开始选型 判断目标操作系统 Android EncryptedSharedPreferences + Android Keystore iOS / iPadOS Keychain Services + Secure Enclave Windows Credential Manager Linux secret-tool / libsecret

2.5 常见错误与调试方法

平台常见错误调试手段
AndroidKeyStoreException: KeyStore not initialized检查 Keystore 实例化是否在后台线程;确认设备锁屏状态
iOSerrSecItemNotFound确认查询参数(kSecAttrAccount)完全匹配写入时的值;检查 Access Group 权限
WindowsERROR_NOT_FOUND (1168)使用 CredEnumerate 枚举所有凭据验证目标名称;检查应用程序清单中 supportedOS 配置
LinuxGDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown确认 secret-service 守护进程(gnome-keyring-daemon)正在运行;检查 DBUS_SESSION_BUS_ADDRESS 环境变量
跨平台调试技巧:在开发阶段为每个平台封装统一的 SecureStorageProvider 接口,并在单元测试中注入 mock 实现。这样可以在不依赖实际安全硬件的情况下验证业务逻辑。