zoukankan      html  css  js  c++  java
  • iOS利用钥匙串保存获取设备的唯一标识

    简介

    还在假期中便收到了来自产品狗们的需求文档,然后在火车上的时候便看了一下他们提出的几点需求。因为是版本迭代,所以说大功能并没有变多少,只是小 修小补,但是有两个问题我还是比较关注的,一个是苹果对中国开放Apple Pay,要求可以内部充值或者付款,第二就是,要同一设备记录登陆过的手机号。第一个在上面的文章中已经部分介绍,下面主要介绍如果获得设备的唯一标识, 以及当该软件被删除重新安装以及更新后,如何保持唯一标识不变。
    在非越狱的手机上获取某个硬件信息生成唯一标识,第一只能找到苹果的漏洞,第二就是调用一些私有接口,显然这两条路都比较艰难,并不可持续发展,所以网上 大部分的唯一标识都是从操作系统层面获取的,在重置手机系统的时候都会被清除,在系统升级、卸载重装、备份恢复都可以保留,现在本人尚未发现可以使用严格 意义上的唯一标识。接下来我想跟大家探讨的是如何通过“合法”的手段来尽量拿到不会轻易发生变化的“唯一标识”。
    在iOS5之前,苹果提供了一个uniqueIdentifier的接口来获取设备的唯一标识,只是出于隐私保护,苹果在iOS5.0之后变废弃了,然后 大家应该都知道了,凡是用了uniqueIdentifier这个接口的,苹果直接都给拒掉了。没办法,只能另辟蹊径了。于是乎,iOS 6.0系统新增了两个用于替换uniqueIdentifier的接口,分别 是:identifierForVendor,advertisingIdentifier。在现在比较流行的友盟与talkingData统计中,标识 设备一个用的是OpenUUID+IDFA,一个用的是Keychain+IDFA。苹果公司规定说如果你的应用如果不存在广告那么你是不被允许使用 advertisingIdentifier的。所以,综上所述,最终从网上找到了解决方案:要么openudid要么Keychain+uuid。我个 人推荐第二种方案,至于为什么,看下面吧

    1. openudid是什么?Openudid是一个github上一个开源的项目:地址
      原理是利用iOS系统中的UIPasteboard剪贴板类,它用 app-special pastboards 来存储一160位的随机字符串,存取的方式类似字典的key-value。 app-special pastboards 可持久存储字符串,即使开关机、卸载应用,并能在app之间共享。Openudid的第一次访问的时候用key去检查剪贴板内是否存在对应的 value(随机数),如果不存在就生成一个并存储在Pasteboard中,第二次访问的时候就可以直接取到而不去生成新的随机数。
      但是iOS7之后,苹果封堵了剪贴板通信的漏洞,iOS之前是所有的应用都可以共享同一个剪贴板存储内容,现在只有在同一 CFBundleIdentifier标识下的App才能共享内容,如com.koudai.a和com.koudai.b,它们的 com.mycompany部分是一样的,就能共享(请用真机测试,模拟器会有偏差)。当你将设备中同一Vendor(Vendor就是 CFBundleIdentifier的前两部分com.xxxxxx)下的所有应用删除后,再重新下载安装(重新下载安装!重新下载安装!重要的事情说 三遍,别说你卸载了然后Xcode跑了发现没变,那你就卸载之后下个别的应用,再运行,看看!),在你重新运行应用的时候便会重新生成一个新的 openudid串,所以说单用这个作为设备的唯一标识显然是不科学的,要保持标识的唯一显然还是需要配合其他方案,但是在这里说一下,苹果都给你了 idfv作为标识,为什么还要用这种方案呢,万一哪天苹果再一不高兴,立马不让你用了,这不就傻比了么,所以,我个人还是不建议用这个方法。
    2. Keychain是什么?相信用过Mac的人都知道钥匙串这个东西,当我们用户名密码登陆某个网站时Mac总会弹出一个提示询问我们是否保存或者 更新账号信息,然后当我们登陆成功后,下次只要点击该输入框,就会显示出记录的账号密码,那么这些账号密码保存在哪里?那里安全么?其实大可放心,苹果将 其信息全部都存储在钥匙串里了。钥匙串,是苹果用来存储密码和证书的一块加密存储区域,目的是为了帮助用户安全存储应用或者浏览器的密码,省去了很多输入 密码和记密码的麻烦。keychain不是存储在手机的沙盒内,而是手机的某个公共区域,手机重启和应用卸载,都不会对这片存储区域造成影响,因为是加密 存储不存在被其他应用修改的问题,所以就有人拿keychain来存储唯一标识。
      在Max OS上访问keychian需要提供用户的登录密码,而在iOS上用户原则上只能访问本应用存储的keychain,除非是同一个 provisioning 证书的两个应用,比如美团的猫眼就能读取美团app中的keychian,用户第一次打开猫眼app就会弹出提示,用户可以读取美团的账号和密码免登录进 入猫眼。keychain是根据provision 证书来鉴定权限,所以app的版本需要使用同一个,否则版本之间会失效。用户恢复出厂设置,机器上的keychain会被清除,但如果事先对手机进行了备 份,keychain存储的内容依然有效。顺便提一句keychain在越狱的机器上是可以被导出的,所以存储敏感信息前请加密。
    3. UUID。它是苹果再iOS6后提供的一个获取大随机数的方法。UUID, 全球独立标识(Universal Unique Identifier),据wiki说UUID随机数算法得到的数重复概率为170亿分之一,170亿分之一什么概念?可以告诉你买一注双色球的中奖概率 是1700万分之一。随机算法有几套,包括用时间戳、MD5什么的,苹果是遵循的RFC 4122 version 4,大家可以去google下。
      NSString *uuid = [[NSUUID UUID] UUIDString];
      这个每次调用此方法得到的UUID肯定是不一样的,所以必须借助于持久化存储。
      NSString *uuid =
      [[UIDevice currentDevice].identifierForVendor UUIDString];

      这个方法在你程序不卸载重装的情况下获取到的uuid是不变的,(设备升级但应用未卸载情况下并没有测试)。这个返回的也是一个NSUUIID的对象,至于这两个方法的区别,我觉得应该是第二个又根据设备某个独有信息做了一些加密操作返回的值。

    本文中部分引用:
    http://www.jianshu.com/p/4a31e2a30b78

    实现

    根据Keychain+UUID实现获取设备唯一标识,地址:
    https://github.com/TripleStoneCheng/keychain-uuid.git

    部分重要属性讲解

    请参考上面的链接中github中的Demo,仔细阅读readme以及代码中的注释进行操作。其中在使用官方提供的Demo时,
    KeychainItemWrapper *itemWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"com.chenglei" accessGroup:nil];进 行实例化以后一定要记得将kSecAttrService或kSecAttrAccount进行赋值,否则会一直插入失败。但是经测试后发现如果用 @”UUID”这个字符串作为关键字时,kSecAttrService或kSecAttrAccount不用赋值也没问题,具体原因我也不造,等造了再 补上。

    写入

    因为kSecAttrService、kSecAttrAccount这俩是作为主键存在,在存入的时候两者必须一个不为 空,kSecAttrGeneric可有可无,当你创建有kSecAttrService或kSecAttrAccount时,必须对应的创建密码。为了 使获取的数据更加精确,我建议将kSecAttrService、kSecAttrAccount两个属性以及kSecAttrGeneric都写上。

    读取

    在读取的时候有以下几种情况
    1. kSecAttrGeneric有值
    在读取的时候,当钥匙串只利用kSecAttrGeneric这个关键字对本应用内的Keychain进行搜索时,当写入Keychain时只是设置不同 的kSecAttrService或者kSecAttrService,而设置相同的kSecAttrGeneric,则读出最近一次写入或更改的值,即 钥匙串最后一次修改同一kSecAttrGeneric所对应的值。如果不同,则取出对应的值。
    2. kSecAttrGeneric没有值
    当kSecAttrGeneric没有设置的时候,则查询条件可根据kSecAttrService或者kSecAttrAccount或者两者结合。
    3. kSecAttrService或kSecAttrAccount有值
    当kSecAttrService有值时,如果只查询kSecAttrService这个条件,则也是Keychain最近一次修改的所对应的同一kSecAttrService的值。kSecAttrAccount亦然。
    所以为了使查询结果更精确,则查询条件就得更加精确,最好是三个条件都写。这样不管是增删改查都不会造成误删数据。
    哦,对了,在这里说一下,如果你钥匙串中没有该值则调用查询或者删除API会返回相应的码来告诉你,该操作是错误的以及错误原因。

    错误:如果出现存取错误:-34018错误,那么很可能是你的证书或者配置文件出现了问题。

    转自:http://blog.csdn.net/chenglei9128/article/details/50727149

  • 相关阅读:
    Spring.Net的AOP的通知
    Spring.Net的IOC入门
    Unity依赖注入使用
    C#dynamic关键字(1)
    多线线程async与await关键字
    C#面试题
    MangoDB的C#Driver驱动简单例子
    安装vuecli和使用elememtUi
    再也不怕aop的原理了
    easyui实现多选框,并且获取值
  • 原文地址:https://www.cnblogs.com/superbobo/p/5235931.html
Copyright © 2011-2022 走看看