真机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

