Ale*_*lex 8 security macos memory-management aes objective-c
我的目标是能够获得文件/文件夹和密码,使用Objective-C在AES中对其进行加密和解密.我不是加密书呆子或任何东西,但我之所以选择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;
}
Run Code Online (Sandbox Code Playgroud)
这段代码的问题在于它使用了!! 5 !! 在内存中的时间,用户选择的文件大小(使用NSMutableData打开).从用户的角度来看这是完全不可接受的(想象一下加密内存中2Gb到10Gb的文件),但我真的很茫然.
你能建议任何可以解决这个问题的修改吗?可能一次加密一个块(这样只有一个或两个chunck同时在内存中,而不是整个文件*5).最大的问题是我不知道该怎么做.有任何想法吗?
谢谢
PS:当我使用这个类别时,我这样做:
NSMutableData* data = [NSMutableData dataWithContentsOfFile: @"filepath"];
[data AES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];
Run Code Online (Sandbox Code Playgroud)
而这3行就产生了如此大的内存问题.
顺便说一下,OH:我需要一个初始化向量吗?我认为它更安全,或者其他什么,但我不知道.如果真的有需要,你能告诉我怎么做吗?
这就是我现在正在做的事情:
NSMutableData* data = [NSMutableData dataWithContentsOfMappedFile: @"filepath"];
[data SafeAES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];
Run Code Online (Sandbox Code Playgroud)
并且该类别中的新方法:
- (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);
Run Code Online (Sandbox Code Playgroud)
我无法理解为什么这有时会起作用,有时则不然.我真的很茫然.有人可以检查一下这段代码吗?
为了不将所有文件一次加载到内存中,我使用-dataWithContentsOfMappedFile,然后调用-getBytes:range:,因为我在这里看到它不会一次将所有文件加载到实际内存中,只加载指定的范围.
请看我现在正在做的回答.
我决定离开舒适的 Objc-C 领域,并用 C 函数重写上面的第二个 NSMutableData 类别。我已经尽力了,但如果这段代码有缺陷,我也不会感到惊讶,所以请提出建议!我还放弃了“方案”类别,并决定采用独立的方法。这里:
// What do you think this number should be? 16B, 256B...? 1KB, 1MB? Please tell me
#define kChunkSizeBytes (1024*1024) // 1 MB
- (BOOL)cryptFile: (NSString*)oldFPath
toFile: (NSString*)newFPath
withPassword: (NSString*)password
andOperation: (CCOperation)operation
{
// READ PASSWORD
// 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 (![password getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
{ return FALSE; } // Length of 'key' is bigger than keyPtr
// CREATE CRYPTOR
CCCryptorRef cryptor;
CCCryptorStatus cryptStatus = CCCryptorCreate(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL, // IV - needed?
&cryptor);
if (cryptStatus != kCCSuccess) {
return FALSE; // Handle error here
}
// OPEN OLD FILE AND READ SIZE
FILE* oldFile = fopen([oldFPath UTF8String], "rb");
if(oldFile == NULL) {
return FALSE; // Could not open old file
}
fseek(oldFile, 0, SEEK_END);
size_t oldFileSize = ftell(oldFile);
fseek(oldFile, 0, SEEK_SET);
// OPEN NEW FILE
FILE* newFile = fopen([newFPath UTF8String], "ab");
if(newFile == NULL) {
return FALSE; // Could not open new file
}
// ..CRYPT
NSInteger byteOffset;
size_t dataOutMoved;
size_t dataInLength = kChunkSizeBytes;
size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);
const void* dataIn = malloc(dataInLength);
void* dataOut = malloc(dataOutLength);
// ..crypt data one chunk at a time
for (byteOffset = 0; byteOffset <= oldFileSize; byteOffset += kChunkSizeBytes) {
if ([[NSThread currentThread] isCancelled]) { break; }
if ((byteOffset + kChunkSizeBytes) > oldFileSize) { dataInLength = oldFileSize - byteOffset; }
else { dataInLength = kChunkSizeBytes; }
fseeko(oldFile, byteOffset, SEEK_SET);
fread(dataIn, 1, dataInLength, oldFile);
CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);
fwrite(dataOut, 1, dataOutMoved, newFile);
}
// If thread hasn't been cancelled, finalize
if (![[NSThread currentThread] isCancelled]) {
CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
fwrite(dataOut, 1, dataOutMoved, newFile);
}
// CLOSE AND RELEASE
free(dataIn);
free(dataOut);
fclose(oldFile);
fclose(newFile);
CCCryptorRelease(cryptor);
return TRUE;
}
Run Code Online (Sandbox Code Playgroud)
我知道“for”循环内部没有错误检查,其他地方也可能出现这种情况。请对此提出建议!那里有一些代码检查线程是否已被取消。那是因为这段代码是在我的类控制的单独线程上运行的。每当用户单击“取消”按钮时,我创建的线程就会发送取消消息。这些 if 确保线程确实取消。请随意提出建议(再次!)并在您喜欢的任何地方使用此代码:)
PS:我已经对此进行了测试,无论是加密还是解密,到目前为止都完美无缺。我最初的问题(内存太多)似乎也解决了!
| 归档时间: |
|
| 查看次数: |
5525 次 |
| 最近记录: |