objective-c - 多线程环境下的 NSData 与 CCCrypt

标签 objective-c aes nsdata

我有一个使用 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", 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,又称为“kCCDecodeError - 输入数据未正确解码或解密。”

此外,如果在 CCCrypt 调用中而不是 kCCOptionPKCS7Padding 我传递 0 -> 无填充,该方法会解密第一个数据 block ,但是当线程切换时,它会失败,并显示 -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 是一种分组密码。您必须一次解密一个 block 。 AES block 是 128 位(这与 AES256DecryptWithKey 中的“256”无关)。所以你必须保证你传递的数据是16字节的倍数。

我还没有尝试过使用CCCrypt()这样一来,这并不是真正的目的。 CCCrypt()当您想要进行一次性解密时,这是一个方便的功能。当您想要“随时”解密时,您可以使用 CCCryptorCreate()然后多次调用 CCCryptorUpdate()最后CCCryptorFinal() (或者您可以调用CCCryptorFinal(),然后调用CCCryptorReset()以使用相同的 key 解密更多内容)。最后你调用CCCryptorRelease()释放你的加密器。

编辑我又想了一些,并意识到 CCCrypt()即使您将输入分解为 16 字节 block ,也不能以这种方式使用。 AES 加密的每个 block 都会修改下一个 block 的 IV,因此您不能在流中间启动某人。这就是为什么你需要一个持久的 CCCryptor整个 session 中的对象。

关于objective-c - 多线程环境下的 NSData 与 CCCrypt,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6127318/

相关文章:

java - 如何使用OpenSSL解密Java AES加密数据?

java - C#.net 到 Java-使用带密码的 AES 加密和解密

ios - 显示大图像文件时在 viewDidAppear 之后调用 DidRecieveMemoryWarning

objective-c - 如何在 iOS 中使用 twitvid 在 Twitter 上上传视频?

Iphone 对象 C - 数据、对象和数组

php - 加密解密有什么问题?

ios - 知道来自 NSStream 的数据是完整的

ios - 将一系列位转换为 NSData 对象

objective-c - 创建自定义表格 View 分隔符时出错

ios - 自动布局错误