【问题标题】:NSMutableData encryption in place using NSInputStream使用 NSInputStream 进行 NSMutableData 加密
【发布时间】:2014-05-14 14:25:42
【问题描述】:

我正在尝试使用 CommonCrypto 加密 NSMutableData 对象(将生成的字节复制到自身,而不复制它)。以前,我使用 CCCrypt() “一次性”方法,主要是因为它看起来很简单。我注意到我的数据对象在内存中重复了。 为了避免这种情况,我尝试使用缓冲区大小为 2048 字节的 NSInputStream 对象。我正在阅读我的 NSMutableData 对象,并不断调用 CCCryptorUpdate() 来处理加密。问题是,它似乎仍然是重复的。这是我当前的代码(请注意,它是 NSMutableData 上的一个类别 - 主要是因为历史原因 - 因此是“自我”引用):

- (BOOL)encryptWithKey:(NSString *)key
{
    // Key creation - not relevant to the dercribed problem
    char * keyPtr = calloc(1, kCCKeySizeAES256+1); 
    [key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding];

    // Create cryptographic context for encryption
    CCCryptorRef cryptor;
    CCCryptorStatus status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, keyPtr, kCCKeySizeAES256, NULL, &cryptor);
    if (status != kCCSuccess)
    {
        MCLog(@"Failed to create a cryptographic context (%d CCCryptorStatus status).", status);
    }

    // Initialize the input stream
    NSInputStream *inStream = [[NSInputStream alloc] initWithData:self];
    [inStream open];
    NSInteger result;
    // BUFFER_LEN is a define 2048
    uint8_t buffer[BUFFER_LEN];
    size_t bytesWritten;

    while ([inStream hasBytesAvailable])
    {
        result = [inStream read:buffer maxLength:BUFFER_LEN];
        if (result > 0)
        {
            // Encryption goes here
            status = CCCryptorUpdate(
                                     cryptor,               // Previously created cryptographic context
                                     &result,               // Input data
                                     BUFFER_LEN,            // Length of the input data
                                     [self mutableBytes],   // Result is written here
                                     [self length],         // Size of result
                                     &bytesWritten          // Number of bytes written
                                     );

            if (status != kCCSuccess)
            {
                MCLog(@"Error during data encryption (%d CCCryptorStatus status)", status);
            }
        }
        else
        {
            // Error
        }
    }

    // Cleanup
    [inStream close];
    CCCryptorRelease(cryptor);
    free(keyPtr);
    return ( status == kCCSuccess );
}

我在这里肯定遗漏了一些明显的东西,加密,甚至使用输入流对我来说有点新鲜......

【问题讨论】:

  • 输入输出缓冲区有什么问题?
  • 问题是有大文件需要加密,我不想让它们在内存中重复。
  • 所以真的没有问题,只是暂时的内存使用。嗯,这就是内存的用途——被使用。这称为过早优化,在确定存在问题之前进行优化。几乎总是浪费时间。
  • 虽然 60MB 的内存可能没什么大不了的,但它足够大且足够有趣,值得学习。探索这一点没有问题,特别是作为已经工作代码的优化。请记住,临时内存峰值可能会导致其他应用程序出现不必要的内存警告,迫使它们转储缓存甚至可能被杀死,从而降低用户的整体体验。尝试减少这种情况是值得的(一旦你有一个可行的解决方案,并且不会引入过多的复杂性)。

标签: ios encryption nsinputstream commoncrypto


【解决方案1】:

只要您只调用一次CCUpdate(),您就可以加密到您读取的同一个缓冲区,而无需使用流。有关示例,请参阅RNCryptManager.m。学习applyOperation:fromStream:toStream:password:error:。我确实在这里使用了流,但如果您已经拥有NSData,则不需要这样做。

但是,您必须确保只调用一次 CCUpdate()。如果你多次调用它,它会破坏它自己的缓冲区。这是 CommonCryptor (radar://9930555) 中的一个开放错误。

附带说明:您的密钥生成非常不安全,并且对此类数据使用 ECB 模式几乎不能算作加密。它在密文中留下可用于解密数据的模式in some cases just by looking at it。如果您确实打算保护这些数据,我不推荐这种方法。如果你想学习如何很好地使用这些工具,请参阅Properly Encrypting With AES With CommonCrypto。如果您需要预打包的解决方案,请参阅RNCryptor。 (不过,RNCryptor 目前没有方便的就地加密方法。)

【讨论】:

  • 问题是文档很大。如果可能的话,我希望内存中的数据更少,这就是我认为我会使用流的原因。 (感谢您提供的链接,我一定会研究它们)。
  • 虽然就地转换肯定是一个有效的用例,但在传输数据时简单地解密或加密通常更方便。以 RNCryptor 的异步接口为例。例如,它可以在通过NSURLConnection 下载数据时轻松解密数据。只需一点额外代码,它就可以在您读取流时解密或在编写流时加密,因此您永远不需要在内存中使用加密版本。
  • 这是一个完全有效的观察结果。不幸的是,我无法更改现有(甚至是糟糕的)解决方案,因此就地加密将是我唯一的选择。我也尝试在没有缓冲区的情况下解决它(通过使用一次性 CCCrypt(),或者使用 CCCreate() 和 CCCUpdate() 的“常规”过程,我没有设法解决它。
  • 您尝试过使用指向同一个缓冲区的密文和明文指针对 CCUpdate 进行一次调用?只要您只调用一次,这应该可以工作。您可以尝试关闭填充(ECB 并非绝对必要)。这是 CCUpdater 错误的另一部分。
  • 我试过this,很遗憾没有解决问题。
【解决方案2】:

行内:

result = [inStream read:buffer maxLength:BUFFER_LEN];

数据被读入buffer 并且result 被设置为执行的结果。 行:

status = CCCryptorUpdate(cryptor, &result, ...

您应该使用buffer 作为输入数据,而不是状态

status = CCCryptorUpdate(cryptor, buffer, ...

使用更好的名称将有助于消除简单的错误。如果变量被命名为readStatus 而不是result,则很可能不会发生错误。同样,不是将数据变量命名为buffer,而是将其命名为streamData,事情也会更清楚。糟糕的命名确实会导致错误。

【讨论】:

  • 你绝对是正确的,但这不是重复的原因。
  • 我了解 Rob 关于CCUpdate().
猜你喜欢
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 2014-04-22
  • 1970-01-01
相关资源
最近更新 更多