5. handshake 协定

handshake protocol重要而繁琐。

TLS 1.3对握手做了大批改,上面先讲TLS 1.2,讲完再介绍一下剖析TLS 1.3.

5.1.handshake的总体流程

handshake protocol用于产生给record protocol应用的SecurityParameters。
在handshake中:

  • 客户端和服务器端协商TLS协定版本号和一个CipherSuite,
  • 认证对端的身份(可选,个别如https是客户端认证服务器端的身份),
  • 并且应用密钥协商算法生成共享的master secret。

步骤如下:

  • 替换Hello音讯,协商出算法,替换random值,查看session resumption.
  • 替换必要的密码学参数,来容许client和server协商出premaster secret。
  • 替换证书和密码学参数,让client和server做认证,证实本人的身份。
  • 从premaster secret和替换的random值 ,生成出master secret。
  • 把SecerityParameters提供被record层。
  • 容许client和server确认对端得出了雷同的SecurityParameters,并且握手过程的数据没有被攻击者篡改。

Handshake的后果是在单方建设雷同的Session,Session 蕴含下列字段:

  1. session identifier
    session id,用来惟一标识一个session,在session 复原的时候,也要用到
  2. peer certificate
    对端的 X509v3 格局证书. 如果不须要认证对端的身份,就为空。
  3. compression method
    压缩算法,个别被禁用
  4. cipher spec
    CipherSuite,如上文介绍,蕴含: 用于生成key的pseudorandom function (PRF) , 块加密算法例如AES, MAC算法 (例如 HMAC-SHA256). 还包含一个 mac_length字段,在后文的we握手协定介绍
  5. master secret
    48字节的,client和server共享密钥。
  6. is resumable
    一个标记位,用来标识以后session是否能被复原。

以上字段,随后被用于生成 record层的SecurityParameters,多个连贯能够通过握手协定的session复原性能来复用同一个session。

握手协定应用 非对称加密/密钥协商/数字签名 3类算法,
因而要求读者对这3类算法概念清晰,能精确辨别。
在此廓清一下,:
非对称的算法分为3类:

  • 非对称加密,有:RSAES-PKCS1-v1_5,RSAES-OAEP ,Rabin-Williams-OAEP, Rabin-Williams-PKCS1-v1_5等
  • 非对称密钥协商,有:DH,DHE,ECDH,ECDHE 等
  • 非对称数字签名:RSASSA-PKCS1-v1_5,RSASSA-PSS,ECDSA,DSA,ED25519 等

另外,非对称加密算法,能够当作密钥协商算法来用,所以 RSAES-PKCS1-v1_5,RSAES-OAEP 也能够当作密钥协商算法来用


插播一段 RSA:

RSA的理论工程利用,要遵循PKCS#1 规范,见 https://www.ietf.org/rfc/rfc3447

其中的 RSAES-PKCS1-v1_5 和 RSASSA-PKCS1-v1_5 是应用RSA算法的两种不同scheme(体制)。
RSAES示意 RSA Encryption schemes,即非对称加密,
RSAES有:RSAES-OAEP,RSAES-PKCS1-v1_5两种,其中RSAES-OAEP更新更平安

RSASSA示意 Signature schemes with appendix,即appendix模式(appendix和recovery的区别请参看密码学教材)的非对称数字签名算法。
RSASSA有: RSASSA-PSS, RSASSA-PKCS1-v1_5 两种, 其中RSASSA-PSS更新更平安

RSA还有一个缺点,就是很容易被工夫侧通道攻打,所以当初的RSA实现都要加 blinding ,后文有介绍。

能够看到,RSA是一种很非凡的算法,既能够当非对称加密算法应用,又能够当非对称数字签名应用。这一点很有迷惑性,其实很多用RSA的人都分不清本人用的是RSA的哪种模式。

相比之下,ECC(椭圆曲线)这一块的算法就很清晰,ECDSA只能用作数字签名,ECDH只能用作密钥替换。

分分明 RSAES-PKCS1-v1_5 和 RSASSA-PKCS1-v1_5 有什么用涅?

PKCS#1标准解释:

