Моя цель состоит в том, чтобы, имея файл / папку и пароль, иметь возможность шифровать и дешифровать его в AES с помощью Objective-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;
}
Проблема с этим кодом в том, что он использует около !!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:
, потому что я видел здесь , чтотаким образом, он не будет загружать все файлы в реальную память одновременно, только указанный диапазон.
РЕДАКТИРОВАТЬ 2
Пожалуйста, посмотрите мой ответ, что я делаю сейчас.