Моя цель - иметь возможность, получив файл / папку и пароль, зашифровать и расшифровать их в AES, используя Цель-C. Я не крипто-ботаник или что-то в этом роде, но я выбрал AES, потому что нашел его довольно стандартным и очень безопасным. Я использую категорию NSMutableData, в которой есть методы для шифрования и дешифрования данных. Вот он:
- (NSInteger)AES256EncryptionWithKey: (NSString*)key {
// The 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
if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
{ return 2; } // Length of 'key' is bigger than keyPtr
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 numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL , // initialization vector (optional)
[self bytes], dataLength, // input bytes and it's length
buffer, bufferSize, // output buffer and it's length
&numBytesEncrypted); // ??
if (cryptStatus == kCCSuccess) {
// The returned NSData takes ownership of the buffer and will free it on deallocation
[self setData: [NSData dataWithBytesNoCopy: buffer length: numBytesEncrypted]];
return 0;
}
free(buffer); // Free the buffer;
return 1;
}
- (NSInteger)AES256DecryptionWithKey: (NSString*)key {
// The 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
if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
{ return 2; } // Length of 'key' is bigger than keyPtr
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);
if (cryptStatus == kCCSuccess) {
// The returned NSData takes ownership of the buffer and will free it on deallocation
[self setData: [NSData dataWithBytesNoCopy: buffer length: numBytesDecrypted]];
return 0;
}
free(buffer); // Free the buffer;
return 1;
}
Проблема с этим кодом в том, что он использует about !! 5 !! раз в памяти размер файла (открытого с помощью NSMutableData), который выбирает пользователь. Это совершенно неприемлемо с точки зрения пользователя (представьте себе шифрование файла размером 2–10 ГБ в памяти ??), но я действительно в затруднении.
Не могли бы вы предложить какие-либо модификации, которые решат эту проблему? Вероятно, шифрование одного фрагмента за раз (таким образом, в памяти одновременно находится только один или два фрагмента, а не весь файл * 5). Большая проблема в том, что я не знаю, как это делать. Есть идеи?
Спасибо
PS: Когда я использую эту категорию, я делаю это следующим образом:
NSMutableData* data = [NSMutableData dataWithContentsOfFile: @"filepath"];
[data AES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];
И только эти 3 строки создают такую большую проблему с памятью.
Ой, между прочим: я могу нужен вектор инициализации? Я думаю, что это более безопасно, или что-то в этом роде, но я не знаю. Если действительно есть необходимость, не могли бы вы рассказать мне, как это сделать?
Вот чем я сейчас занимаюсь:
NSMutableData* data = [NSMutableData dataWithContentsOfMappedFile: @"filepath"];
[data SafeAES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];
И новый метод в категории:
- (void)SafeAES256EncryptionWithKey: (NSString*)key {
// The 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
if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
{ return 2; } // Length of 'key' is bigger than keyPtr
CCCryptorRef cryptor;
CCCryptorStatus cryptStatus = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL, // IV - needed?
&cryptor);
if (cryptStatus != kCCSuccess) {
; // Handle error here
}
NSInteger startByte;
size_t dataOutMoved;
size_t dataInLength = kChunkSizeBytes; // #define kChunkSizeBytes (16)
size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);
const void* dataIn = malloc(dataInLength);
void* dataOut = malloc(dataOutLength);
for (startByte = 0; startByte <= [self length]; startByte += kChunkSizeBytes) {
if ((startByte + kChunkSizeBytes) > [self length]) { dataInLength = [self length] - startByte; }
else { dataInLength = kChunkSizeBytes; }
NSRange bytesRange = NSMakeRange(startByte, (int)dataInLength);
[self getBytes: dataIn range: bytesRange];
CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);
if (dataOutMoved != dataOutLength) {
NSLog(@"dataOutMoved != dataOutLength");
}
[self replaceBytesInRange: bytesRange withBytes: dataOut];
}
CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
[self appendBytes: dataOut length: dataOutMoved];
CCCryptorRelease(cryptor);
Я не могу понять почему это иногда работает, а иногда нет. Я действительно в растерянности. Не мог бы кто-нибудь проверить этот код?
Чтобы не загружать весь файл в память сразу, я использую -dataWithContentsOfMappedFile
, а затем вызываю -getBytes: range:
, потому что Я видел здесь , что таким образом он не будет загружать сразу весь файл в реальную память, только указанный диапазон.
Пожалуйста, посмотрите мой ответ о том, что я делаю сейчас.