【问题标题】:CCCrypt working with pieces of files AES CBC: garbage to end of files on encryption-decryption cycleCCCrypt 处理文件 AES CBC:加密-解密周期中文件末尾的垃圾
【发布时间】:2015-03-09 15:54:57
【问题描述】:

虽然我能够根据 Zaph 和其他人的一些出色示例轻松使用 CCCrypt 进行数据加密和解密 (AES128CBC),但在处理加密/解密文件时,我遇到了两个奇怪的 CCCrypt 问题。

1) 在加密然后解密文件时,我的文件末尾会出现额外的垃圾,并且根据文件的不同而有所不同。一个原始文件的十六进制转储和加密和解密后的结果有一个额外的“0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B”,另一个文件最后有一个额外的“05 05 05 05 05”。两个结果文件的命令行差异报告“文件末尾没有换行符”。除了这些问题(以及处理与这些问题相关的文件的问题,例如 NSJSONSerialization 拒绝解析)之外,一切似乎都在工作。是什么导致了这个令人沮丧的问题?

2) 为了最终容纳非常大的文件,我将文件分成块,然后使用前一个块的最后 16 字节作为下一个块的 IV(因为无论如何这都是 CBC 的工作方式,对吗?) .奇怪的是,在可被密钥大小整除的块上,使用 kCCOption PKCS7Padding 会导致问题。因此,IV 在解密与在这些块之间进行加密时最终会有所不同。当我只是将所有块的选项设置为零时,包括最后一个可能不能被密钥大小整除的块,我得到一个例外。谁能帮我理解这个问题是什么?我只是使用一个条件来避免这个问题,但我不明白这个异常,可能它与问题1有关。

    let fileSize = getFileSizeFromPath(filePath)
    println("filesize = \(fileSize)")
    while file != nil {
        if let inputBuffer = file?.readDataOfLength(oneMegaByte) {
            if inputBuffer.length == 0 {
                file?.closeFile()
                break
            } else {
                println("input buffer length: \(inputBuffer.length)")
                if let outputBuffer = inputBuffer.AES128CBC(key: key, iv: iv, encryptionOp: encrypt) {
                    println("output buffer length: \(outputBuffer.length)")
                    outFile?.writeData(outputBuffer)
                    println("input file offset:\(file!.offsetInFile) output file offset:\(outFile!.offsetInFile)")
                    if encrypt {
                        let range = NSMakeRange((outputBuffer.length - const.keyLength), const.keyLength)
                        iv = outputBuffer.subdataWithRange(range)
                        println("range of iv for next chunk:\(range), iv value: \(iv)")
                    } else {
                        let range = NSMakeRange((inputBuffer.length - const.keyLength), const.keyLength)
                        iv = inputBuffer.subdataWithRange(range)
                        println("range of iv for next chunk:\(range), iv value: \(iv)")
                    }
                } else {
                    file?.closeFile()
                    println("problem encrypting data")
                    break
                }
            }
        } else {
            file?.closeFile()
            break
        }

以及我添加到 NSData 扩展的方法来加密和解密:

func AES128CBC(#key: NSData, iv: NSData, encryptionOp: Bool) -> NSData? {
    if key.length != 16 || iv.length != 16 || key.bytes == iv.bytes {
        return nil
    }

    let data = self
    let dataLength = UInt(data.length)
    let cPtrToData = UnsafePointer<UInt8>(data.bytes)
    let cPtrToIVData = UnsafePointer<UInt8>(iv.bytes)
    let cPtrTokeyData = UnsafePointer<UInt8>(key.bytes)
    let keySize = size_t(kCCKeySizeAES128)

    let buffer: NSMutableData! = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
    if buffer == nil { return nil }
    var cPtrTobuffer = UnsafeMutablePointer<UInt8>(buffer.mutableBytes)
    let bufferSize = size_t(buffer.length)

    var operation: CCOperation
    if encryptionOp {
        operation = UInt32(kCCEncrypt)
    } else {
        operation = UInt32(kCCDecrypt)
    }

    let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
    var options: CCOptions
    if dataLength % UInt(const.keyLength) != 0 {
        options = UInt32(kCCOptionPKCS7Padding)
    } else {
        options = 0
    }
        var encryptedByteCount: UInt = 0

    var operationResult = CCCrypt(operation, algoritm, options, cPtrTokeyData, keySize, cPtrToIVData, cPtrToData, dataLength, cPtrTobuffer, bufferSize, &encryptedByteCount)

    if UInt32(operationResult) == UInt32(kCCSuccess) {
        println("encrypted / decrypted byte count: \(encryptedByteCount)")
        buffer.length = Int(encryptedByteCount)
        return buffer
    }
    println("Error: \(operationResult)")
    return nil
}

【问题讨论】:

  • 以下不正确:“在可被密钥大小整除的块上”,它基于块大小,对于 AES 而言,密钥大小为 16 字节。
  • 谢谢.. 我应该设置更改以防我最终移至 256 位。

标签: ios swift aes encryption-symmetric


【解决方案1】:

额外的字节是PKCS#7 padding。加密是基于块的,因此必须在加密时添加字节,然后在解密时删除字节以实现此目的。根据 PKCS#7,额外字节是添加的字节数。这些额外的字节:“05 05 05 05 05”表示添加了 5 个字节的填充。

如果指定了 PKCS#7 填充并且输入数据是块大小的精确倍数,则添加另一个块(并且将是所有填充字节)。这必须发送,否则解密时会出现填充错误。如果您在加密和解密方面都知道数据是块大小的倍数,则可以跳过 PKCS#7 填充。在 OP 的情况下,如果中间段都是块大小的倍数,则可以在除最后一个之外的所有加密段上跳过此操作。 AES 使用 128 位(16 字节)块。

【讨论】:

  • 谢谢。我很确定它与填充有关,但 CCCrypt 不应该在解密时删除填充吗?我应该在解密时关闭文件之前尝试手动删除它吗?
  • 在提供 kCCOptionPKCS7Padding 选项时,CCCrypt 不会删除解密时的填充,它会在解密后通过以下语句删除:buffer.length = Int(encryptedByteCount)
  • 对不起,我不明白。我正在使用上面的'buffer.length = Int(encryptedByteCount)',但我仍然在生成的解密文件中找到额外的字节。也许你是说我不应该在解密时使用填充选项,只加密然后该语句将删除额外的填充?我会试一试。 (再次感谢您)
  • 您如何查看/访问填充字节?您是否正在访问经过 encryptedByteCount 的数据?
  • 我正在使用十六进制编辑器查看最终解密的文件(并将其与原始文件进行比较)。最终解密的文件是调用包含上面代码的方法的结果(顶部),它调用了上面名为 AES128CBC 的 NSData 扩展方法。我使用 Bool 集调用该方法一次进行加密,然后在生成的加密文件上再次调用它,并使用与 Bool 集相同的密钥进行解密。所有文件名都不同。在每次重新运行之前,我一直在检查然后删除 2 个生成的文件。最终解密的明文文件每次都包含填充。
【解决方案2】:

感谢 Zaph 的帮助,我了解了上述问题。为了清楚起见,我将更直接地回答上述问题。

1) 正如 Zaph 所说,我解密文件中的额外字节是填充。它们之所以出现是因为以下有问题的代码,我已将其删除。

