You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

真机Realm解密失败但模拟器正常的技术求助

问题解决:Realm加密后真机无法打开文件(解密失败)

问题描述

应用集成加密Realm后,仅模拟器能正常运行,真机启动时报错:

Failed to open Realm file at path '/var/mobile/.../Documents/default.realm': Realm file decryption failed (Decryption failed)

集成加密前应用运行完全正常,已尝试下载Realm源码排查、自行生成密钥(代码如下),但问题未解决:

var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { (pointer: UnsafeMutableRawBufferPointer) in
    SecRandomCopyBytes(kSecRandomDefault, 64, pointer.baseAddress!)
}

怀疑info.plist配置遗漏但未找到相关线索,相关代码片段如下:

AppDelegate中的Realm配置

static var getkey: Getkey = Getkey()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    ...
    initRealm()        
    return true
}

func initRealm(){        
    // 配置加密Realm
    var config = Realm.Configuration(
        encryptionKey: AppDelegate.getkey.getKey(),
        schemaVersion: self.version,
        migrationBlock: { migration, oldSchemaVersion in
          if oldSchemaVersion < 1 {}
        }
    )
    Realm.Configuration.defaultConfiguration = config
}

ViewModel中的Realm调用

init() {
    setupObserver()
}

private func setupObserver(){
    let config = Realm.Configuration(encryptionKey: AddressViewModel.getkey.getKey())
    do{
        let realm = try Realm(configuration: config)
        let results = realm.objects(Address.self)
        ...
    }catch{
        print("setup addy ---> \(config)")
        print(error.localizedDescription)
    }
}

可能的原因及解决办法

1. 密钥实例不统一

你在AppDelegate和AddressViewModel中分别创建了Getkey实例,若Getkey每次实例化都会生成新密钥,两处使用的解密密钥会不一致,导致真机解密失败(模拟器因沙箱缓存特性可能偶然兼容)。
解决:将Getkey改为单例模式,确保全局使用同一密钥:

class Getkey {
    static let shared = Getkey()
    private init() {} // 禁止外部创建新实例
    
    func getKey() -> Data {
        // 你的密钥生成/读取逻辑
    }
}

两处调用统一改为:

// AppDelegate中
encryptionKey: Getkey.shared.getKey()

// ViewModel中
let config = Realm.Configuration(encryptionKey: Getkey.shared.getKey())

2. 密钥未持久化

若Getkey未将密钥存储到安全位置,每次App启动都会生成新密钥,旧加密文件无法用新密钥解密。
解决:将密钥存储到Keychain,启动时读取而非重复生成:

func getKey() -> Data {
    // 先从Keychain读取已有密钥
    if let existingKey = readKeyFromKeychain() {
        return existingKey
    }
    // 读取失败则生成新密钥并存储
    var key = Data(count: 32)
    _ = key.withUnsafeMutableBytes { pointer in
        SecRandomCopyBytes(kSecRandomDefault, 32, pointer.baseAddress!)
    }
    saveKeyToKeychain(key)
    return key
}

// Keychain读写实现(简化版,需完善错误处理)
private func saveKeyToKeychain(_ key: Data) {
    let query: [CFString: Any] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccount: "RealmEncryptionKey",
        kSecValueData: key,
        kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
    ]
    SecItemDelete(query as CFDictionary)
    SecItemAdd(query as CFDictionary, nil)
}

private func readKeyFromKeychain() -> Data? {
    let query: [CFString: Any] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccount: "RealmEncryptionKey",
        kSecReturnData: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne
    ]
    var data: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &data)
    return status == errSecSuccess ? data as? Data : nil
}

3. 旧未加密文件残留

集成加密前的未加密Realm文件可能仍在真机沙箱中,用加密配置打开未加密文件会触发解密失败。
解决:卸载真机上的App后重新安装,彻底清除沙箱中的旧文件;或在代码中检测文件加密状态,主动迁移/删除旧文件。

4. 密钥长度兼容性问题

Realm官方支持16、24、32字节的AES密钥(对应AES-128/192/256),你生成的64字节密钥虽会被截断为前32字节,但可能存在平台兼容性问题。
解决:改为生成32字节的标准AES-256密钥(代码已在上述Keychain示例中调整)。


内容的提问来源于stack exchange,提问作者Duc Dang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:56:31