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

UserDefaults存加密数据取回报错,求SymmetricKey安全存Keychain方案

解决你的CryptoKit加解密与Keychain存储问题

我来帮你逐一解决这两个问题:

问题1:UserDefaults中取回SealedBox的类型转换错误

你遇到的类型转换错误,核心原因是你存入UserDefaults的是SealedBox的combined Data,但取出时直接尝试将Data强制转换为ChaChaPoly.SealedBox类型——这是行不通的,因为UserDefaults只会存储基础类型(比如Data),不会直接保存自定义对象。

正确的做法是:取出存储的Data后,通过ChaChaPoly.SealedBox(data:)初始化方法还原SealedBox对象。另外,建议不要用try!强制解包,最好用do-catch处理可能的错误(比如数据损坏、密钥不匹配等)。

修正后的decrypt方法:

func decrypt() {
    guard let savedKeyData = Services.getKeyData(),
          let sealedBoxData = UserDefaults.standard.data(forKey: "sealedBox"),
          let sealedBox = try? ChaChaPoly.SealedBox(data: sealedBoxData) else {
        textView.text = "解密失败:数据或密钥异常"
        return
    }
    
    do {
        let retrievedKey = SymmetricKey(data: savedKeyData)
        let decryptedMessage = try ChaChaPoly.open(sealedBox, using: retrievedKey)
        textView.text = String(data: decryptedMessage, encoding: .utf8) ?? "无法转换为可读文本"
    } catch {
        textView.text = "解密出错:\(error.localizedDescription)"
    }
}

问题2:无需UnsafeBytes存储SymmetricKey到Keychain

从Swift 5.6开始,SymmetricKey遵守了ContiguousBytes协议,你可以直接将它转换为Data,完全不需要使用withUnsafeBytes这种不安全的操作。SwiftKeychainWrapper本身支持直接存储Data类型,所以我们可以优化密钥的存储流程:

优化后的加密方法(存储Data到Keychain)

func encrypt(text: String) {
    let key = SymmetricKey(size: .bits256)
    guard let message = text.data(using: .utf8) else {
        print("文本转Data失败")
        return
    }
    
    do {
        let sealedBox = try ChaChaPoly.seal(message, using: key)
        // 存储SealedBox的合并Data到UserDefaults
        UserDefaults.standard.set(sealedBox.combined, forKey: "sealedBox")
        // 直接将SymmetricKey转为Data存储到Keychain
        let keyData = Data(key)
        Services.saveKeyData(keyData: keyData)
    } catch {
        print("加密出错:\(error.localizedDescription)")
    }
}

对应的Keychain服务方法(修改为处理Data)

把原来处理字符串的Services方法改成直接操作Data:

class Services {
    static func saveKeyData(keyData: Data) {
        SwiftKeychainWrapper.standard.set(keyData, forKey: "encryptionKey")
    }
    
    static func getKeyData() -> Data? {
        return SwiftKeychainWrapper.standard.data(forKey: "encryptionKey")
    }
}

这样就完全避免了UnsafeBytes和Base64编码的额外开销,代码更简洁安全。

内容的提问来源于stack exchange,提问作者Tymek Sala

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 17:22:45