多线程环境中带有CCCrypt的NSData [英] NSData with CCCrypt in multi-threaded environment

查看:106
本文介绍了多线程环境中带有CCCrypt的NSData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用AES加密的文件。
我使用以下NSData类别:

I have a file that was encrypted using AES. I use the following NSData category:

#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

解密过程似乎有效当我使用以下代码从文件系统加载整个文件时很好:

The descrypt process seems to work fine when I load the entire file from filesystem with the following code:

[NSData dataWithContentsOfFile:dataPath];

在上一次调用时未读取文件,而是在外部代码中对文件进行分块时,会发生问题文件,并仅用一小部分数据初始化一个NSData并尝试对其解密,特别是当不同的线程使用此代码时(或至少是我的想法),就会出现问题:

The problem occurs when the file is not read with the previous call but when an external code that chunk the file and init an NSData with only a little chunk of data and try to decrypt this, in particolar the problems occurs when different thread use this code (or at least is what i think):

- (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-输入数据未正确解码或解密。

What happens is that the function CCCrypt in this case fail with error code -4304 AKA "kCCDecodeError - Input data did not decode or decrypt properly."

此外,如果在CCCrypt调用中而不是kCCOptionPKCS7Padding,我传递0->无填充该方法解密第一个数据块,但在切换线程时失败,并显示-4300 AKA kCCParamError-非法参数值。

Moreover if in the CCCrypt call instead of kCCOptionPKCS7Padding i pass 0 -> No padding the method decrypt the first chunk of data but when the thread is switched it fails with -4300 AKA "kCCParamError - Illegal parameter value."

在控制台中显示以下消息:

With the following message in console:

[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字节的倍数。

AES is a block cipher. You must decrypt it one block at a time. An AES block is 128-bits (this is not related to the "256" in AES256DecryptWithKey). So you must ensure that the data you pass is a multiple of 16 bytes.

我没有尝试使用 CCCrypt() CCCrypt()是方便的功能,当您想进行一次解密时。当您想按需进行解密时,可以使用 CCCryptorCreate(),然后多次调用 CCCryptorUpdate()最后是 CCCryptorFinal()(或者您可以先调用 CCCryptorFinal(),然后再调用 CCCryptorReset( )用相同的密钥解密更多内容。最后,您调用 CCCryptorRelease()释放您的加密器。

I haven't tried to use CCCrypt() this way, that's not really what it's for. CCCrypt() is a convenience function when you want to do a one-shot decryption. When you want to do "as you go" decryption, you use CCCryptorCreate() and then multiple calls to CCCryptorUpdate() and finally CCCryptorFinal() (or you can call CCCryptorFinal() and then CCCryptorReset() to decrypt more stuff with the same key). Finally you call CCCryptorRelease() to release your cryptor.

编辑关于此内容的更多信息,并意识到即使将输入分成16个字节的块,也不能以这种方式使用 CCCrypt()。每个AES加密块都会修改下一个块的IV,因此您不能只是在流中间开始某人。这就是为什么在整个会话中都需要一个持久性 CCCryptor 对象的原因。

EDIT I was thinking about this some more, and realized that CCCrypt() can't be used this way even if you broke up the input into 16-byte chunks. Each block of AES encryption modifies the IV of the next block, so you can't just start someone in the middle of a stream. That's why you need a persistent CCCryptor object over the entire session.

这篇关于多线程环境中带有CCCrypt的NSData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