虽然我能够根据Zaph和其他人的一些很好的例子,使用CCCrypt轻松地使用数据加密和解密(AES128CBC),但在处理加密/解密文件时,我遇到了两个与CCCrypt有关的奇怪问题。 / p>
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
}
答案 0 :(得分:1)
额外的字节是PKCS#7 padding。加密是基于块的,因此必须在加密时添加字节,然后在解密时删除字节以实现此目的。根据PKCS#7,额外字节是添加的字节数。这些附加字节:“05 05 05 05 05”表示添加了5个字节的填充。
如果指定了PKCS#7填充并且输入数据是块大小的精确倍数,则添加另一个块(并且将是所有填充字节)。这必须发送,解密时会出现填充错误。如果您在加密和解密方面都知道数据是块大小的倍数,则可以跳过PKCS#7填充。在OP的情况下,如果中间段都是块大小的倍数,则可以在除最后一个之外的所有加密段上跳过。 AES使用128位(16字节)块。
答案 1 :(得分:0)
感谢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
&#34;如果禁用填充,或者在解密时,将[block]的总字节数与块大小对齐;否则CCCryptFinal()将返回kCCAlignmentError。&#34;
这意味着当我将选项设置为零(没有设置PKCS7Padding选项)时,最后一个块会导致程序崩溃。
由于我将文件分解为用于加密和解密操作的子片段(最终适应非常大的文件大小),我需要使用前一个文件块的最后16个字节作为下一个块的IV(这就是CBC的工作方式,通过从前一个块的最后16个字节生成的IV播种每个后续的密钥大小的块(无论是否使用上面的Zaph指出的128位或256位密钥大小)。
然而,我无法理解的是,如果选择PKCSPadding选项,CCCrypt ALWAYS在加密时提供填充,即使操作的数据是密钥大小的倍数。 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扩展(上面的第二个代码块,除了删除提到的问题代码外,保持不变)