var options: CCOptions
if dataLength % UInt(const.keyLength) != 0 {
    options = UInt32(kCCOptionPKCS7Padding)
} else {
    options = 0
}

由于如果选择了 PKCS7Padding 选项(这就是添加它们的原因),CCCrypt 的输出将始终被密钥长度整除,因此上述代码将测试在解密操作期间永远无法满足的条件。然后 CCCrypt 不会为我剥离填充。由于在我的应用程序中,我主要处理文件的子片段,这些片段始终是密钥大小的固定倍数,而不是整个文件,因此我试图使用上述代码删除文件之间不必要的填充,而不考虑解密操作中的后果.这当然更适合在调用我的 NSData 扩展的代码中完成。

2) 根据手册页(没有适用于 ios 的手册,但可以在此处找到 2007 年的旧 mac 手册:https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/CCCrypt.3cc.html

"如果禁用填充,或者在解密时,总字节数[必须]与块大小对齐;否则 CCCryptFinal() 将返回 kCCAlignmentError。"

这意味着当我将选项设置为零(没有设置PKCS7Padding选项)时,最后一个块会导致程序崩溃。

由于我将文件分解为子片段以进行加密和解密操作(以最终适应非常大的文件大小),因此我需要使用前一个文件块的最后 16 个字节作为下一个块的 IV(这就是 CBC 的工作原理,通过使用从前一个块的最后 16 个字节生成的 IV 为每个后续密钥大小的块播种(不管上面 Zaph 指出的使用 128 位还是 256 位密钥大小)。

但是,我无法理解的是,如果选择了 PKCSPadding 选项,CCCrypt 总是在加密时提供填充,即使操作的数据是密钥大小的倍数。 CCCrypt 在文件的每个块的末尾添加了 16 个字节,然后我在文件中间写了填充。哎呀。

我现在使用下面的代码来调用加密/解密方法,并丢弃在这个更合适的级别上没有运行到文件末尾的数据的方法调用的填充。

while file != nil {
    if let inputBuffer = file?.readDataOfLength(fileChunkLength) { 
        if inputBuffer.length == 0 {
            file?.closeFile()
            break 
        } else { 
            println("input buffer length: \(inputBuffer.length)") 
            println("IV passed to aes128:\(iv)") 
            if let outputBuffer = inputBuffer.AES128CBC(key: key, iv: iv, encryptionOp: encrypt) { 
                println("output bufferlength: \(outputBuffer.length)")

                if encrypt && file?.offsetInFile < fileSize {
                    // only write the data, and discard the padding when not at the end of the file
                    outFile?.writeData(outputBuffer.subdataWithRange(NSMakeRange(0, fileChunkLength)))
                    println("Bytes written \(fileChunkLength)")
                    // set the iv for the next chunk in encryption ops (no need when file is finished anyway)
                    iv = outputBuffer.subdataWithRange(NSMakeRange(fileChunkLength - ivLength, ivLength))
                } else {
                    outFile?.writeData(outputBuffer)
                    println("Bytes written \(outputBuffer.length)")
                    // set iv for decryption ops (doesn't matter for encryption ops where file is at end)
                    iv = inputBuffer.subdataWithRange(NSMakeRange(inputBuffer.length - ivLength, ivLength))
                }
                println("input file offset:\(file!.offsetInFile) output file offset:\(outFile!.offsetInFile)")

            } else {
                file?.closeFile()
                println("problem encrypting data")
                break
            }
        }
    } else {
        file?.closeFile()
        break

NSData 扩展(上面的第二个代码块,除了删除提到的问题代码之外保持不变)

【讨论】:

  • 有很多代码只是为了节省 16 字节的填充。代码越多越不安全。正如肯特贝克所说:“做最简单的事情可能会奏效”但是who is he,他知道什么。 ;-) 这里最简单的就是去掉所有的填充条件代码,每次只使用填充。
  • 没错,我听从了您的建议。也许,如果上面不清楚,我需要编辑。最终,我可能能够使用 CCCryptor 而不仅仅是一次性 CCCrypt 功能,这样我就不用处理文件块了。我需要了解更多。至于在文件中添加填充,如果文件要在另一个我无法控制的系统上通过标准 AES128 CBC 解密来解密,这并不理想。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
相关资源
最近更新 更多