【问题标题】:What is the better way to encrypt NSURLCache?加密 NSURLCache 的更好方法是什么?
【发布时间】:2023-04-02 05:45:01
【问题描述】:

我想使用 AES256 加密/解密来自 NSURLSession 的所有缓存数据。我是使用 Alamofire 的新手,但我认为可以在不涉及库本身的情况下做到这一点。

我不知道在缓存之前加密数据并在从缓存中检索后解密数据的最无缝方式是什么。

我知道我可以使用 Alamofire 的 SessionDelegate 和方法 dataTaskWillCacheResponsedataTaskWillCacheResponseWithCompletion 进行加密,但我没有看到与从缓存中提取的数据进行解密相关的任何内容。

另一方面,我正在考虑使用自定义 NSURLProtocol 来覆盖 cachedResponse,但我没有看到与该响应的缓存相关的任何内容,只有提取的数据。

总之,我不知道是否可以做到这一点,或者我必须使用NSURLSessionDelegate/SessionDelegateNSURLProtocol 之间的混合,或者可能是子类NSURLCache 来完成这项工作并将其传递给Alamofire 会议,或者那里有更简单的东西,或者我大错特错:P

任何帮助将不胜感激。


编辑

我正在尝试通过下一个实现来实现它。首先是缓存的一个非常简单的子类:

class EncryptedURLCache: URLCache {

    let encryptionKey: String

    init(memoryCapacity: Int, diskCapacity: Int, diskPath path: String? = nil, encryptionKey: String) {

        guard !encryptionKey.isEmpty else {
            fatalError("No encryption key provided")
        }

        self.encryptionKey = encryptionKey
        super.init(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: path)
    }

    override func cachedResponse(for request: URLRequest) -> CachedURLResponse? {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
        return super.cachedResponse(for: request)?.cloneDecryptingData(withKey: encryptionKey)
    }

    override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }
        super.storeCachedResponse(cachedResponse.cloneEncryptingData(withKey: encryptionKey), for: request)
    }
}

以及缓存响应的扩展以返回加密/解密数据

extension CachedURLResponse {

    func cloneEncryptingData(withKey key: String) -> CachedURLResponse {
        return clone(withData: data.aes256Encrypted(withKey: key))
    }

    func cloneDecryptingData(withKey key: String) -> CachedURLResponse {
        return clone(withData: data.aes256Decrypted(withKey: key) ?? data)
    }

    private func clone(withData data: Data) -> CachedURLResponse {
        return CachedURLResponse(
            response: response,
            data: data,
            userInfo: userInfo,
            storagePolicy: storagePolicy
        )
    }
}

这是有效的,但仅适用于我使用标头 Cache-Control: max-age=60 安装的 mockable.io。我还在针对 SWAPI http://swapi.co/api/people/1/ 和 Google Books https://www.googleapis.com/books/v1/volumes?q=swift+programming 进行测试。

在所有三种情况下,响应都被正确加密和缓存。我正在测试切断 Internet 连接并设置会话配置的requestCachePolicy = .returnCacheDataDontLoad

在这种情况下,对 mockable.io 的请求被正确解密并从缓存中返回,但其他人说NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."。这很奇怪,因为使用该策略,如果无法返回缓存数据,则必须说NSURLErrorDomain Code=-1008 "resource unavailable"。如果解密时出错,则表示序列化为 JSON 对象时出错。

我还使用公共共享缓存进行了测试,它按预期工作,使用该策略返回数据。我认为这可能与 SWAPI 和 GBooks 响应中缺少缓存标头有关,但此测试有效,它返回缓存的数据。

然后我进行了另一个测试:使用我的缓存但不加密/解密数据,只需按原样克隆返回的缓存响应,没有结果。然后我尝试了最后一个非常愚蠢的测试:为了避免克隆响应,只需返回cachedResponse,然后它就可以工作了。这怎么可能?如果我克隆 cachedResponse 以注入我的加密/解密数据,它就不起作用!即使在examples from Apple 中,他们也在毫不畏惧地创建新的缓存响应。

我不知道错误在哪里,但我会在一两分钟后跳过窗口。

请问,有什么帮助吗?非常感谢。


编辑 2

我正在与来自 Apple 的 DTS 工程师更改电子邮件,结论是这是不可能实现的,因为支持 CF 类型比 Foundation 对象执行更多逻辑,在这种情况下,它正在对 URLRequest 进行验证当系统缓存响应时传递给它,但是当使用常规 NSCachedURLResponse 进行克隆时我无法传递它。

当系统根据请求进行验证时,没有可匹配的。

【问题讨论】:

  • @emengero 是否意味着您加密/解密 URLCache 失败并且没有办法做到这一点?
  • @msmialko 正确

标签: ios alamofire nsurlsession nsurlcache nsurlprotocol


【解决方案1】:

据我所知,无法拦截来自委托方的缓存检索调用,而且我认为如果请求从缓存中出来,甚至不会要求自定义协议处理请求,但是我可能是错的。所以可能你的选择是:

  • 在发出 URL 请求之前明确向缓存请求数据。
  • 在实际处理响应的代码中添加代码,以便识别数据已加密并对其进行解密。

    例如,您可以在将附加标头存储到缓存中时在标头中插入一个附加标头,以指示缓存的数据已加密。然后,当你在返回的路上看到那个神奇的标头值时,解密它。

  • 编写 NSURLCache 的子类并在那里处理解密(理想情况下,将磁盘上的数据存储在不同的文件中,以避免破坏应用中使用普通缓存的任何请求)。

【讨论】:

  • 谢谢@dgatwood。我正在尝试完成这个子类化URLCache,覆盖与存储和检索相关的方法以加密/解密。一切似乎都运行良好(.db 文件被存储和写入,数据被加密......)但是当我断开 WiFi 以测试是否检索到数据时(使用.returnCacheDataDontLoad 作为缓存策略)我得到一个连接错误,这很奇怪,如果没有缓存数据,错误将是“找不到资源”,没有“连接错误”。我不知道发生了什么。任何想法都会非常感激。
  • 您可能(不,很可能)遇到了框架中的错误。您可以尝试让您的类包装 NSURLCache 而不是子类化它,以防万一会话调用了一些未记录的 SPI 方法。然后你会得到一个更容易调试的崩溃。 :-)
  • 昨天我和Apple开了一个TSI,我希望他们能给这个带来光明。我会分享任何回应。谢谢你:)
  • 我的猜测是他们正在调用 getCachedResponseForDataTask:completionHandler 并且由于某些类集群的怪异或其他原因,它没有调用您的方法。
  • 我也实现了这些方法,一个用于存储,一个用于检索,结果相同。
猜你喜欢
  • 2014-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 2021-08-18
  • 2011-10-30
  • 1970-01-01
相关资源
最近更新 更多