KeyChine的使用

转载 2015年11月21日 18:59:54

[转]iOS开发 -- KeyChain使用与共享数据

2014-5-1阅读1805 评论0

keyChain是iOS提供的一种安全保存私密数据的方式,整个系统的keychain被保存在/private/var/Keychains/keychain-2.db中,其中保存的数据是经过加密的(不过上有政策下有对策,越狱后导出keychain变为可能,请看我下篇文章)


keychain优点:

1.每个组(keychain-access-groups)之间数据访问隔离,没有权限的app如何读取他人数据,保证了数据安全。

2.全局统一存储,即使删除了app,keychain里的数据也还在,下次重新安装app后依然能访问

3.存储后的数据加密

4.同一个组的app可以共享keychain中的数据

5.暂时想不到了,求大伙补充


keychain缺点:

1.删除app后不会自动清除keychain里的数据,如果存储密码等敏感数据会有一定风险(越狱后keychain能被导出来)

2.有可能会速度略慢(原因是最近无意间导了台4s的keychain的数据库文件出来一看,好家伙,几万条数据,估计有和长年积累有关,也估计是有不少app已经把它当kv数据库用了····无语···)

3.暂时想不到了,求大伙补充


keychain使用:

好,先不几人忧天,言归正传先看看如何使用它。

keychain的读取和写入都有点类似组装一个查询条件然后传给Security.framework里的接口去做。首先new一个工程,导入Security.framework,然后写一个工具类如下:

#import <Foundation/Foundation.h>
#import <Security/Security.h>


@interface CHKeychain : NSObject

+ (void)save:(NSString *)service account:(NSString *)acc data:(NSData *)data;
+ (void)save:(NSString *)service data:(NSData *)data;

+ (NSData *)load:(NSString *)service;
+ (NSData *)load:(NSString *)service account:(NSString *)acc;


+ (void)delete:(NSString *)service;
+ (void)delete:(NSString *)service account:(NSString *)acc;
@end

//
//  CHKeychain.m
//  CleanWBKeyChain
//
//  Created by iBcker on 14-4-30.
//  Copyright (c) 2014年 iBcker. All rights reserved.
//

#import "CHKeychain.h"

@implementation CHKeychain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service account:(NSString *)acc{
    NSMutableDictionary *dicx = [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
            nil];
    if (acc) {
        dicx[(__bridge_transfer id)kSecAttrAccount]=acc;
    }
    if (service) {
        dicx[(__bridge_transfer id)kSecAttrService]=service;
    }
    return dicx;
}

+ (void)save:(NSString *)service account:(NSString *)acc data:(NSData *)data
{
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service account:acc];
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
    [keychainQuery setObject:data forKey:(__bridge_transfer id)kSecValueData];
    SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}

+ (void)save:(NSString *)service data:(id)data {
    [[self class] save:service account:service data:data];
}

+ (NSData *)load:(NSString *)service account:(NSString *)acc
{
    NSData *ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service account:acc];
    [keychainQuery setObject:(__bridge_transfer id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
    [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        ret = (__bridge_transfer NSData*)keyData;
    }
    if (keyData)
        CFRelease(keyData);
    return ret;
}

+ (NSData *)load:(NSString *)service {
   return [[self class] load:service account:service];
}

+ (void)delete:(NSString *)service {
   return [[self class] delete:service account:service];
}

+ (void)delete:(NSString *)service account:(NSString *)acc
{
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service account:acc];
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}
@end

然后使用的话就直接

        //写
        [CHKeychain save:@"uname" data:[@"xiaoxiao" dataUsingEncoding:NSUTF8StringEncoding]];
        [CHKeychain save:@"pwd" data:[@"123" dataUsingEncoding:NSUTF8StringEncoding]];
        
        //读
        NSData *nameData =[CHKeychain load:@"uname"];
        NSLog(@"name:%@",[[NSString alloc] initWithData:nameData encoding:NSUTF8StringEncoding]);


不过这只是举个例子,建议不要这么直接存,原因见上面提到的 keychain的缺点。所以建议大家可以把多条数据合并在一起然后再存,例如可以改为:

<span style="padding: 0px; margin: 0px;">	</span>//写
        NSDictionary *userInfo = @{@"uname":@"xiaoxiao",@"pwd":@"123"};
        [CHKeychain save:@"userInfo" data:[NSKeyedArchiver archivedDataWithRootObject:userInfo]];
        //读
        NSData *userData =[CHKeychain load:@"userInfo"];
        NSLog(@"userInfo:%@",[NSKeyedUnarchiver unarchiveObjectWithData:userData]);

当然,我个人还是建议先在代码里把数据进行一定的加密后再存入kechain,以加大逆向的难度



keychain数据共享:

如果我们有多个APP,他们之间又需要互相共享一下数据,那么我可以考虑下使用keychain进行数据共享。

首先我们需要新建一个权限申明文件,例如新建一 keychain.entitlements  ,上传了一个给需要的人(http://download.csdn.net/detail/u011690307/7279715

拖入xcode中,效果如下:




并且在build setting里面的Code Signing Entitlements填上keychain.entitlements





作用是定义我们能访问的组有$(AppIdentifierPrefix)*和$(AppIdentifierPrefix)$(CFBundleIdentifier),AppIdentifierPrefix为App的前缀,例如这串数字






例如你的AppIdentifierPrefix为7H96H3XX,那么$(AppIdentifierPrefix)*就代表7H96H3XX.*  ,$(AppIdentifierPrefix)$(CFBundleIdentifier)就代表7H96H3XX.com.xxx.xxx

注意,访问权限是你定义的所有item,而默认写入组是这些item的第一个。也就是说如果你现在写入keychain,那么所属组就是7H96H3XX.*,其他app也需要能访问7H96H3XX.*才能

读取到你的数据。如果不想让别人读取到你的数据,可以改为$(AppIdentifierPrefix)$(CFBundleIdentifier)在前面。


最后还需要提醒一点的是,如果你的帐号有多个AppIdentifierPrefix(team帐号),一需要确认一下你的mobileprovision文件里面的keychain-access-groups是否和你的keychain.entitlements文件定义的一致,否则xcode会报错。具体查看mobileprovision的方法是:命令行cat一下


定义好这些之后,就再new一个工程来看看能不能互相访问彼此存的数据吧,enjoy it。

AngularJS 的基础使用

AngularJS 把应用程序数据绑定到 HTML 元素。 AngularJS 可以克隆和重复 HTML 元素。 AngularJS 可以隐藏和显示 HTML 元素。 AngularJS 可以在 HT...
  • zhaodongwoshini
  • zhaodongwoshini
  • 2016年11月21日 23:42
  • 1216

Struts2的简单使用(一)

今天学习了一下Struts2框架的搭配~ 也简单的使用Struts2框架学习了Action和怎么使用核心配置文件struts.xml~ 主要实现了网页上的简单登录注册~ 项目结构如图: 写了俩...
  • qq_33642117
  • qq_33642117
  • 2016年07月11日 20:51
  • 1201

在Oracle中索引的使用

索引是由Oracle维护的可选结构,为数据提供快速的访问。准确地判断在什么地方需要使用索引是困难的,使用索引有利于调节检索速度。 当建立一个索引时,必须指定用于跟踪的表名以及一个或多个表列。一旦建立了...
  • ysyn1209
  • ysyn1209
  • 2016年04月22日 23:44
  • 7244

weblogic使用简介

 (1)安装weblogic,默认安装即可;  (2)在开始菜单中选择Weblogic 的Configuration新建weblogic配置     在BEA Weblogic Congigurati...
  • hxmcGu
  • hxmcGu
  • 2007年06月25日 13:35
  • 3755

cookie简单实用的使用方法

cookie的由来:             在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,...
  • qq_35488412
  • qq_35488412
  • 2017年03月09日 16:07
  • 1743

构造函数的用法学习

1。如果不为类编写构造函数,编译器就会创建一个默认的构造函数,运行时会在创建新对象时调用它。 2。构造函数互相之间的可链接关系using System; namespace Webtest{     ...
  • lovelxj
  • lovelxj
  • 2005年02月16日 11:56
  • 2244

C++错误:不允许使用不完整的类型

写了下面这个代码,结果在ifstream处提示“不允许使用不完整的类型”     string from,to;     cin>>from>>to;     ifstream is(f...
  • qq10593994
  • qq10593994
  • 2015年07月28日 20:36
  • 9705

Android-ViewPagerIndicator使用方法

转自http://blog.csdn.net/dalancon/article/details/41696373 现在很多的应用页面都是由一个个的TAB组成的,我们可以用布局加事件监听实现tab...
  • rankun1
  • rankun1
  • 2016年08月12日 18:29
  • 958

MyBatis基本使用步骤

MyBatis是一个数据持久层(ORM)框架。把实体 类和SQL语句之间建立了映射关系,是一种半自 动化的ORM实现。MyBATIS需要开发人员自己来写sql语句,这可以增加了程序的灵活性,在一定程度...
  • archer119
  • archer119
  • 2016年06月01日 23:27
  • 3351

eclipse使用大全

基本设置部分: 1、设置代码的字体类型和大小: Window -> Preferences -> General -> Appearance -> Content Assist -> Colors...
  • wl4066261
  • wl4066261
  • 2016年12月20日 00:08
  • 1972
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:KeyChine的使用
举报原因:
原因补充:

(最多只允许输入30个字)