指纹登陆
随着智能时代的来临,人们越来越习惯于将重要信息和个人隐私存放于智能手机中。这时手机数据的安全性就变得尤其重要。通常我们使用口令或密码来保护设备,但是随着iPhone和Android手机硬件的逐步升级与支持,使用指纹验证成为了代替密码的一种常规方法。
指纹验证是指通过手机中的触摸传感器来识别用户。通过这种技术我们可以使用指纹解锁设备或在应用中通过指纹授权使用例如指纹登录或指纹支付等功能。
为什么使用指纹验证
对于应用而言,使用指纹验证可以从多方面提升用户体验:
-
指纹验证是一种更加快捷方便的方法来验证用户身份。传统校验用户身份的方法,通常是使用密码或口令,要想越安全就需要使用越复杂的密码。相较于输入冗长且复杂的密码,指纹识别仅仅需要的是使用指尖触碰传感器完成验证。这无疑能给用户带来更加顺畅的使用体验。
-
指纹不会被遗忘。如上一条所说,通常密码都是冗长且复杂的,尤其是不常使用的密码,很容易在一段时间后被用户忘记。而使用指纹识别则完全没有这种烦恼,你可以随时使用指纹验证。
-
指纹验证无需找回功能。由于指纹永远不会被遗忘,也不会改变。通常密码找回的功能对于指纹验证来说就毫无意义。
-
指纹验证更加安全。通常为了方便记忆密码,用户倾向于使用方便记忆的信息(比如生日)来生成密码,这让密码变得很容易被**。而即便使用了较为复杂的密码,也无法保证其拥有和指纹一样的独特性和安全性。一旦密码被泄露或盗取,其他人能轻易获取到用户信息。
iOS技术背景
Touch ID
iOS设备下的指纹识别是通过iOS设备提供的Touch ID实现。
Touch ID技术包括在iOS设备上的一套先进的硬件与软件系统:
-
在iOS硬件设备上的HOME按键由蓝宝石水晶制成,能作为透镜获取到你手指皮肤下纹理的高清图片
-
Touch ID会通过你传入的一系列指纹图片,通过算法生成出属于指纹的数字标识,存储于特殊的芯片上
Touch ID只会存储通过指纹图片生成的数字标识,所以不用担心你真实的指纹泄露。由于指纹的独特性,Touch ID算出的数字标识能相同的概率是50000之1,就是50000个不同指纹才可能会有一对可以通过Touch ID相互匹配。所以Touch ID是一项足够安全的技术。
Touch ID已经备苹果普遍使用于iTunes Store, App Store和Apple Pay。微信和支付宝的支付也已经使用了Touch ID。所以Touch ID也是一项被广泛信赖并使用的技术。
Secure Enclave
如在介绍Touch ID时介绍的一样,Touch ID不会存储任何关于指纹的图片,而是存储的指纹标识。不过即使是这些数字标识也被iOS存储于一块叫做Secure Enclave(安全领域)的芯片上。你的指纹信息只会被用于比对是否与设备中存储的指纹信息进行比对,不能被操作系统,更不会被其他应用访问。这些信息也不会上传到苹果的服务或者被iCloud等云服务备份,不可能被用于与其他的指纹库进行比对。
LocalAuthentication
LocalAuthentication framework是我们的app来使用iOS设备上生物信息验证(Touch ID或者Touch ID)的机制。上文已经介绍过,为了最大限度的保护用户信息的私密性和安全性,iOS上使用了Secure Enclave的方式来将验证数据与设备上的其他系统隔离。用户的认证信息甚至无法被操作系统访问,我们通过认证得到的只是一条布尔值的结果。
Keychain
当需要存储私密信息时,你可以使用Keychain。Keychain是由iOS的Security framework提供的服务,你可以设置keychain中元素的访问权限为每次去读取这项数据时都需要用户的认证(通过Face ID或者Touch ID)。当每次去请求keychian中数据访问权限时,LocalAuthentiation Framework会通过系统展示相应的界面提示用户录入数据,然后将这些数据传入Secure Enclave进行比对。比对结束后,Secure Enclave会成功或失败的结果返回。其间用户或者操作系统都不会也不能获取到用户的指纹信息。
Android技术背景
BiometricPrompt API 和 FingerprintManager
现在很多安卓手机已经内置了触摸传感器,所以安卓在最新发布的Android 8提供了BiometricPrompt来提供指纹识别或面部识别的支持。Android 6以后的版本也可以使用FingerprintManager来提供指纹识别的支持。
Android Keystore System
安卓的Keystore System用于存储加密的Keys,不过不同于安卓的KeyChain。这些信息只能用于自己的应用中。
概述
在iOS和Android技术背景中我们得知:
-
我们通过指纹识别能获取到只是指纹是否匹配,并不能得到其他与指纹信息有关的结果
-
但是我们可以存储私密信息,并通过指纹获取访问权限
在此基础上,初步设计了指纹识别的流程。iOS和安卓在指纹识别上的流程由于API的限制不尽相同,但是大概的步骤基本一致:
-
打开指纹认证界面获取获取权限
-
如果步骤1指纹认证成功,获取到权限后尝试从设备中获取用户登录所需**
-
如果步骤2成功获取到用户**,即用户已经绑定了指纹登陆,那么获取用户**登陆
-
如果步骤2未能获取到用户**,即用户还未绑定指纹登陆,那么开始绑定指纹的流程
-
绑定指纹的流程可以分为前端和后端的工作,先由后端生成用户登陆所需的独特**传回给前端
-
前端收到**后将**存储于本地设备中
-
使用获取到的**就能成功登陆
简化流程如图:
红色方块为后端操作,蓝色方块为前段操作
iOS实现方案
iOS上的指纹登陆方案的主题内容为:
-
使用Security framework将用户**信息存储于keychain中
-
使用LocalAuthentication framework来验证用户指纹信息,并且获取到keychain中的**访问权限
配置工程
要使用Touch ID和Keychain的功能,首先需要将需要的framework添加到工程中。打开工程后选择对应的Target后点击Build Phases > Link Binary With Libraries,点击+号添加LocalAuthentication和Security两个framework。
开启指纹登陆
在用户使用指纹登陆之前,需要用户先开启指纹登陆功能。开启指纹登陆的具体步骤可细分为以下步骤。
1. 判断设备是否支持指纹
判断当前设备是否支持指纹识别,如果支持,在合适的时机提示用户是否开启指纹登陆,可以在用户成功登陆后或设置界面展示相应的界面提示用户开启指纹登陆。
var context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
2. 确认指纹信息,向服务器请求开启指纹登陆
当用户点击开启指纹登陆时,iOS设备通过LocalAuthentication framework调起系统指纹认证界面,如果认证通过向服务端发起开启指纹登陆的请求。
var context = LAContext()
let reason = "请确定开启指纹登陆"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { (success, error) in
if success {
} else {
}
})
3. 存储用户信息
iOS端收到服务端的响应,将其中的**解析出来,存储于Keychain中,并设置为只能通过TouchID访问。并将用户名和指纹识别开启状态存储于UserDefault中。
let access = SecAccessControlCreateWithFlags(nil,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
.userPresence,
nil)
let query: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrAccount as String: userInfo.name,
kSecAttrServer as String: AuthInfo.server,
kSecValueData as String: userInfo.authKey
];
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
}
4. 开启成功
使用指纹认证
在用户开启了指纹登陆功能后,就可以在登陆时试用指纹登陆。指纹登陆的步骤为:
1. 判断用户是否开启指纹登陆
判断当前设备是否支持指纹识别,如果支持,检查设备UserDefault中的数据,判断当前用户是否开启指纹登陆。
var context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
2. 获取用户数据
如果当前用户开启了指纹登陆,调起系统指纹认证界面,申请访问Keychain的权限,如果认证成功从Keychain中尝试取得存储于keychain中的用户**。
let query: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecUseOperationPrompt as String: "使用指纹登陆",
kSecReturnData as String: true
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else {
return nil
}
guard
let existingItem = item as? [String : Any],
let authKeyData = existingItem[kSecValueData as String] as? Data,
let authKey = String(data: authKeyData, encoding: .utf8),
let name = existingItem[kSecAttrAccount as String] as? String else {
return nil
}
let savedUserInfo = UserInfo(name: name, authKey: authKey)
3. 如果成功拿到用户**,使用**向服务端发起指纹登陆请求。
4. iOS端收到登陆请求的响应,判断是否登陆成功。
5. 如果响应为成功,完成登陆逻辑。
Android实现方案
Todo
总结
iOS的Demo工程已经上传至git,由于实际应用情况所有请求走的是SSL通道,所以并没有设计数据加解密的内容。如果需要,可以自行在向服务器请求生成**时,通过RSA算法生成public key传递给服务器作为**的加密key。
参考资料
About Touch ID advanced security technology
Logging a User into Your App with Face ID or Touch ID