A generally good cryptographic practice is to employ a given RSA
key pair in only one scheme. This avoids the risk that
vulnerability in one scheme may compromise the security of the
other, and may be essential to maintain provable security.

FIPS PUB 186-3 美国标准规定:

An RSA key pair used for digital signatures shall only be used for one
digital signature scheme (e.g., ANS X9.31, RSASSA-PKCS1 v1.5 or
RSASSA-PSS; see Sections 5.4 and 5.5). In addition, an RSA digital
signature key pair shall not be used for other purposes (e.g., key
establishment).

一对密钥只做一个用处,要么用作非对称加解密,要么用作签名验证,别混着用!
一对密钥只做一个用处,要么用作非对称加解密,要么用作签名验证,别混着用!
一对密钥只做一个用处,要么用作非对称加解密,要么用作签名验证,别混着用!

这个要求,决定了一个协定的 PFS(前向安全性),在斯诺登曝光NSA的“今日捕捉,明日破解”政策后,越发重要

https://news.ycombinator.com/...

http://news.netcraft.com/arch...

https://lwn.net/Articles/572926/

https://www.eff.org/deeplinks...

http://www.wired.com/2013/10/...

PFS反映到密钥协商过程中,就是:

  • 不要应用RSA做密钥协商,肯定只用RSA做数字签名
  • 不要把ECDH的公钥固定内置在客户端做密钥协商

后文能够看到这一准则在 TLS 1.3, QUIC,Apple的iMessage等协定中一再贯彻。

非对称RSA/ECC这个话题比拟大了,前面有空再写文章吧,读者能够先看一下参考资料,外面有清晰的介绍。

插播完结,持续TLS。


因为设计的时候,就要思考兼容性,而且理论历史悠久,所以TLS协定90年代已经应用的一些算法,当初曾经被破解了,例如有的被发现破绽(rc4),有的密钥长度过短(例如已经美帝有进口限度,限度RSA 在512比特以下,对称加密密钥限度40比特以下,起初2005年限度被勾销),然而思考到兼容,当初的TLS实现中,还是蕴含了这种曾经被破解的老算法的代码。这样,如果攻击者能够烦扰握手过程,诱使client和server应用这种曾经被破解的算法,就会威逼TLS协定的平安,这被称为“降级攻打”。

为了在握手协定解决降级攻打的问题,TLS协定规定:client发送ClientHello音讯,server必须回复ServerHello音讯,否则就是fatal error,当成连贯失败解决。ClientHello和ServerHello音讯用于建设client和server之间的平安加强能力,ClientHello和ServerHello音讯建设如下属性:

  • Protocol Version
  • Session ID
  • Cipher Suite
  • Compression Method.

另外,产生并替换两个random值 ClientHello.random 和 ServerHello.random

密钥协商应用四条: server的Certificate,ServerKeyExchange,client的Certificate,ClientKeyExchange 。TLS规定当前如果要新增密钥协商办法,能够订制这4条音讯的数据格式,并且指定这4条音讯的应用办法。密钥协商得出的共享密钥必须足够长以后定义的密钥协商算法生成的密钥长度必须大于46字节

在hello音讯之后,server会把本人的证书在一条Certificate音讯外面发给客户端(如果须要做服务器端认证的话,例如https)。 并且,如果需要的话,server会发送一条ServerKeyExchange音讯,(例如如果服务器的证书只用做签名,不必做密钥替换,或者服务器没有证书)。client对server的认证实现后,server能够要求client发送client的证书,如果这是协商进去的CipherSuite容许的。下一步,server会发送ServerHelloDone音讯,示意握手的hello音讯局部曾经完结。而后server会期待一个client的响应。如果server曾经发过了CertificateRequest音讯,client必须发送Certificate音讯。而后发送ClientKeyExchange音讯,并且这条音讯的内容取决于ClientHello和ServerHello音讯协商的算法。如果client发送了有签名能力的证书,就显式发送一个通过数字签名的CertificateVerify音讯,来证实本人领有证书私钥。

