2011-05-25 68 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