APP安全机制(十五) —— Keychain Services API使用简单示例(二)

版本记录

版本号 时间
V1.0 2019.01.01 星期二

前言

在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对app的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密
8. APP安全机制(八)—— 偏好设置的加密存储
9. APP安全机制(九)—— 基本iOS安全之钥匙链和哈希(一)
10. APP安全机制(十)—— 基本iOS安全之钥匙链和哈希(二)
11. APP安全机制(十一)—— 密码工具:提高用户安全性和体验(一)
12. APP安全机制(十二)—— 密码工具:提高用户安全性和体验(二)
13. APP安全机制(十三)—— 密码工具:提高用户安全性和体验(三)
14. APP安全机制(十四) —— Keychain Services API使用简单示例(一)

Testing the Behavior

在本节中,您将了解如何为包装器集成单元测试。 特别是,您将测试包装器公开的功能。

1. Creating the Class

要创建包含所有单元测试的类,请单击File ▸ New ▸ File… 并选择iOS ▸ Source ▸ Unit Test Case Class。 在下一个屏幕上,将类名指定为SecureStoreTests,子类XCTestCase并确保语言为Swift。 单击Next,选择SecureStoreTests组,验证是否已选中SecureStoreTests目标复选框,然后单击Create

Xcode将提示一个对话框来创建一个Objective-C桥接头。 单击Don’t Create以跳过创建。

打开SecureStoreTests.swift文件并删除花括号内的所有代码。

接下来,在import XCTest语句下面添加以下内容:

@testable import SecureStore

这使单元测试访问SecureStore框架中定义的类和方法。

注意:您可能会看到“No such module”错误。 不用担心,当您到本教程的这一部分结束并执行测试时,错误将消失。

接下来,在SecureStoreTests的顶部添加以下属性:

var secureStoreWithGenericPwd: SecureStore!
var secureStoreWithInternetPwd: SecureStore!

接下来,添加一个新的setUp()方法,如下所示:

override func setUp() {
  super.setUp()
  
  let genericPwdQueryable =
    GenericPasswordQueryable(service: "someService")
  secureStoreWithGenericPwd =
    SecureStore(secureStoreQueryable: genericPwdQueryable)
  
  let internetPwdQueryable =
    InternetPasswordQueryable(server: "someServer",
                              port: 8080,
                              path: "somePath",
                              securityDomain: "someDomain",
                              internetProtocol: .https,
                              internetAuthenticationType: .httpBasic)
  secureStoreWithInternetPwd =
    SecureStore(secureStoreQueryable: internetPwdQueryable)
}

由于您测试了通用密码和Internet密码,因此您可以使用两种不同的配置创建包装器的两个实例。 这些配置是您在上一节中开发的配置。

在您忘记之前,您需要在测试的拆卸阶段清除钥匙串的状态,以便下次可以重新开始。 将此方法添加到类的末尾:

override func tearDown() {
  try? secureStoreWithGenericPwd.removeAllValues()
  try? secureStoreWithInternetPwd.removeAllValues()
  
  super.tearDown()
}

由于您应该独立于其他测试隔离和执行每个测试,因此您将删除Keychain中已有的所有密码。 执行顺序无关紧要。

现在是时候为通用密码添加单元测试了。

2. Testing Generic Passwords

tearDown()下面添加下面代码

// 1
func testSaveGenericPassword() {
  do {
    try secureStoreWithGenericPwd.setValue("pwd_1234", for: "genericPassword")
  } catch (let e) {
    XCTFail("Saving generic password failed with \(e.localizedDescription).")
  }
}

// 2
func testReadGenericPassword() {
  do {
    try secureStoreWithGenericPwd.setValue("pwd_1234", for: "genericPassword")
    let password = try secureStoreWithGenericPwd.getValue(for: "genericPassword")
    XCTAssertEqual("pwd_1234", password)
  } catch (let e) {
    XCTFail("Reading generic password failed with \(e.localizedDescription).")
  }
}

// 3
func testUpdateGenericPassword() {
  do {
    try secureStoreWithGenericPwd.setValue("pwd_1234", for: "genericPassword")
    try secureStoreWithGenericPwd.setValue("pwd_1235", for: "genericPassword")
    let password = try secureStoreWithGenericPwd.getValue(for: "genericPassword")
    XCTAssertEqual("pwd_1235", password)
  } catch (let e) {
    XCTFail("Updating generic password failed with \(e.localizedDescription).")
  }
}

// 4
func testRemoveGenericPassword() {
  do {
    try secureStoreWithGenericPwd.setValue("pwd_1234", for: "genericPassword")
    try secureStoreWithGenericPwd.removeValue(for: "genericPassword")
    XCTAssertNil(try secureStoreWithGenericPwd.getValue(for: "genericPassword"))
  } catch (let e) {
    XCTFail("Saving generic password failed with \(e.localizedDescription).")
  }
}