而后,client发送一个ChangeCipherSpec音讯,并且client拷贝待定的Cipher Spec到以后的Cipher Spec。而后client立刻用新算法+新key+新密钥 发送Finished音讯。收到后,server发送本人的ChangeCipherSpec音讯,作为响应,并且拷贝待定的Cipher Spec到以后的Cipher Spec。此时,握手就实现了,client和server能够开始替换应用层数据(如下图所示)。应用层数据不得在握手实现前发送。

援用一个来自网络的图片:

Client                                               ServerClientHello                  -------->                                                ServerHello                                               Certificate*                                         ServerKeyExchange*                                        CertificateRequest*                             <--------      ServerHelloDoneCertificate*ClientKeyExchangeCertificateVerify*[ChangeCipherSpec]Finished                     -------->                                         [ChangeCipherSpec]                             <--------             FinishedApplication Data             <------->     Application Data       Figure 1.  Message flow for a full handshake* 示意可选的音讯,或者依据上下文在某些状况下会发送的音讯。Indicates optional or situation-dependent messages that are notalways sent.

注:为了帮忙解决管道阻塞的问题,ChangeCipherSpec是一个独立的TLS protocol content type,并不是一个握手音讯。

TLS的残缺握手过程,要进行RSA/ECDH/ECDSA等非对称计算,非对称计算是很慢的。对于非对称的性能:
例如在2015年的服务器cpu: Intel(R) Xeon(R) CPU E3-1230 V2 @ 3.30GHz 上,
应用如下命令测试:

openssl speed rsa2048openssl speed ecdsap256openssl speed ecdhp256openssl speed aes-128-cbcopenssl speed -evp aes-128-cbc

后果如下表:

算法性能性能
RSA-2048私钥运算 723.7 次/秒公钥运算 23505.8 次/秒
256 bit ecdsa (nistp256)签名 8628.4 次/秒验证 2217.0 次/秒
256 bit ecdh (nistp256)ECDH协商 2807.8 次/秒
aes-128-cbc加密 121531.39 K/秒
aes-128-cbc 应用aesni硬件加速加密 683682.13 K/秒

注:非对称的单位是 次/秒,这是因为非对称个别只用于解决一个block,
对称的单位是 K/秒,因为对称个别用于解决大量数据流,所以单位和流量一样。
能够给非对称的 次/秒 乘以 block size ,就能够和对称做比拟了。例如rsa-2048,723.7*2048/8/1024=185.2672 K/秒 ,
RSA-2048 私钥运算性能 是aes-128-cbc 的 1.5/1000。是aesni的 2.6/10000

如上,性能数据惨不忍睹, 几乎不能忍!!!

有鉴于此,TLS从设计之初,就采纳了万能伎俩—加cache,有2种cache伎俩:session id,和session ticket。把握手的后果间接cache起来,绕过握手运算。

当client和server决定复原一个之前的session,或复用一个已有的session时(能够不必协商一个新的SecurityParameters),音讯流程如下:

客户端应用要被复原的session,发送一个ClientHello,把Session ID蕴含在其中。server在本人的session cache中,查找客户端发来的Session ID,如果找到,sever把找到的session 状态复原到以后连贯,而后发送一个ServerHello,在ServerHello中把Session ID带回去。而后,client和server都必须ChangeCipherSpec音讯,并紧跟着发送Finished音讯。这几步实现后,client和server 开始替换应用层数据(如下图所示)。如果server在session cache中没有找到Session ID,那server就生成一个新的session ID在ServerHello里给客户端,并且client和server进行残缺的握手。

流程图如下:

Client                                                ServerClientHello                   -------->                                                 ServerHello                                          [ChangeCipherSpec]                              <--------             Finished[ChangeCipherSpec]Finished                      -------->Application Data              <------->     Application Data    Figure 2.  Message flow for an abbreviated handshake

5.2. handshake 协定外层构造

从音讯格局来看,TLS Handshake Protocol 在 TLS Record Protocol 的下层. 这个协定用于协商一个session的平安参数。 Handshake 音讯(例如ClientHello,ServerHello等) 被包装进 TLSPlaintext构造外面,传入TLS record层,依据以后session 状态做解决,而后传输。

如下:

enum {    hello_request(0), client_hello(1), server_hello(2),    certificate(11), server_key_exchange (12),    certificate_request(13), server_hello_done(14),    certificate_verify(15), client_key_exchange(16),    finished(20), (255)} HandshakeType;struct {    HandshakeType msg_type;  /* handshake type */    uint24 length;          /* bytes in message */    select (HandshakeType) {              case hello_request:       HelloRequest;      case client_hello:        ClientHello;      case server_hello:        ServerHello;      case certificate:         Certificate;              case server_key_exchange: ServerKeyExchange;      case certificate_request: CertificateRequest;      case server_hello_done:   ServerHelloDone;      case certificate_verify:  CertificateVerify;      case client_key_exchange: ClientKeyExchange;      case finished:            Finished;      case session_ticket:      NewSessionTicket; /* NEW */    } body;} Handshake;

TLS协定规定,handshake 协定的音讯必须依照规定的程序发,收到不按程序来的音讯,当成fatal error解决。也就是说,TLS协定能够当成状态机来建模编码。

上面依照音讯发送必须遵循的程序,一一解释每一条握手音讯。

handshake协定的外层字段,见这个抓包:

5.3. handshake — ClientHello,ServerHello,HelloRequest

Hello音讯有3个:ClientHello, ServerHello,HellloRequest
一一阐明:

5.3.1 Client Hello

当客户端第一次连贯到服务器时,第一条message必须发送ClientHello。
另外,rfc里规定,如果客户端和服务器反对重协商,在客户端收到服务器发来的HelloRequest后,也能够回一条ClientHello,在一条曾经建设的连贯上开始重协商。(重协商是个很少用到的个性。)

音讯构造:

struct {    uint32 gmt_unix_time;    opaque random_bytes[28];} Random;opaque SessionID<0..32>;uint8 CipherSuite[2];enum { null(0), (255) } CompressionMethod;struct {    ProtocolVersion client_version;    Random random;    SessionID session_id;    CipherSuite cipher_suites<2..2^16-2>;    CompressionMethod compression_methods<1..2^8-1>;    select (extensions_present) {               case false:            struct {};               case true:            Extension extensions<0..2^16-1>;    };} ClientHello;

Random 其中:
gmt_unix_time 是 unix epoch工夫戳。
random_bytes 是 28字节的,用密码学平安随机数生成器 生成进去的随机数。


密码学平安的随机数生成,这是个很大的话题,也是一个大陷阱,目前最好的做法就是用 /dev/urandom,或者openssl库的 RAND_bytes()

历史上,恰好就在SSL的random_bytes这个字段,NetScape浏览器晚期版本被爆出过随机数生成器破绽。
被爆菊的随机数生成器应用 pid + 工夫戳 来初始化一个seed,并用MD5(seed)得出后果。
见 http://www.cs.berkeley.edu/~d...,
倡议读者检查一下本人的随机数生成器。


client_version
: 客户端反对的最高版本号。

random
: 客户端生成的random。

ClientHello.session_id 惟一标识一个session,用来做session cache。如果为空,示意不做复用,要求服务器生成新的session。
session_id的起源有:

  1. 之前的协商好的连贯的session_id
  2. 以后连贯的session_id
  3. 以后也在应用中的另一条连贯的session_id

其中第三种容许不做从新握手,就同时建设多条独立的平安连贯。这些独立的连贯可能程序创立,也能够同时创立。一个SessionID当握手协商的Finished音讯实现后,就非法可用了。存活直到太旧被移除,或者session 关联的某个连贯产生fatal error。SessionID的内容由服务器端生成。

注:因为SessionID的传输是不加密,不做MAC爱护的,服务器不容许把私密信息发在外面,不能允许伪造的SessionID在服务器造成平安问题。(握手过程中的数据,整体是受Finished音讯的爱护的)

ClientHello.cipher_suites字段,蕴含了客户端反对的CipherSuite的列表,依照客户端心愿的优先级排序,每个CipherSuite有2个字节,每个CipherSuite由:一个密钥替换算法,一个大量数据加密算法(须要制订key length参数),一个MAC算法,一个PRF 形成。服务器会从客户端发过来的列表中抉择一个;如果没有能够承受的抉择,就返回一个 handshake failure 的 alert,并敞开连贯。如果列表蕴含服务器不意识,不反对,或者禁用的CipherSuite,服务器必须疏忽。
如果SessionID不为空,则cipher_suites外面起码要蕴含客户端cache的session外面的那个CipherSuite

compression_methods,相似地,ClientHello外面蕴含压缩算法的列表,依照客户端优先级排序。当然,如前介绍,服务器个别禁用TLS的压缩。

compression_methods 前面能够跟一组扩大(extensions), extensions都是可选的,比拟有用的扩大如: SNI, session ticket,ALPN,OCSP 等,后文介绍。

客户端发送了ClientHello后,服务器端必须回复ServerHello音讯,回复其余音讯都会导致 fatal error 敞开连贯。

5.3.2 Server Hello

当收到客户端发来的ClientHello后,失常解决完后,服务器必须回复ServerHello。

音讯构造:

struct {    ProtocolVersion server_version;    Random random;    SessionID session_id;    CipherSuite cipher_suite;    CompressionMethod compression_method;    select (extensions_present) {        case false:            struct {};        case true:            Extension extensions<0..2^16-1>;    };} ServerHello;

server_version
: 服务器抉择 ClientHello.client_version 和 服务器反对的版本号 中的最小的。

random
: 服务器生成的random,必须确保和客户端生成的random没有关联。

session_id
: 服务器为本连贯调配的SessionID。如果ClientHello.session_id不为空,服务器会在本人的本地做查找。

  • 如果找到了匹配,并且服务器决定复用找到的session建设连贯,服务器应该把ClientHello.session_id同样的 session id填入ServerHello.session_id,这示意复原了一个session,并且单方会立刻发送Finished音讯。
  • 否则,回复一个和ClientHello.random_id不同的Serverhello.session_id,来标识新session。服务器能够回复一个空的session_id,来通知客户端这个session不要cache,不能复原。
    如果一个session 被复原了,那必须复原成之前协商的session外面的 CipherSuite。要留神的是,并不要求服务器肯定要复原session, 服务器能够不做复原。

在实践中,session cache在服务器端要求key-value模式的存储,如果tls服务器不止一台的话,就有一个存储怎么共享的问题,要么存储同步到所有TLS服务器的内存里,要么专门搞服务来反对存储,并应用rpc拜访,
无论如何,都是很麻烦的事件,相比之下,后文要介绍的session ticket就简略多了,所以个别优先应用session ticket。

cipher_suite
: 服务器选定的一个CipherSuite。如果是复原的session,那就是session里的CipherSuite。

compression_method
: 跟下面相似。

extensions
: 扩大列表。要留神的是,ServerHello.extensions 必须是 ClientHello.extensions的子集。

5.3.3 Hello Extensions

The extension 的格局是:

struct {    ExtensionType extension_type;    opaque extension_data<0..2^16-1>;} Extension;enum {    signature_algorithms(13), (65535)} ExtensionType;

其中:

  • “extension_type” 标识是哪一个扩大类型。
  • “extension_data” 一坨二进制的buffer,扩大的数据体,各个扩大本人做解析。

extension_type 只能呈现一次,ExtensionType之间不指定程序。

extensions 可能在新连贯创立时被发送,也可能在要求session复原的时候被发送。所以各个extension都须要规定本人再残缺握手和session复原状况下的行为。
这些状况比拟琐碎而奥妙,具体案例要具体分析。

5.3.4 Hello Request

服务器任何时候都能够发送 HelloRequest 音讯。

HelloRequest的意思是,客户端应该开始协商过程。客户端应该在不便的时候发送ClientHello。服务器不应该在客户端刚创立好连贯后,就发送HelloRequest,此时应该让客户端发送ClientHello。

客户端收到这个音讯后,能够间接疏忽这条音讯。
服务器发现客户端没有响应HelloRequest后,能够发送fatal error alert。

音讯构造:

struct { } HelloRequest;

HelloRequest不蕴含在握手音讯的hash计算范畴内。