APP安全机制(八)—— 偏好设置的加密存储

版本记录

版本号 时间
V1.0 2017.08.29

前言

在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对app的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密

偏好设置存储信息的隐患

大家都知道,存储可以有很多种方式,其中一种就是偏好设置进行存储,但是在存储之前如果不进行任何加密的话,那么黑客只要是将沙盒攻陷获取里面的信息,你的数据就裸奔了,你的敏感数据就一览无余了,所以在偏好设置里面存储信息时最好进行加密存储。


有关偏好设置加密的一个框架

关于偏好设置加密,大家可以用自己的方法进行加密,还可以做的就是用第三方加密框架,这里要说的就是NSUserDefaults的一个分类SecureAdditions,大家可以用pod进行安装,如下所示:

pod 'SecureNSUserDefaults'

1. SecureAdditions API

下面我们就看一下该分类或者说框架的API接口。

1. NSUserDefaults+SecureAdditions.h
#import <Foundation/Foundation.h>

@interface NSUserDefaults (SecureAdditions)

- (void)setSecret:(NSString*)secret;

- (BOOL)secretBoolForKey:(NSString *)defaultName;
- (NSData*)secretDataForKey:(NSString *)defaultName;
- (NSDictionary*)secretDictionaryForKey:(NSString *)defaultName;
- (float)secretFloatForKey:(NSString *)defaultName;
- (NSInteger)secretIntegerForKey:(NSString *)defaultName;
- (NSArray *)secretStringArrayForKey:(NSString *)defaultName;
- (NSString *)secretStringForKey:(NSString *)defaultName;
- (double)secretDoubleForKey:(NSString *)defaultName;
- (NSURL*)secretURLForKey:(NSString *)defaultName;
- (id)secretObjectForKey:(NSString *)defaultName;

- (void)setSecretBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setSecretFloat:(float)value forKey:(NSString *)defaultName;
- (void)setSecretInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setSecretDouble:(double)value forKey:(NSString *)defaultName;
- (void)setSecretURL:(NSURL *)url forKey:(NSString *)defaultName;
- (void)setSecretObject:(id)value forKey:(NSString *)defaultName;

@end
2. NSUserDefaults+SecureAdditions.m
#import "NSUserDefaults+SecureAdditions.h"
#import "CocoaSecurity.h"

#define kStoredObjectKey              @"storedObject"

@implementation NSUserDefaults (SecureAdditions)

static NSString *_secret = nil;

#pragma mark - Getter methods

- (BOOL)secretBoolForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object boolValue];
    } else {
        return NO;
    }
}

- (NSData*)secretDataForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSData class]]) {
        return object;
    } else {
        return nil;
    }
}

- (NSDictionary*)secretDictionaryForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSDictionary class]]) {
        return object;
    } else {
        return nil;
    }
}

- (float)secretFloatForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object floatValue];
    } else {
        return 0.f;
    }
}

- (NSInteger)secretIntegerForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object integerValue];
    } else {
        return 0;
    }
}

- (NSArray *)secretStringArrayForKey:(NSString *)defaultName
{
    id objects = [self secretObjectForKey:defaultName];
    if([objects isKindOfClass:[NSArray class]]) {
        for(id object in objects) {
            if(![object isKindOfClass:[NSString class]]) {
                return nil;
            }
        }
        return objects;
    } else {
        return nil;
    }
}

- (NSString *)secretStringForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSString class]]) {
        return object;
    } else {
        return nil;
    }
}

- (double)secretDoubleForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSNumber class]]) {
        return [object doubleValue];
    } else {
        return 0;
    }
}

- (NSURL*)secretURLForKey:(NSString *)defaultName
{
    id object = [self secretObjectForKey:defaultName];
    if([object isKindOfClass:[NSURL class]]) {
        return object;
    } else {
        return nil;
    }
}

- (id)secretObjectForKey:(NSString *)defaultName
{
    // Check if we have a (valid) key needed to decrypt
    NSAssert(_secret, @"Secret may not be nil when storing an object securely");
    
    // Fetch data from user defaults
    NSData *data = [self objectForKey:defaultName];
    
    // Check if we have some data to decrypt, return nil if no
    if(data == nil) {
        return nil;
    }
    
    // Try to decrypt data
    @try {
        
        // Generate key and IV
        CocoaSecurityResult *keyData = [CocoaSecurity sha384:_secret];
        NSData *aesKey = [keyData.data subdataWithRange:NSMakeRange(0, 32)];
        NSData *aesIv = [keyData.data subdataWithRange:NSMakeRange(32, 16)];
        
        // Decrypt data
        CocoaSecurityResult *result = [CocoaSecurity aesDecryptWithData:data key:aesKey iv:aesIv];
        
        // Turn data into object and return
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:result.data];
        id object = [unarchiver decodeObjectForKey:kStoredObjectKey];
        [unarchiver finishDecoding];
        return object;
    }
    @catch (NSException *exception) {
        
        // Whoops!
        NSLog(@"Cannot receive object from encrypted data storage: %@", exception.reason);
        return nil;
        
    }
    @finally {}
}

