iOS 核心数据加密使用 NSValueTransformer

Posted

技术标签:

【中文标题】iOS 核心数据加密使用 NSValueTransformer【英文标题】:iOS Core Data encryption using NSValueTransformer 【发布时间】:2015-10-16 13:55:20 【问题描述】:

我正在尝试使用 Core Data 和 CommonCrypto 加密数据。我正在尝试使用 NSValueTransformer 懒惰地加密和解密。

但是,当我现在尝试将加密数据保存到持久存储协调器时,它失败了。每次我试图将我的数据保存到数据库时,它都会给我:

-[__NSCFString bytes]: 无法识别的选择器发送到实例

我确定这是某种数据库和 NSManagedObject 不匹配,但我无法弄清楚。我觉得这可能很简单,但我找不到解决方案。我的代码:

NSValueTransformer

class TryHardEncryption: NSValueTransformer 

override class func transformedValueClass() -> AnyClass 
    return NSString.self


override class func allowsReverseTransformation() -> Bool 
    return true



override func reverseTransformedValue(value: AnyObject?) -> AnyObject? 
    if let message = value as? NSString 
        let keyString        = "12345678901234567890123456789012"
        let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafeMutablePointer<Void>(keyData.bytes)
        print("keyLength   = \(keyData.length), keyData   = \(keyData)")

        let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let dataLength    = size_t(data.length)
        let dataBytes     = UnsafeMutablePointer<Void>(data.bytes)
        print("dataLength  = \(dataLength), data      = \(data)")

        let cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
        let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
        let cryptLength  = size_t(cryptData!.length)

        let keyLength              = size_t(kCCKeySizeAES256)
        let operation: CCOperation = UInt32(kCCDecrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionPKCS7Padding + kCCOptionECBMode)

        var numBytesEncrypted :size_t = 0

        let cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            cryptPointer, cryptLength,
            &numBytesEncrypted)

        if UInt32(cryptStatus) == UInt32(kCCSuccess) 
            //  let x: UInt = numBytesEncrypted
            cryptData!.length = Int(numBytesEncrypted)
            print("DecryptcryptLength = \(numBytesEncrypted), Decrypt = \(cryptData)")

            // Not all data is a UTF-8 string so Base64 is used
            let base64cryptString = cryptData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
            print("base64DecryptString = \(base64cryptString)")
            print( "utf8 actual string = \(NSString(data: cryptData!, encoding: NSUTF8StringEncoding))");
            return base64cryptString
         else 
            print("Error: \(cryptStatus)")
        
    
    return nil


override func transformedValue(value: AnyObject?) -> AnyObject? 
    if let message = value as? NSString 
        let keyString        = "12345678901234567890123456789012"
        let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)
        print("keyLength   = \(keyData.length), keyData   = \(keyData)")

        let data: NSData! = message.dataUsingEncoding(NSUTF8StringEncoding) as NSData!
        let dataLength    = Int(data.length)
        let dataBytes     = UnsafePointer<UInt8>(data.bytes)
        print("dataLength  = \(dataLength), data      = \(data)")

        let cryptData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)!
        let cryptPointer = UnsafeMutablePointer<UInt8>(cryptData.mutableBytes)
        let cryptLength  = size_t(cryptData.length)

        let keyLength              = size_t(kCCKeySizeAES256)
        let operation: CCOperation = UInt32(kCCEncrypt)
        let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
        let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)

        var numBytesEncrypted :size_t = 0

        let cryptStatus = CCCrypt(operation,
            algoritm,
            options,
            keyBytes, keyLength,
            nil,
            dataBytes, dataLength,
            cryptPointer, cryptLength,
            &numBytesEncrypted)

        if UInt32(cryptStatus) == UInt32(kCCSuccess) 
            cryptData.length = Int(numBytesEncrypted)
            print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")

            // Not all data is a UTF-8 string so Base64 is used
            let base64cryptString = cryptData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
            print("base64cryptString = \(base64cryptString)")
            return NSString(string: base64cryptString) as NSObject

         else 
            print("Error: \(cryptStatus)")
        
    
    return nil



要让 NSValueTransformer 工作,我必须做的是:

let transformer: TryHardEncryption = TryHardEncryption()
    NSValueTransformer.setValueTransformer(transformer, forName: "TryHardEncryption")

没有上面的代码,NSValueTransformer 永远不会被调用。

我已将数据库字段标记为可转换类型并将其命名为:TryHardEncryption。你们知道这里有什么问题吗?

编辑 它所涉及的属性是:

@NSManaged var establishmentDescription: String?

当我调试它们时,加密和解密函数都返回一个字符串。

【问题讨论】:

可能是更新模型而不为修改后的实体生成新文件的简单案例,可能会有警告,不确定,但您不妨浏览一下您的模型类和如果您的应用是小型实验性应用,请检查它们。 我恢复了在 Transformable 类型更改之前对 NSManagedObject 所做的所有更改,但又没有运气了 :( 不过感谢您提供的信息。 您是否尝试过放置异常断点并使用它运行应用程序?它可能会向您显示问题发生的确切位置。另外关于您必须使用NSValueTransformer.setValueTransformer 的事实,您是否在数据模型检查器中输入了转换器类型名称(当您选择可转换时,新文本字段应出现在属性类型下拉列表下方)? 在调试时,我看到代码进入了transformedValue 方法。我什至打印了结果,一切看起来都很正常。我通过将出现在数据模型检查器中的名称字段值复制到 setvaluetransformer 方法来确保名称相同。 那么你的函数似乎不是问题,在断点导航器的底部你可以找到一个“+”按钮,然后从打开的菜单中添加一个通用的异常断点,这样的断点应该显示您需要在发生异常的确切位置(它并不总是非常有效,但在大多数情况下很有帮助),您需要在启用断点的情况下运行应用程序以使其工作。你试过吗? 【参考方案1】:

我终于弄明白了……我对对象必须是什么类型以及我的 nsvaluetransformer 必须返回什么感到困惑。我没有将值转换为 nsvaluetransformer 中的正确类型,因此我在代码中返回 nil。但是,它并没有以我很容易弄清楚的方式崩溃。其次,我将实体列改回 NSObject。在transformedValue 方法中返回一个NSData 对象,在reverseTransformedValue 方法中返回一个NSString。这实际上是我让它工作所需要的。非常感谢你的帮助。确实是类型错误。

【讨论】:

【参考方案2】:

为了帮助任何想查看更正后的代码示例的人,请将问题中的transformedValue() 的返回行替换为: return base64cryptString.dataUsingEncoding(NSUTF8StringEncoding)

【讨论】:

以上是关于iOS 核心数据加密使用 NSValueTransformer的主要内容,如果未能解决你的问题,请参考以下文章

Android-IO加解密核心与dex文件改造过程分析

Android-IO加解密核心与dex文件改造过程分析

核心数据 SQLite 加密?

核心数据加密类

使用 MagicalRecord 进行核心数据加密

加密核心数据迁移实体名称不匹配问题