前言
文章开始之前,我们先来看几个图片
还记得最早期的手机,蓝牙配对需要输入四个数字的pincode了吧?
为啥后来配对的图片变成这种了呢?
这背后的技术或者说标准到底经历了什么?
这篇文章希望能这个问题说清楚,同时既然说到加密算法,我们也可以把蓝牙世界和HTTPS世界做一个对比,会发现两者之间有很多的共同点,毕竟,都是通信范畴的东西。
概念扫盲
密码学体系是近几十年已经成熟起来的体系,我们这里不去展开论述,但是一些关键的信息还是我们必须了解的。
在加密领域,我们首先要了解的概念是:
共享密钥加密(对称密钥加密):加密和解密同用一个密钥。加密时就必须将密钥传送给对方,那么如何安全的传输呢?这个是非对称加密算法所要解决的事情。
有同学要说,为什么要多此一举,直接用非对称加密来加密数据不香吗?很简单,非对称加密速度慢嘛。所以实战中一般都是需要对称加密和非对称加密这两个兄弟打一个配合。
对于搞蓝牙开发的来说,这个对称加密密钥至关重要,叫做linkkey,在点对点通信中,双方第一次建立连接之后就确定下来的,需要存储到flash,这样下次连接就很快了,如果手机端删除配对,那么就需要重新配对并协商这个linkkey。
我们通过sniffer抓包,数据的payload一般来说是加密的,看不懂的,需要知道这个linkkey,才能把所有的数据都解密出来。
我们经常听到蓝牙的“配对”一词,所谓配对,就是蓝牙主从设备通过协商得到彼此加密通信的唯一对称加密密码Linkkey的过程。
公开密钥加密(非对称密钥加密):公开密钥加密使用一对非对称的密钥。一把叫做私有密钥,一把叫做公开密钥。私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。使用此加密方式,发送密文的一方使用公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听盗走。
但由于公开密钥比共享密钥要慢,所以我们就需要综合一下他们两者的优缺点,使他们共同使用,而这也是HTTPS采用的加密方式。在交换密钥阶段使用公开密钥加密方式,之后建立通信交换报文阶段则使用共享密钥加密方式。
这里就有一个问题,如何证明公开密钥本省是货真价实的公开密钥。如,正准备和某台服务器建立公开密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许在公开密钥传输过程中,真正的公开密钥已经被攻击者替换掉了。
为了解决这个问题,在网路环境下,可以使用由数字证书认证机构(CA,Certificate Authority)和其他相关机关颁发的公开密钥证书。
接收到证书的客户端可以使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行验证,一旦验证通过,客户端便可以明确两件事:
一、认证服务器的公开密钥的是真实有效的数字证书认证机构。
二、服务器的公开密钥是值得信赖的。
而在蓝牙的场景中,因为不是网路环境,所以认证就是用户自己来做了,有几种实现的方式,在蓝牙里,认证这件事情叫做authentication,又叫鉴权。
随机数发生器:
蓝牙的随机数发生器一般是软件实现的伪随机数发生器,随机数的使用场景是很多的,初始化阶段的In_Randa,认证阶段的AU_Randa、linkkey生成阶段的随机数等等都需要。
HTTPS加密流程:
首先服务器端用非对称加密产生公钥和私钥。然后把公钥发给客 户端,路径或许有人会截取,但是没有用,因为用公钥加密的文件只有私钥可以解密,而私钥永远都不会离开服务器的。当公钥到达客户端之后,客户端会用对称加密产生一个秘钥并且用公钥来加密发送给服务器端,这个秘钥就是以后用来通信的钥匙。这样服务器端收到公钥加密的秘钥时就可以用私钥来解公钥从而获得秘钥。这样的话客户端和服务器端都获得了秘钥,信息交流相对是安全的。
TLS1.3把2RTT减少为1RTT,但是基本的数据信息流程不会变,我们就参考上图好了,这个图应该是TLS1.2的。
图中client hello和server hello都是明文传输的,后面其实隐含了一步server certification没画出来,这一步是把ca证书发给client,client用ca的公钥解密之后校验,如果校验通过则证明身份没有问题,进行后续key exchange。
change cipher spec这一步,client此时已经有两个随机数了,但是这两个随机数是明文的,不安全,此时再生成一个随机数Pre-Master,而且这个pre-master的前两个字节有tls的版本号在里面,可以进一步限制中间人采用低版本去攻击,server是会校验这两个字节的,和之前hello阶段的版本号做一个对比。
三个随机数随机程度已经可以了,生成对称加密的密码:
enc_key=Fuc(random_C, random_S, Pre-Master)
这个enc_key就类似于蓝牙的linkkey。
把pre-master通过之前的证书publikkey加密后发送给server,server用自己的privatekey解密之后就得到了pre-master, server也可以计算得到enc-key了。
随后的encrypted handshake message都是用enc-key加密的报文了。
蓝牙加密的前世今生
BR/EDR
- Prior to version 2.1 => BR/EDR legacy
- Version 2.1 => BR/EDR (uses Secure Simple Pairing)
- Version 4.2 => BR/EDR (uses Secure Connections)
LE
- Version 4.0 and 4.1 => LE legacy (uses Secure Simple Pairing)
- Version 4.2 => LE (uses Secure Connections)
蓝牙4.0版本开始支持BLE,BLE的secure connection并非新技术,在BR/EDR中就早已经提出。
SC最先提出在BR/EDR中,追溯其起源可以到Core 2.1,在Core 2.1之前,加密认证方式所有的安全均建立在0-16位的数字上,存在被破解的风险,于是人们提出了SSP(Secure Simple Pairing),SSP是一种在传统认证基础上改进过来的认证加密方式
SSP的改变主要在于认证阶段,采用椭圆曲线非对称加密方式,交换公钥、存留私钥、公钥结合私钥计算共享密钥的方式进行认证,最终认证通过后由共享密钥生成连接密钥(Link Key)
可以发现在认证加密过程中密钥的生成存在随机性,并且用户都不会直接接触到密钥,大大的提高了其安全系数,并且椭圆曲线非对称加密算法其加密程度也非常高,在此基础上,配对的安全系数提高了加密过程依然采用传统的E0算法,直到在Core 4.1中将BLE的AES-CCM加密算法引入过来,将SSP认证与AES-CCM加密结合,提出了SC加密认证机制。
上述为BR/EDR中SC的发展过程,对BLE来说,也分成legacy和secure两种连接方式,但是这里的legacy类似于下面的secure simple paring,在core4.2以下,虽然配对过程也是secure simple paring,但是没有采用ecdh加密,所以如果攻击者能够捕获LE配对帧,有可能确定产生的LTK。
从Core 4.2开始,BLE直接将BR/EDR的SC安全机制借鉴过来,提出LE Secure Connections,新提出的安全机制对于BLE来讲,改变的仅仅是配对阶段,加密阶段原本BLE就是使用的AES-CCM。
上图可以看到各个版本的加密方式的演进过程,早期的对称加密基本都是使用E0的序列加密,128位密钥长度的E0序列加密在某些情况下可通过不是很庞杂的方法破解,4.1以后采用AES的块加密,算法更为强壮。
Legacy配对的问题
有了上一章各版本的大致演进过程后,我们可以看一下早期版本的配对有多么的危险。
对于PIN/传统配对,当用户在一个或两个设备上输入完全相同的机密的PIN码时,根据配置和设备类型,两个蓝牙设备同时获得链接密钥。下图从概念上描述了PIN码输入和密钥获得过程。注意,如果PIN码小于16字节,会用发起设备的地址(BD_ADDR)来补足PIN值以生成初始密钥。那些Ex框代表了蓝牙链接密钥获得过程所使用的加密算法。
完成链接密钥生成后,设备通过互相认证彼此来完成配对,以验证他们拥有相同的链接密钥。
在上图中,我们只要掌握了PINCODE,剩下的所有数据就都可以解密了,而蓝牙配对中使用的PIN码可以是1到16个字节的二进制数或更常见的字母数字字符,大多数只有4个字节,也就是文章开始那个图对应的情况。对于低风险应用场景,典型的4位数PIN码可能是足够的;对于要求更高安全级别的设备,应当使用更长的PIN码(例如, 8个字符的字母数字)。
关于上图中出现的若干种Key:
在蓝牙spec的Security Speccification这一章的Key management这一节里,定义了若干个很容易混淆的key:
初始密钥Kinit,组合秘钥(Combination Key)Kab,设备密钥(Unit Key)Ka,临时密钥Kmaster,加密密钥Kc。
Kinit是初始化期间用作连接密钥,由蓝牙地址,Pincode,随机数(IN_RAND)根据E22算法生成,长度是128bit。如果两个设备未曾联系过,他们将使用Kinit作为密钥。在双方连接密钥交换完成后Kinit被废弃。
Ka又叫Unit Key,基本上在设备生成后保持不变,存放在非易失memory。
Kab需要主从设备建立连接后,互相交换信息(分别提供一个随机数和自己的蓝牙地址)后生成(E21算法),如下图所示,图中的K指的是link key,这个linkkey是广义上的,不是我们常说的建立完连接后的那个linkkey,在init阶段,这个linkkey就是Kinit,而在下图这个过程之后,原来的link key就可以丢弃了,就变成了Kab。
Kab和Ka功能上没有区别,只是使用场景不同,在要求更高安全的场景,使用Kab,同时这会要求设备有更多的储存空间去储存Key。
Kmaster适用于一对多的通信场景,也就是广播。
Kc由当前linkkey,COF(如果是点对点通信的话,和之前认证过程中产生的认证加密偏移量AOC相关,广播的话和蓝牙地址相关),和128bit的随机数通过E3算法生成,这个也就是通常意义上的link key了
Secure Simple Pair
Secure Simple Pairing(SSP)通过提供一些关联模型来简化配对过程。这些模型具有适应不同设备输入/输出能力的灵活性。SSP也通过增加ECDH公钥加密来改进安全性,以防止配对过程中的被动窃听和中间人(MITM)攻击。
所以说SSP最大的进步,在于使用了非对称加密来传递Linkey需要的相关参数。
接下来我们来看一下具体流程:
通信前,总得让对端知道自己是个什么样的设备,上图中的Step 5——IO capabilities exchange就高速了对方自己具有怎么的人机交互能力,可以理解为HTTPS的那张图1中的“hello”阶段,双方交换的那些信息,当然有所不同,https需要交换的是版本、算法库等等信息,而我们的IO capability主要是为后面的认证(鉴权)服务的,下文会提到。
接下来稍稍有点不同,在HTTPS那张图里面,紧接着应该是认证了,而蓝牙的认证是在Step7,在蓝牙的场景里public key exchange被提前了,不像HTTPS里面,change cipher spec是在认证之后做的。我的一种猜测是,因为非对称加密的计算耗时比较大,蓝牙设备普遍存在配对慢的问题,所以这部分工作能提前尽量提前。
我们先来说Public Key Exchange,和Https有点小不同。Https的流程如下图:
服务方向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证,CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等。
如信息审核通过,CA会向申请者签发认证文件-证书。证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名,该签名使用散列算法根据以上的明文计算出, 然后使用私钥对该sign值加密,加密后,客户端可以通过内置的CA机构公钥验证该sign值,而该sign值只能是CA机构的私钥生成的,才能通过客户端的验证,由此该证书不可能被其他的中间人伪造或更改数据。
所以,HTTPS的关键点client预留了CA证书,client可以用非对称加密算法验证server发过来的证书并得到server的public key,然后继续用非对称加密算法把自己的public key共享给server端。
而蓝牙没有中间商CA的参与,又是如何做到交换key的呢?
这基于以下的数学理论:
用于创建SSP密钥对的精心选择的数学空间和算法使得:
- 非常困难(即不可能使用当前最先进的计算机)使用公钥计算私钥(但是很容易根据私钥计算公钥)
- 给定两个SSP密钥对A和B,存在一个众所周知的函数F,使得F(PublicA,PrivateB)= F(PublicB,PrivateA)。这个函数的结果是DHKey。只有拥有A和B的两个设备能够计算出相同的DHKey。
这是SSP的神奇之处:两台设备将能够进行配对,而无需通过空中传输任何关键信息,且无需通过带外机制(例如键入它)在键盘上共享该信息)。DHKey将用作计算链路密钥(Link Key)的种子。配对过程的其余部分与LMP配对相似。
在Step6中,蓝牙所使用的DH算法就是ECDH椭圆曲线算法,蓝牙所选择的曲线是P-192或者P-256,椭圆曲线算法无论是密钥生成还是签名,都比RSA算法快百倍以上。
但是在嵌入式系统的芯片上,这个计算还是有点慢的。所以除了上述流程上提前之外,有的蓝牙芯片还内置了asic模块去做椭圆曲线算法,这个和https硬件加速器是相似的目的。
说完非对称加密完成的密钥交换,我们再来说下身份认证。
为了防止中间人攻击和抵赖,身份认证,HTTPS的身份认证的流程如下图:
蓝牙的身份认证和HTTPS有很大不同,主要区别就是https是通过CA颁发证书,由第三方的权威机构CA去认证身份的合法性,而蓝牙不是互联网结构,只能通过设备双方去认证彼此。
鉴于此,蓝牙的认证根据版本不同,分成以下两种情况。
身份认证
一是legacy authentication,主要是蓝牙2.1版本之前,见蓝牙spec的Security Speccification这一章的Authentication这一节。
蓝牙设备认证过程是质询-响应方案的形式。认证过程中互动的每个设备被称为申请者(Claimant)或验证者(Verifier)。申请者是试图证明自己身份的设备,而验证者是验证申请者身份的设备。质询-响应协议通过校验一个机密的密钥——蓝牙链路密钥来验证设备。下图从概念上展示了质询-响应验证方案。
认证过程中的步骤如下图:
步骤 1. 验证者发送一个128位的随机质询(AU_RAND)给申请者
步骤 2. 申请者使用E1算法,以他或她的唯一的48位蓝牙设备地址、链接密钥和AU_RAND为输入,计算出一个认证响应。验证者执行同样的计算。只有E1输出的高32位才被用于认证的目的。128位输出中剩余的96位作为认证加密偏移(ACO)值,将被稍后用作输入以创建蓝牙加密密钥。
步骤 3. 申请者返回E1输出的高32位作为计算的响应——已签名的响应(SRES)——给验证者。
步骤 4. 验证者比较来自申请者的SRES和自己计算的值。
步骤 5. 如果两个32位值相等,认证被认为是成功的。如果两个32位值不相等,认证失败。
把这些步骤执行一遍就完成了一个单向认证。蓝牙标准允许执行单向和相互认证。用于相互认证时,验证者和申请者交换角色重复上述过程。
如果认证失败,蓝牙设备会等待一定时间间隔再做新的尝试。这个时间间隔呈指数级增长,以防止攻击者采用不同的链接密钥以试错法来击破认证方案。重要的是,要注意该技术不提供针对离线攻击的安全保证。这种攻击利用窃听配对帧和穷尽猜测PIN码来确定链接密钥。
注意,与认证相关的安全机制是完全基于链接密钥的保密性。虽然蓝牙设备地址和随机质询值被视为公开参数,但链路密钥不是。链路密钥是在配对过程中生成出来的,不应该在蓝牙设备之外暴露出来或通过无线链路传输。但是,链路密钥从主机到控制器(例如,PC到USB适配器)是明文传输,而当主机被用于密钥存储时情况相反。质询值是与认证过程相关的公开参数,对每次交互处理过程必须是随机且唯一的。质询值由蓝牙控制器中的伪随机数发生器生成。
如果双方都支持Secure Connection(BT4.1+)则 ,则使用Secure Authentication。
整个鉴权流程与legacy Authentication相似,不同之处在于使用了H4,H5算法,并且会进行相互认证。当配对过程使用了P-256 Elliptic Curve ,则鉴权过程应该使用Secure Authentication。
鉴权使用哪种方式根据之前协商得到的设备的IO capability而定,OOB是优先级最高的。
- Out of Band:两设备的通过别的途径交换配对信息,例如NFC等。例如一些NFC蓝牙音箱。
- Numeric Comparison:配对双方都显示一个6位的数字,由用户来核对数字是否一致,一致即可配对。例如手机之间的配对。
- Passkey Entry:要求配对目标输入一个在本地设备上显示的6位数字,输入正确即可配对。例如连接蓝牙键盘。
- Just Works:用于配对没有显示没有输入的设备,主动发起连接即可配对,用户看不到配对过程。例如连接蓝牙耳机。
上述2、3这种需要人参与的鉴权方式,在蓝牙协议里面称作MITM(man-in-the-middle)authentication(不是MITM attack 哦)
比如说下图是Numeric Comparison
下图则是passkey的情况
这四种鉴权方式种只有Just work方式是不安全的,JUSTWORK由于不涉及人机交互,所以没法防止MITM的中间人攻击。
认证阶段最主要用到的算法是摘要算法,蓝牙主要使用的摘要算法是HMAC-SHA256,在认证阶段多处都会用到。
结论
1. 蓝牙进化2.1以后,使用secure simple pairing方式后,和HTTPS的加密流程大致相同,都是通过非对称加密去交换key,生成后续的对称加密密钥。
2. 蓝牙和https在加密上最大的区别是认证方式,根源还是在于蓝牙是点对点通信,没有第三方参与,所以认证主要是靠两个设备自行通过IO交互能力,由人去加以判断是否接受连接。
3. 蓝牙和https在加密流程上的小区别是,蓝牙的交换key这个步骤提得比较靠前,目的应该是因为非对称加密(P192、P256ECDH)比较慢,为了用户体验好,所以尽量早一点去进行计算。有一些蓝牙芯片甚至内部集成asic算法模块,目的也是大致相同的。
4. 蓝牙Legacy连接方式,以及secure simple paring方式中的Just work鉴权方式,由于无法防止中间人攻击,所以安全性得不到保证。