Skip to content

Instantly share code, notes, and snippets.

@lamprosg
Last active July 18, 2017 11:07
Show Gist options
  • Select an option

  • Save lamprosg/55cf3bac74fa44c4e78cda78612b41d7 to your computer and use it in GitHub Desktop.

Select an option

Save lamprosg/55cf3bac74fa44c4e78cda78612b41d7 to your computer and use it in GitHub Desktop.
(iOS) Secure file saving
#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