Last active
July 18, 2017 11:07
-
-
Save lamprosg/55cf3bac74fa44c4e78cda78612b41d7 to your computer and use it in GitHub Desktop.
(iOS) Secure file saving
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define FILE_PASSWORD_BYTES_LENGTH 16 | |
| NSString *const MAIN_ENCRYPTION_SERVICE_NAME = @"com.yourDomain.Keychain.MainEncryption"; | |
| #pragma mark - Stored files | |
| - (NSString *)writeDataToEncryptedFile:(NSData *)theData withName:(NSString *)theName forAccount:(NSString*)account { | |
| NSData *readyToStoreData = [self encryptData:theData withPassword:[self getPasswordForAccount:account]]; | |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); | |
| NSString *documentsDirectory = paths.firstObject; | |
| NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:theName]; | |
| NSFileManager *fileManager = [NSFileManager defaultManager]; | |
| if([fileManager fileExistsAtPath:savedFilePath]){ | |
| [fileManager removeItemAtPath:savedFilePath error:nil]; | |
| } | |
| [readyToStoreData writeToFile:savedFilePath options:NSDataWritingFileProtectionComplete error:nil]; | |
| //Exclude files from backup in iCloud | |
| NSURL *documentsUrl = [[NSURL alloc] initWithString:savedFilePath]; | |
| NSError *error; | |
| if ([documentsUrl setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:&error] == NO) { | |
| NSLog(@"Error: Unable to exclude directory from backup: %@", error); | |
| } | |
| // Enable hardware-level encryption of files | |
| NSDictionary* attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete | |
| forKey:NSFileProtectionKey]; | |
| [fileManager setAttributes:attributes ofItemAtPath:savedFilePath error:nil]; | |
| return savedFilePath; | |
| } | |
| - (NSData *)readFileWithPath:(NSString *)fullFileName | |
| { | |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); | |
| NSString *documentsDirectory = paths.firstObject; | |
| NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:fullFileName]; | |
| NSData *encryptedData = [NSData dataWithContentsOfFile:savedFilePath]; | |
| #pragma mark - Cached files | |
| - (NSString *)writeDataToEncryptedTemporaryFile:(NSData *)theData withName:(NSString *)theName forAccount:(NSString*)account { | |
| NSData *readyToStoreData = [self encryptData:theData withPassword:[self getPasswordForAccount:account]]; | |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); | |
| NSString *documentsDirectory = paths.firstObject; | |
| NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:theName]; | |
| NSFileManager *fileManager = [NSFileManager defaultManager]; | |
| if([fileManager fileExistsAtPath:savedFilePath]){ | |
| [fileManager removeItemAtPath:savedFilePath error:nil]; | |
| } | |
| [readyToStoreData writeToFile:savedFilePath options:NSDataWritingFileProtectionComplete error:nil]; | |
| //Exclude files from backup in iCloud | |
| NSURL *documentsUrl = [[NSURL alloc] initWithString:savedFilePath]; | |
| NSError *error; | |
| if ([documentsUrl setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:&error] == NO) { | |
| NSLog(@"Error: Unable to exclude directory from backup: %@", error); | |
| } | |
| // Enable hardware-level encryption of files | |
| NSDictionary* attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete | |
| forKey:NSFileProtectionKey]; | |
| [fileManager setAttributes:attributes ofItemAtPath:savedFilePath error:nil]; | |
| return savedFilePath; | |
| } | |
| - (NSString *)writeDataToEncryptedTemporaryFile:(NSData *)theData withFileExtension:(NSString *)fileExtension forAccount:(NSString*)account { | |
| NSData *readyToStoreData = [self encryptData:theData withPassword:[self getPasswordForAccount:account]]; | |
| NSString *filename = [NSString stringWithFormat:@"%@.%@",[self getUniqueFilename],fileExtension]; | |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); | |
| NSString *documentsDirectory = paths.firstObject; | |
| NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:filename]; | |
| NSFileManager *fileManager = [NSFileManager defaultManager]; | |
| if([fileManager fileExistsAtPath:savedFilePath]){ | |
| [fileManager removeItemAtPath:savedFilePath error:nil]; | |
| } | |
| [readyToStoreData writeToFile:savedFilePath options:NSDataWritingFileProtectionComplete error:nil]; | |
| //Exclude files from backup in iCloud | |
| NSURL *documentsUrl = [[NSURL alloc] initWithString:savedFilePath]; | |
| NSError *error; | |
| if ([documentsUrl setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:&error] == NO) { | |
| NSLog(@"Error: Unable to exclude directory from backup: %@", error); | |
| } | |
| // Enable hardware-level encryption of files | |
| NSDictionary* attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete | |
| forKey:NSFileProtectionKey]; | |
| [fileManager setAttributes:attributes ofItemAtPath:savedFilePath error:nil]; | |
| return savedFilePath; | |
| } | |
| #pragma mark - Read files/data | |
| - (NSData*)readEncryptedDataFromTemporaryFileWithName:(NSString *)filename forAccount:(NSString*)account | |
| { | |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); | |
| NSString *documentsDirectory = [paths objectAtIndex:0]; | |
| NSString *savedFilePath = [documentsDirectory stringByAppendingPathComponent:filename]; | |
| NSData *encryptedData = [NSData dataWithContentsOfFile:savedFilePath]; | |
| return [self decryptData:encryptedData withPassword:[self getPasswordForAccount:account]]; | |
| } | |
| - (NSData*)readEncryptedDataFromTemporaryPath:(NSString *)fullPath forAccount:(NSString*)account | |
| { | |
| NSData *encryptedData = [NSData dataWithContentsOfFile:fullPath]; | |
| return [self decryptData:encryptedData withPassword:[self getPasswordForAccount:account]]; | |
| } | |
| #pragma mark - Helpers | |
| - (NSData *)generatePasswordWithLength:(NSInteger)length | |
| { | |
| int err = 0; | |
| NSMutableData* clientRandom = [NSMutableData dataWithLength:length]; | |
| err = SecRandomCopyBytes(kSecRandomDefault, length, [clientRandom mutableBytes]); | |
| if(err == 0) { | |
| NSData *base64Data = [clientRandom base64EncodedDataWithOptions:0]; | |
| NSData *returnData = [base64Data subdataWithRange:NSMakeRange(0, length)]; | |
| return returnData; | |
| } | |
| return nil; | |
| } | |
| - (NSString *)getPasswordForAccount:(NSString*)account { | |
| NSString *password = [self getPasswordForAccount:theAccount andService:MAIN_ENCRYPTION_SERVICE_NAME]; | |
| if (password.length == 0) { | |
| NSData *generatedPassword = [self generatePasswordWithLength:FILE_PASSWORD_BYTES_LENGTH]; | |
| password = [[NSString alloc] initWithData:generatedPassword | |
| encoding:NSUTF8StringEncoding]; | |
| [self setPassword:password forAccount:account]; | |
| } | |
| return password; | |
| } | |
| #pragma mark Encryption | |
| - (NSData *)encryptData:(NSData *)theData withPassword:(NSString *)thePassword | |
| { | |
| return [RNCryptor encryptData:theData password:thePassword];; | |
| } | |
| - (NSData *)decryptData:(NSData *)theData withPassword:(NSString *)thePassword | |
| { | |
| NSError *error = nil; | |
| NSData *plaintext = [RNCryptor decryptData:theData password:thePassword error:&error]; | |
| if (error != nil) { | |
| NSLog(@"ERROR:%@", error); | |
| return nil; | |
| } | |
| return plaintext; | |
| } | |
| #pragma mark - Unique filename | |
| - (NSString *)getUniqueFilename | |
| { | |
| return [[NSProcessInfo processInfo] globallyUniqueString]; | |
| } | |
| //Use a seperate class for this | |
| #pragma mark - Keychain | |
| - (BOOL)setPassword:(NSString *)thePassword forAccount:(NSString *)theAccount andService:(NSString *)serviceName | |
| { | |
| //Set the accessibility type | |
| [SAMKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlockedThisDeviceOnly]; | |
| NSError *error; | |
| BOOL success = [SAMKeychain setPassword:thePassword | |
| forService:serviceName | |
| account:theAccount | |
| error:&error]; | |
| if (error) { | |
| DLog(@"Keychain storing failed with error: %@",[error description]); | |
| } | |
| return success; | |
| } | |
| - (NSString*)getPasswordForAccount:(NSString *)theAccount andService:(NSString *)serviceName | |
| { | |
| //Set the accessibility type | |
| [SAMKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlockedThisDeviceOnly]; | |
| NSString *passwordRetrieved = [SAMKeychain passwordForService:serviceName | |
| account:theAccount]; | |
| return passwordRetrieved; | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment