2011-05-25 67 views
2

我有一个使用AES加密的文件。 我用下面的NSData类别:在多线程环境中使用CCCrypt的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 

的descrypt过程似乎正常工作当我加载从文件系统的整个文件用下面的代码:

[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", THIS_FILE, self); 

      [self abort]; 
      return nil; 
     } 
    } 

    // Perform the read 

    HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, bytesToRead); 

    ssize_t result = read(fileFD, buffer, bytesToRead); 

    // Check the results 

    if (result < 0) 
    { 
     HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath); 

     [self abort]; 
     return nil; 
    } 
    else if (result == 0) 
    { 
     HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath); 

     [self abort]; 
     return nil; 
    } 
    else // (result > 0) 
    { 
     HTTPLogVerbose(@"%@[%p]: Read %d bytes from file", THIS_FILE, self, 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 

有人能帮忙吗?

回答

2

AES是分组密码。您必须一次解密一个块。 AES块是128位(这与AES256DecryptWithKey中的“256”无关)。所以你必须确保你传递的数据是16字节的倍数。

我还没有尝试过使用CCCrypt()这种方式,这不是真的。 CCCrypt()是一个方便的功能,当你想做一次性解密。当你想要“随着你去”解密时,你使用CCCryptorCreate(),然后多次调用CCCryptorUpdate(),最后CCCryptorFinal()(或者你可以调用CCCryptorFinal()然后CCCryptorReset()用同一个密钥解密更多的东西)。最后你打电话CCCryptorRelease()发布你的密码。

编辑我正在考虑这一点,并意识到CCCrypt()不能用这种方式,即使你把输入分解成16字节的块。每个AES加密块都会修改下一个块的IV,因此您不能只在流的中间启动某个人。这就是为什么你需要在整个会话中持续使用CCCryptor对象。

+0

所以你的建议是有一个CCCryptor的共享引用,并在多个CCCryptorUpdate()调用之间使用它?我一定会试一试,我会让你知道的! 但kCCOptionPKCS7Padding选项不用于不关心填充? – 2011-05-25 17:41:51

+0

kCCOptionPKCS7Padding表示它应该将PKCS填充应用于您所递交的内容。你把它交给流的中间。您不能只在流的中间应用填充。 (你还需要跟踪你的IV,所以你不能只是在流的中间开始解密。) – 2011-05-25 17:45:45

+0

我试图实现你的建议我,但仍然存在问题:调用我的代码的类因此需要大量的数据(并且我没有控制权)有时需要的数据块不是16个字节的倍数,而通常需要的是2个字节,这会中断我的解密过程 – 2011-05-30 14:25:17