最近学了些HTTP相关的内容,做了些笔记,深入了解TLS后感觉密码学挺有意思。
HTTP
HTTP/0.9
- 这是后来回来定义的版本,这个初始版本采用纯文本格式
- 只有GET动作,在响应请求后立即关闭连接,功能非常有限
HTTP/1.0
- 1996年发布,与1.1差不了多少了
- 增加了
HEAD
、POST
等方法 - 增加响应状态码,标记可能的错误原因
- 引入协议版本号概念
- 引入了
Header
头部的概念 - 传输的数据不再仅限于文本
- 增加了
- 但是1.0并不是一个标准,只相当于一个备忘录(参考文档)
HTTP/1.1
- 1999年,发布RFC文档编号2616,是个正式的标准
- 增加了
PUT
、DELETE
等方法 - 增加了缓存管理和控制
- 明确了连接管理,允许持久连接
- 允许响应数据分块(
chunked
),利于传输大文件 - 强制要求
Host
头,让互联网主机托管成为可能
- 增加了
HTTP请求报文格式
- 请求方法 | URL | 协议版本
Header
头部(一行一个字段,key-value,中间冒号分隔)- 空行
- 请求包体(GET没有,POST用)(GET也可以填body,服务端不一定处理)
HTTP响应报文格式
- 协议版本 | 状态码 | 状态码描述
Header
头部(一行一个字段,key-value,中间冒号分隔)- 空行
- 响应包体
常见的Header
- Accept-Charset: 数据编码格式
- Content-Type: 数据的类型
- Cache-control:max-age,客户端的话就是希望多新的缓存,服务端的话就是告诉客户端某个时间段内的数据都是最新的
- If-Modified-Since:如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最最新的资源;如果没有更新,服务端会返回304 Not Modified的响应,那客户端就不用下载了
- Retry-After:告诉客户端应该在多长时间以后再次尝试一下
- Connection:一般是keep-alive,用来保活TCP连接,close的话就是传输完后就关闭TCP连接
- Referer:客户端告诉服务端我是从哪个链接来的,服务端收到后可以做处理,重定向,或者用于支付后的回调(微信支付)
- User-Agent:老生常谈,操作系统和浏览器名称版本,很多骚操作
一些特点
- 一般POST时,先发送header,服务器响应 100 Continue 后,浏览器再发送Data,服务器响应 200 OK,可能分两个TCP包
- header是key-value,中间冒号分隔
- HTTP即超文本传输协议 HyperTextTransferProtocol, 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范
- HTTP没有实体,不是互联网,不是语言,不是HTML,不是孤立的协议。
- HTTP是基于TCP的,TCP的重传HTTP不知道
- 由于有Keep-Alive机制,每次HTTP连接无需TCP重新握手
HTTP/2.0
- HTTP2.0通过头压缩、分帧、多路复用等技术提升性能
- SPDY是谷歌推出的,HTTP2.0基于此制定
- HTTP2.0对header压缩,用索引表替代key-value,将一个TCP连接中切分多个流。
- 每个流有自己ID,流是双向的虚拟通道,流有优先级。2.0还将所有信息分割为帧。
- 有header帧和data帧,用二进制编码,多个data帧属于同一个流。
- 这样可以将多个请求分到不同的流中,将请求内容拆成帧,进行二进制传输。
- 帧可以打散乱序发送,然后根据每个帧首部的流标识符重新组装,并根据优先级决定先处理哪个流。
- HTTP2.0其实是将n个请求变成n个流,将数据分成帧,乱序发送到一个TCP连接中。
- 这样2.0解决了1.1的队首阻塞问题。1.1通过pipeline用多条TCP实现并发,2.0只用1个TCP实现并发,提高性能。
- 但HTTP2.0还是基于TCP的,TCP处理包时有严格顺序,前面流2的帧没收到,后面流1的帧也会阻塞
QUIC
- QUIC是谷歌基于UDP改进的应用层,未来HTTP3.0可能基于此实现,解决HTTP2.0的问题,QUIC也是面向连接的。
- QUIC自定义类似TCP的连接、重试、多路复用、流量控制技术,进一步提升性能
机制1:自定义连接
- 一条TCP连接是四元组(源IP和端口,目标IP和端口)标识的,一旦一个元素变化就需要断开重连
- 移动互联经常不稳定、切换,都会导致重连,又三次握手,时延高
- 而基于UDP的QUIC自己用64位随机数作为标识替代四元组,而且UDP是无连接的
- 当前述的四个元素变化时,不用重新建立连接
机制2:自定义重传机制
- TCP有通过采样往返时间RTT的自适应重传算法,但是这种采样不准确。
- QUIC也有递增序列号,任何一个包重发序列号都+1,不像TCP重发包还是原来的序号。
- 这样超时采样就准了,但是如何知道重发的包内容与前一个一样呢
- QUIC在数据流里定义了
offset
,可以通过offset
查看数据发送到了哪里 - 只要某个
offset
没到就重发,按照offset
拼接成一个流,相当于多设一个参数处理流的顺序。
机制3:无阻塞的多路复用
- 跟2.0一样,同一条QUIC上可以创建多个流来发送HTTP请求
- 但因为基于UDP,一个连接上的多个流之间没有依赖
- 假如流2丢了一个UDP包,后面跟着流3的一个UDP包,虽然流2的包需要重传,但流3的包无需等待,而TCP就会阻塞
机制4:自定义流量控制
- TCP的流量控制是通过滑动窗口协议,QUIC也是通过window_update来告诉对方自己可接受的字节数
- 但QUIC的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,每个流也有控制窗口
- TCP里接收端的窗口起始点是下一个要接收并且ACK的包,即便后来的包都到了放在缓存里,窗口也不能右移
- 只要前面的包没到,后面的到了也不能ACK,就会导致后面的到了也可能超时重传,浪费带宽。
- QUIC的ACK是基于offset的,每个offset的包到了进了缓存就可以应答,应答后就不用重发,再继续等中间的包即可
- QUIC窗口的其实位置为当前收到的最大offset,从这个offset到当前流所能容纳的最大缓存才是真正的窗口
- 另外还有整个连接的窗口,需要对所有流的窗口做一个统计
关于HTTP的队首阻塞
HTTP/1.0对于同一个tcp连接,所有的请求放入队列中,只有前一个请求的响应收到了,然后才能发送下一个请求。可见,HTTP/1.0的队首组塞发生在客户端。
HTTP/1.1对于同一个tcp连接,HTTP/1.1允许一次发送多个请求,也就是说,不必等前一个响应收到,就可以发送下一个请求,这样就解决了HTTP/1.0的客户端的队首阻塞。但服务端响应的发送要根据请求的顺序排队发送,如果前一个请求的处理时间长就会影响后面的响应发送。可见,HTTP/1.1的队首阻塞发生在服务器端。
HTTP/2.0无论在客户端还是在服务器端都不需要排队,在同一个TCP连接上,有多个stream,由各个stream发送和接收请求,各个steam相互独立,互不阻塞。只要TCO没有人在用那么就可以发送已经生成的requst或者reponse的数据,在两端都不用等,从而彻底解决了HTTP协议层面的队首阻塞问题。
HTTPS
建立连接过程
- 开始连接时,客户端用服务端的公钥加密发送,服务端用客户端的公钥加密回应
- CA证书里有公钥,CA是递归的最后有几个大的rootCA,先用非对称加密获取密钥,然后用对称加密传输数据,TLS/SSL
- HTTPS握手过程:(c是客户端,s是服务端)
- c发送
Client Hello
给s,明文传输TLS版本、加密套件候选列表、压缩算法候选列表等以及一个随机数A - s返回
Server Hello
给c,声明选择使用的协议版本、加密套件、压缩算法等以及一个随机数B - s发送
Server Certificate
给c,然后再发送 Server Hello Done,说明发送信息就这么多了,结束 - c校验前一步s给的证书,从自己信任的CA仓库中拿CA证书的公钥取解密s的证书,过程中可能需要往上追溯CA、CA的CA等
- c发送
Client Key Exchange
,内含生成的一个随机数C:Pre-master
,用s证书的公钥加密,发送给s,s可以通过自己的私钥解密出来(Pre-master
是RSA加密的说法,DH加密需要DH exponent
) - 现在,c和s都有了三个随机数,分别是自己的、对方的、Pre-master,通过这三个随机数,c和s能生成相同的对称密钥
- c发送
Change Cipher Spec
通知s采用协商的通信密钥和加密算法进行对称加密通信 - c发送
Encrypted Handshake Message
将先前商定好的参数采用协商密钥加密,发送给s用于数据与握手验证 - s发送
Change Cipher Spec
回应c采用协商的通信密钥和加密算法进行对称加密通信 - s发送
Encrypted Handshake Message
将先前商定好的参数采用协商密钥加密,发送给c用于数据与握手验证 - 双方转而使用对称加密通信,握手完成,除了以上过程,其余过程与HTTP一样
- c发送
- 上面是HTTPS单向验证,只是c验证了s的证书,是大部分的场景,也可以在更加严格安全要求的情况下,启用双向验证,互验证书
- 重放与篡改攻击:自定义的:通过Timestamp和Nonce随机数联合起来做不可逆签名来保证。
- 为什么不直接用非对称加密通信?
- 非对称加密的加密的内容不能超过公钥长度,所以只能用来作密钥交换或者内容签名
- 性能问题,对称加密是毫秒级,非对称是秒级,
HTTPS
握手过程的90%
时间都在解密RSA
- 除了
Premaster
外还需要两个随机数的原因是TLS
不信任每个客户端都能产生安全的随机数,所以引入多两个随机数
SSL/TLS
- TLS即SSL升级版,18年推出TLS1.3,目前(19年)用TLS1.2和1.3的都不少,SSL早就废弃了
- 对称加密算法有:
AES
、DES
、3DES
,非对称加密算法有:RSA
、ECDHE
- TLS中上面将两个随机数和Premaster计算成对称加密主密钥的算法是PRF算法(SHA256)
- TLS是在传输层(TCPUDP层)之上,应用层(HTTP)之下的层,HTTPS其实就是在TCP和HTTP之间加了TLS层
- 握手的加密套件:例子:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
,ECDHE
密钥交换、RSA
身份验证、AES
算法、128
加密强度、GCM
加密模式、SHA256
是MAC或者PRF - 目前主流HTTPS的流量是使用
ECDHE
进行密钥交换、用RSA
进行身份验证
TLS协议原理
自顶向下、分层抽象,TLS大致在四层
- 最底层是基础算法原语的实现如
AES
、RSA
、MD5
、SHA256
、ECDH
等 - 第二层是选定参数后,符合密码学里标准分类的算法,包括块加密算法,签名算法,非对称加密算法,MAC算法等,例如:
aes-128-cbc-pkcs7
,rsaes-oaep
,rsassa-pkcs1-v1_5
,hmac-sha256
,ecdsa-p256
,curve25519
等 - 第三层是把多种标准算法组合而成的半成品组件,例如:对称传输组件例如
aes-128-cbc + hmac-sha256,aes-128-gcm
,认证密钥协商算法:rsassa-OAEP + ecdh-secp256r1
,数字信封:rsaes-oaep + aes-cbc-128 + hmac-sha256
,文件密码加密存储组件:pbkdf2+aes-128-cbc-hmac-sha256
,密钥扩展算法PRF-sha256
等 - 第四层是用各种组件拼装而成的各种成品密码学协议/软件,例如:TLS协议,SSH协议,SRP协议,gnupg文件格式,iMessage协议,bitcoin协议等等
- 最底层是基础算法原语的实现如
很多程序员自己造的轮子,往往说白了就是想重复实现第3层的某个组件而已。
TLS加密套件 (CipherSuite)
从上述分层的角度看,
TLS
大致是由三个组件拼成的:- 对称加密传输组件,例如
aes-128-gcm
; - 认证密钥协商组件,例如
rsa-ecdhe
; - 密钥扩展组件,例如
TLS-PRF-sha256
- 对称加密传输组件,例如
这些组件可以再拆分为5类算法,在
TLS
中,这5类算法组合在一起,称为一个CipherSuite
:- authentication (认证算法)
- encryption (加密算法 )
- message authentication code (消息认证码算法 简称MAC)
- key exchange (密钥交换算法)
- key derivation function (密钥衍生算法)
TLS协议设计之初就考虑到了这每一类算法的演变,所以没有定死算法,而是设计了一个算法协商过程,来允许加入新的算法( 简直是软件可扩展性设计的典范!),协商出的一个算法组合即一个
CipherSuite
常见加密套件
加密算法分类
上述第二层中的密码学算法常见有下面几类
- 块加密算法:
AES
(AES_128_GCM等)、Serpent
等 - 流加密算法:
RC4
、ChaCha20
等 - 加密用哈希函数:
MD5
、SHA1
、SHA256
、POLY1305
等 - 消息验证码函数:
AEAD
、HMAC-SHA256
等 - 密钥交换算法:
RSA
、DH
、ECDH
、ECDHE
- 公钥加密算法:
RSA
- 数字签名算法:
RSA
、DSA
、ECDSA
- 块加密算法:
设计一个加密通信协议的过程,就是自顶向下,逐步细化,挑选各类组件,拼装成完整协议的过程
AES加密算法
- 每个加密数据块大小固定为128位(16个字节),最后一块不满16字节的话需要用填充算法补齐
- 最终生成的加密密钥长度有128位、192位、256位三种
- 多种工作模式:
ECB
、CBC
、CFB
等,这块内容很多,后续补充
DES、3DES加密算法
DES:
- 每个加密数据块大小固定为64位(8个字节)
- 生成的密钥长度为64位(其中8位用于校验(每个字节的第8位)),所以AES比DES安全
3DES:
- 还是64位的加密数据块大小
- 简单粗暴的
Triple DES
,使用3个DES
密钥(所以共192位,其中24位校验),对数据块(还是64位的小块)应用三次DES
算法进行三次加密 - 使用的3个密钥不是合并成1个密钥,而还是分成3个用,
3DES
加密时依次使用密钥1、密钥2、密钥3对明文数据块进行加密,解密过程反之亦然。 - 每次加解密原理与
DES
一样
RSA加密算法
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA
算法。从那时直到现在,RSA
算法一直是最广为使用的“非对称加密算法”。
公钥与私钥的产生
- 假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息。
- 她可以用以下的方式来产生一个公钥和一个私钥:
- 随意选择两个大的质数p和q,p不等于q,计算N=pq。
- 根据欧拉函数,求得r =φ(N)=φ(p)φ(q)= (p-1)(q-1)
- 选择一个小于 r 的整数 e,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)
- 将 p 和 q 的记录销毁。
- (N,e)是公钥,(N,d)是私钥。Alice将她的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。
加密消息
- 假设Bob想给Alice送一个消息m,他知道Alice产生的N和e。
- 他使用起先与Alice约好的格式将m转换为一个小于N,且与N互质的整数n
- 比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。
- 假如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。
- 用下面这个公式他可以将n加密为c:
- n^e ≡ c (mod N)
- 计算c并不复杂。Bob算出c后就可以将它传递给Alice。
解密消息
- Alice得到Bob的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n:
- c^d ≡ n (mod N)
- 得到n后,她可以将原来的信息m重新复原。
- 解码的原理是 c^d ≡ n^(e·d)(mod N)
- 已知e·d ≡ 1 (mod r),即e·d =1 +hφ(N)。由欧拉定理可得:
- n ^(e·d) =n^ (1 +hφ(N))=n·((n^φ(N))^h)≡(n(1)^h)(modN) ≡ n (mod N)
签名消息
- RSA也可以用来为一个消息署名。
- 假如Alice想给Bob传递一个署名的消息的话,那么她可以为她的消息计算一个散列值(Message digest)
- 然后用她的私钥加密这个散列值并将这个“署名”加在消息的后面。
- 这个消息只有用她的公钥才能被解密。
- Bob获得这个消息后可以用Alice的公钥解密这个散列值
- 然后将这个数据与他自己为这个消息计算的散列值相比较。
- 假如两者相符的话,那么他就可以知道发信人持有甲的密钥,以及这个消息在传播路径上没有被篡改过。
实战例子
- 通过一次简单实践更好的了解RSA
- 假设p = 2,q = 5(p,q都是素数即可),则N = pq = 10;
- 得到:r = (p-1)(q-1) = (2-1)(5-1) = 4;
- 根据模反元素公式,可以得出,e·d ≡ 1 (mod 4),即e·d = 4n+1 (n为正整数);
- 假设n=5,则e·d = 21,且e、d为正整数,并且e与r互质,则e = 7,d = 3;
- 获得公钥和密钥:公钥为(N, e) = (10, 7),密钥为(N, d) = (10, 3);
- 假设要传输的数字为2,通过公钥加密后为:(2^7)(mod 10) = 8;
- 通过密钥解密:(8^3)(mod 10) = 512(mod 10) = 2,即获得结果;
ECDHE加密算法
- 主要用于HTTPS中的密钥交换
ECDHE
源自ECDH
,ECDH
即使用椭圆曲线加密技术(ECC)的 DH密钥交换(Diffie-Hellman)算法.DH
密钥交换算法,可以让交换双方在不共享任何秘密的情况下协商出一个密钥。ECC
则是建立在基于椭圆曲线的离散对数问题上的密码体制,在相同的密钥长度下,其安全性比RSA
更高。- 而
ECDHE
则是ECDH
的Ephemeral version
,它会为每次握手过程分配一个不同的DH key
,从而提供前向安全性。实际上,在HTTP/2
中允许使用的Cipher Suite
必须采用具有前向安全性的密钥交换算法。
总结
- HTTPS实际就是在TCP层与http层之间加入了TLS/SSL来解决安全问题的。
- 在进行应用数据传输之前,TLS需要通过握手过程来协商安全通信所需的相关参数。
- 整个通信过程中主要用到散列、对称加密、非对称加密和证书等相关技术,来解决客户端与服务器数据传输中各种安全风险问题,从而达到保证整个通信过程的安全。