// 5
func testRemoveAllGenericPasswords() {
  do {
    try secureStoreWithGenericPwd.setValue("pwd_1234", for: "genericPassword")
    try secureStoreWithGenericPwd.setValue("pwd_1235", for: "genericPassword2")
    try secureStoreWithGenericPwd.removeAllValues()
    XCTAssertNil(try secureStoreWithGenericPwd.getValue(for: "genericPassword"))
    XCTAssertNil(try secureStoreWithGenericPwd.getValue(for: "genericPassword2"))
  } catch (let e) {
    XCTFail("Removing generic passwords failed with \(e.localizedDescription).")
  }
}

这里有很多东西,所以要把它分解:

  • 1) testSaveGenericPassword()方法验证它是否可以正确保存密码。
  • 2) testReadGenericPassword()首先保存密码然后检索密码,检查密码是否等于预期的密码。
  • 3) testUpdateGenericPassword()验证为同一帐户保存不同的密码时,最新的密码是检索后的预期密码。
  • 4) testRemoveGenericPassword()测试它可以删除特定帐户的密码。
  • 5) 最后,testRemoveAllGenericPasswords检查是否从Keychain中删除了与特定服务相关的所有密码。

由于您的包装器可以抛出异常,因此每个catch块会在出现问题时使测试失败。

3. Checking Your Work

现在是时候验证一切都按预期工作了。 选择TestHost作为Xcode项目的活动scheme

按键盘上的Command-U(或选择Product ▸ Test中的测试)以执行单元测试。

注意:您不需要像通常在教程中那样运行应用程序。 在本教程中,您将通过执行单元测试来检查代码。

显示测试导航器并等待测试执行。 一旦完成,你会发现所有五个测试都是绿色的。太好了!

接下来,对互联网密码执行相同操作。

滚动到类的末尾,在最后一个花括号之前添加以下内容:

func testSaveInternetPassword() {
  do {
    try secureStoreWithInternetPwd.setValue("pwd_1234", for: "internetPassword")
  } catch (let e) {
    XCTFail("Saving Internet password failed with \(e.localizedDescription).")
  }
}

func testReadInternetPassword() {
  do {
    try secureStoreWithInternetPwd.setValue("pwd_1234", for: "internetPassword")
    let password = try secureStoreWithInternetPwd.getValue(for: "internetPassword")
    XCTAssertEqual("pwd_1234", password)
  } catch (let e) {
    XCTFail("Reading internet password failed with \(e.localizedDescription).")
  }
}

func testUpdateInternetPassword() {
  do {
    try secureStoreWithInternetPwd.setValue("pwd_1234", for: "internetPassword")
    try secureStoreWithInternetPwd.setValue("pwd_1235", for: "internetPassword")
    let password = try secureStoreWithInternetPwd.getValue(for: "internetPassword")
    XCTAssertEqual("pwd_1235", password)
  } catch (let e) {
    XCTFail("Updating internet password failed with \(e.localizedDescription).")
  }
}

func testRemoveInternetPassword() {
  do {
    try secureStoreWithInternetPwd.setValue("pwd_1234", for: "internetPassword")
    try secureStoreWithInternetPwd.removeValue(for: "internetPassword")
    XCTAssertNil(try secureStoreWithInternetPwd.getValue(for: "internetPassword"))
  } catch (let e) {
    XCTFail("Removing internet password failed with \(e.localizedDescription).")
  }
}

func testRemoveAllInternetPasswords() {
  do {
    try secureStoreWithInternetPwd.setValue("pwd_1234", for: "internetPassword")
    try secureStoreWithInternetPwd.setValue("pwd_1235", for: "internetPassword2")
    try secureStoreWithInternetPwd.removeAllValues()
    XCTAssertNil(try secureStoreWithInternetPwd.getValue(for: "internetPassword"))
    XCTAssertNil(try secureStoreWithInternetPwd.getValue(for: "internetPassword2"))
  } catch (let e) {
    XCTFail("Removing internet passwords failed with \(e.localizedDescription).")
  }
}

请注意,上面的代码与之前分析的代码相同。 您刚刚使用secureStoreWithInternetPwd替换了引用secureStoreWithGenericPwd

选择TestHost作为活动scheme(如果尚未选择),然后按键盘上的Command-U再次进行测试。 现在所有的通用和互联网密码测试都应该是绿色的。

恭喜! 您现在有一个可用的独立框架和单元测试。

后记

本篇主要讲述了Keychain Services API使用简单示例,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容