#pragma mark - Setter methods

- (void)setSecret:(NSString*)secret
{
    _secret = secret;
}

- (void)setSecretBool:(BOOL)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithBool:value] forKey:defaultName];
}

- (void)setSecretFloat:(float)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithFloat:value] forKey:defaultName];
}

- (void)setSecretInteger:(NSInteger)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithInteger:value] forKey:defaultName];
}

- (void)setSecretDouble:(double)value forKey:(NSString *)defaultName
{
    [self setSecretObject:[NSNumber numberWithDouble:value] forKey:defaultName];
}

- (void)setSecretURL:(NSURL *)url forKey:(NSString *)defaultName
{
    [self setSecretObject:url forKey:defaultName];
}

- (void)setSecretObject:(id)value forKey:(NSString *)defaultName
{
    // Check if we have a (valid) key needed to encrypt
    NSAssert(_secret, @"Secret may not be nil when storing an object securely");
    
    @try {
        
        // Create data object from dictionary
        NSMutableData *data = [[NSMutableData alloc] init];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
        [archiver encodeObject:value forKey:kStoredObjectKey];
        [archiver finishEncoding];
        
        // Generate key and IV
        CocoaSecurityResult *keyData = [CocoaSecurity sha384:_secret];
        NSData *aesKey = [keyData.data subdataWithRange:NSMakeRange(0, 32)];
        NSData *aesIv = [keyData.data subdataWithRange:NSMakeRange(32, 16)];
        
        // Encrypt data
        CocoaSecurityResult *result = [CocoaSecurity aesEncryptWithData:data key:aesKey iv:aesIv];
        
        // Save data in user defaults
        [self setObject:result.data forKey:defaultName];
    }
    @catch (NSException *exception) {
        
        // Whoops!
        NSLog(@"Cannot store object securely: %@", exception.reason);
        
    }
    @finally {}
}

@end

大家看代码,可以看见里面使用的就是AES对称加密的方法。


加密框架的使用

下面我就举一个很小的例子,为大家说一下加密框架的使用。

#import <SecureNSUserDefaults/NSUserDefaults+SecureAdditions.h>
//该类方法的作用就是保存数据,里面就用到了这个框架

+ (void)saveProfile:(JJUserInfo *)userInfo
{
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setSecret:secretString];
    NSMutableDictionary *userDict = [[NSMutableDictionary alloc] init];
    [userDict setObject:userInfo.userId?:@""       forKey:kUid];
    [userDict setObject:userInfo.token?:@""        forKey:kToken];
    [userDict setObject:userInfo.nickname?:@""     forKey:kUser_nickname];
    [userDict setObject:userInfo.avatar?:@""     forKey:kAvater];
    [userDict setObject:userInfo.coin?:@"" forKey:kCoin];
    [userDict setObject:userInfo.balance?:@"" forKey:kBalance];
    [userDict setObject:userInfo.isArtist?:@"" forKey:kUserType];
    [userDict setObject:userInfo.easePasswordStr?:@"" forKey:kEasePassword];
    [userDefaults setSecretObject:userDict.copy forKey:userData];
    [userDefaults synchronize];
}

后记

关于APP安全是一个无法完结的话题,随着技术的深入,破解与反破解的技术进行一轮轮的博弈,所以未完,待续~~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 原文: http://mrpeak.cn/blog/encrypt/ 移动端App安全如果按CS结构来划分的话,主...
    vb12阅读 935评论 0 6
  • 烟水孤舟,渔樵垂钓。 我笑你痴,你笑我傲。 水底鱼儿也在笑, 笑什么? 我也不知道。 来来来, 一只竹笛,一曲小调...
    谌心阅读 343评论 0 0
  • 标题:Doing his darnedest Doing one's darnedest: 全力以赴to put ...
    LizhuHuang阅读 1,281评论 0 0
  • 大家好,这里是100字说成长 这里有最实用的干货分享,这里有最简短的方法论沉淀,100字说成长,用短短100字陈述...
    100字说成长阅读 712评论 0 3