问题描述
|
我有一个使用AES加密的文件。
我使用以下NSData类别:
#import <CommonCrypto/CommonCryptor.h>
@implementation NSData (AES)
- (NSData *)AES256DecryptWithKey:(NSString *)key {
// \'key\' should be 32 bytes for AES256,will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr,sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers,the output size will always be less than or
//equal to the input size plus the size of one block.
//That\'s why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,keyPtr,kCCKeySizeAES256,NULL /* initialization vector (optional) */,[self bytes],dataLength,/* input */
buffer,bufferSize,/* output */
&numBytesDecrypted);
NSLog(@\"Bytes decrypted: %d\",numBytesDecrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
NSLog(@\"Decrypt failed with error code %d\",cryptStatus);
free(buffer); //free the buffer;
return nil;
}
@end
当我使用以下代码从文件系统加载整个文件时,解密过程似乎工作正常:
[NSData dataWithContentsOfFile:dataPath];
当不使用上一个调用读取文件,而是使用外部代码对文件进行分块并仅使用少量数据初始化NSData并尝试对其进行解密时,就会发生问题,特别是当不同的线程使用此代码时,会出现问题(或者至少是我的想法):
- (NSData *)readDataOfLength:(NSUInteger)length
{
HTTPLogTrace2(@\"%@[%p]: readDataOfLength:%lu\",THIS_FILE,self,(unsigned long)length);
if (![self openFileIfNeeded])
{
// File opening failed,// or response has been aborted due to another error.
return nil;
}
// Determine how much data we should read.
//
// It is OK if we ask to read more bytes than exist in the file.
// It is NOT OK to over-allocate the buffer.
UInt64 bytesLeftInFile = fileLength - fileOffset;
NSUInteger bytesToRead = (NSUInteger)MIN(length,bytesLeftInFile);
// Make sure buffer is big enough for read request.
// Do not over-allocate.
if (buffer == NULL || bufferSize < bytesToRead)
{
bufferSize = bytesToRead;
buffer = reallocf(buffer,(size_t)bufferSize);
if (buffer == NULL)
{
HTTPLogError(@\"%@[%p]: Unable to allocate buffer\",self);
[self abort];
return nil;
}
}
// Perform the read
HTTPLogVerbose(@\"%@[%p]: Attempting to read %lu bytes from file\",bytesToRead);
ssize_t result = read(fileFD,buffer,bytesToRead);
// Check the results
if (result < 0)
{
HTTPLogError(@\"%@: Error(%i) reading file(%@)\",errno,filePath);
[self abort];
return nil;
}
else if (result == 0)
{
HTTPLogError(@\"%@: Read EOF on file(%@)\",filePath);
[self abort];
return nil;
}
else // (result > 0)
{
HTTPLogVerbose(@\"%@[%p]: Read %d bytes from file\",result);
fileOffset += result;
NSData *data = [NSData dataWithBytes:buffer length:result];
return [data AES256DecryptWithKey:@\"abcdefghijklmnopqrstuvwxyz123456\"];
//return data;
}
}
发生的情况是函数CCCrypt在这种情况下失败,错误代码为-4304 AKA \“ kCCDecodeError-输入数据未正确解码或解密。\”
此外,如果在CCCrypt调用中而不是kCCOptionPKCS7Padding传递了0->不填充,则该方法解密第一个数据块,但是在切换线程时,它失败,并显示-4300 AKA \“ kCCParamError-非法参数值。\”
在控制台中显示以下消息:
[Switching to process 13059 thread 0x0]
2011-05-25 18:00:03.631 Drm[1843:6e0b] Bytes decrypted: 131072
2011-05-25 18:00:03.647 Drm[1843:6e0b] Bytes decrypted: 68096
[Switching to process 11779 thread 0x0]
2011-05-25 18:00:04.547 Drm[1843:6e0b] Bytes decrypted: 0
2011-05-25 18:00:04.555 Drm[1843:6e0b] Decrypt failed with error code -4300
有人可以帮忙吗?
解决方法
AES是一种分组密码。您必须一次解密一个块。 AES块为128位(与
AES256DecryptWithKey
中的\“ 256 \”不相关)。因此,您必须确保传递的数据是16字节的倍数。
我没有尝试过用CCCrypt()
,这并不是真的。 CCCrypt()
是想要单次解密时的便利功能。当您想进行“随行随用”解密时,可以使用CCCryptorCreate()
,然后再多次调用CCCryptorUpdate()
,最后再调用CCCryptorFinal()
(或者可以先调用CCCryptorFinal()
,再调用CCCryptorReset()
,以使用相同的密钥解密更多内容)。最后,您拨打CCCryptorRelease()
释放密码。
EDIT我对此进行了更多思考,并意识到即使将输入分成16个字节的块,也不能以这种方式使用CCCrypt()
。每个AES加密块都会修改下一个块的IV,因此您不能只是在流中间开始某人。这就是为什么在整个会话中都需要一个持久的CCCryptor
对象的原因。