当前位置: 首页 > 科技观察

HTTPS虐我千百遍,我却对她如初恋!

时间:2023-03-12 00:57:17 科技观察

本文将讨论HTTPS的加解密原理。很多人知道RSA,认为HTTPS=RSA,用RSA加密解密数据。其实,这是错误的。图片来自PexelsHTTPS使用RSA进行身份验证和密钥交换,然后使用交换的密钥对数据进行加密和解密。身份验证是使用RSA的非对称加密,而数据传输是使用双方相同密钥的对称加密。那么,什么是对称加密和非对称加密呢?对称加密和非对称加密假设隔壁的小王想约小红出去,但是不想让小明知道,所以想用对称加密的方式给小红传一个小纸条。如下图:他要发送的数据是“Meetat5:00PM”(5点开会,如果是中文可以用UTF-8编码),加密方式是直接在ASCII表中左移或右移。他的key是3,意思是在ASCII表中往后移动3位,就会变成“Phhw#dw#8=33#SP”,这样如果普通人截取了,就不知道是什么意思了。但是我们可以想一想,如果他能截获你的数据,他也能截获你的密钥并解密。如下图所示:所以小王打算使用非对称加密。非对称加密的特点是双方都有自己的公钥和私钥对。公钥发给对方,密钥不交换,自己保管不泄露。如下图:小红的公钥是:public_key=(N,e)=(3233,17)她把公钥发给小明,自己的私钥是:private_key=(N,e)=(3233,2753)这里注意公钥和私钥都是两个数字,N通常是一个大整数,e代表一个幂指数。现在小王要给小红发消息,就用小红的公钥加密,怎么加密呢?他要发送的第一个字母是t="M","M"的ASCII码是77,77的加密过程计算如下:T=77^e%N=77^17%3233=3123put77的e次方和N模得到T=3123,然后把这个数发给小红(其他字母同理)。小红收到T后,用自己的私钥解密,计算如下:t=T^e%N=3123^2753%3233=77计算方法一样,这样T就可以还原成t,只要公钥和私钥配对的话,可以用一些数学公式来证明上面的计算是正确的。这就是RSA的加解密原理。如果不知道私钥,则无法正确解密。反之,也可以用私钥加密,用公钥解密。那么HTTPS是如何使用RSA进行加解密的呢?让我们从HTTPS连接建立过程开始。HTTPS连接建立过程HTTPS主要有以下功能:验证服务器身份,比如我访问google.com时连接到Google服务器,防止数据被劫持。例如,一些运营商会在http页面插入广告,以防止敏感数据被窃取和篡改。等等正如openssl的评论所说,这是防止中间人攻击的唯一方法:我们以MDN(https://developer.mozilla.org)的网站为例,然后使用wireshark抓包,观察建立HTTPS连接的过程。如下图所示:首先是三次TCP握手,然后客户端(浏览器)发起HTTPS连接建立请求。客户端首先发送一个ClientHello包,然后服务器响应一个ServerHello。然后将其证书发送给客户端,然后双方进行密钥交换,最后使用交换的密钥对数据进行加密和解密。在ClientHello中,客户端会告知服务器自己当前的信息,如下图:包括客户端要使用的TLS版本、支持的加密套件、要访问的域名、一个随机数(随机数)等等。需要提前告知服务器你要访问的域名,以便服务器发送对应域名的证书,因为此时还没有发生HTTP请求。服务端会在ServerHello中做出一些回应:服务端选择的加密套件叫做TLSECDHERSAWITHAES128GCM_SHA256,意思是:密钥交换使用ECDHE证书签名算法RSA数据加密使用AES128GCM签名验证使用SHA256然后给服务客户端发送了4个证书:第一个证书的通用名称是我们当前访问的域名developer.mozilla.org。如果通用名称是*.mozilla.org那么这个证书可以用于mozilla.org的所有二级子域。第二张证书是第一张证书的颁发机构(CA)的证书,是Amazon,也就是说Amazon会用自己的私钥对developer.mozilla.org进行签名。以此类推,第三个证书将签署第二个证书,第四个证书将签署第三个证书,我们可以看到第四个证书是根证书。一个证书里面会有什么,我们可以展开第一个证书看看,如下图:证书包含三部分:tbsCertificate(待签名证书)待签名证书内容证书签名算法CA签名假设CA将使用其私钥签署tbsCertificate并将其放在签名部分。为什么要签署证书?签名是为了验证身份。Authentication我们先来看看tbsCertificate里面有什么,如下图:包括证书的公钥,证书适用的通用名,证书的有效期,以及颁发者。亚马逊的证书也有上面的结构。我们可以复制亚马逊证书的公钥,如下图:中间有一些填充的数字,用灰色的字表示。可以看到N通常是一个大整数(二进制2048位),e通常是65537。然后我们用这个CA的公钥解密mozilla.org的证书签名,方法和上面类似:取解密后的十六进制数的后64位,即为二进制的256位SHA哈希签名。接下来,我们在wireshark中将tbsCertificate导出到一个原始的二进制文件中,手动计算出tbsCertificate的SHA256哈希值:然后使用openssl计算其哈希值,如下:users/liyincheng/tbsCertificate.bin)=5e300091593a10b944051512d39114d56909dc9a504e55cfa2e2984a883a827d我们发现手动计算的哈希值和加密后的证书可以和证书中的哈希值一致!这意味着只有知道Amazon私钥的人才能正确签名mozilla.org的证书,因为公钥和私钥是唯一匹配的。所以我们验证了第一个证书mozilla.org确实是由第二个证书Amazon颁发的,同样的,我们可以验证Amazon是由第三个证书颁发的,它是由第四个根证书颁发的。而第四个证书是根证书,是操作系统自带的(可以通过Mackeychain工具查看):如果Hacker通过DNS欺骗把你访问的域名指向他的机器,然后伪造一个证书.但是由于根证书是操作系统内置的,它不能改变签名的公钥,也没有正确的私钥,所以只能使用自己的私钥。由于公钥和私钥没有配对,所以很难保证加密和解密。消息是一致的。还是直接把浏览器拿到的证书搬到自己的服务器上?这样一来,给浏览器颁发的证书是一模一样的,但是由于他不知道证书的私钥,无法进行后续的操作,所以这是没有意义的。这就是HTTPS验证身份的方式。另一个例子是SSH,它需要手动验证签名是否正确。比如通过调用或者发邮件的方式告诉服务器,服务器的签名和自己计算出来的证书的签名是否一致。如果一致,说明证书没有被篡改(比如证书的公钥没有被修改为Hacker的公钥):上面显示的值是自己手动计算的值。如果将这个值与之前的值进行比较,看是否相等,就可以知道发送的证书是否被修改过。那么,为什么不直接使用RSA密钥对来加密数据呢?因为RSA密钥对值太大,不适合频繁加解密数据,所以需要较小的密钥。另一个原因是服务器端没有浏览器或客户端的密钥,无法向浏览器发送加密后的数据(不能用自己的私钥加密,因为公钥是公开的)。所以需要密钥交换。密钥交换密钥交换有两种方式:RSA和ECDHE。RSA方法相对简单。浏览器生成一个密钥,然后用证书RSA的公钥加密后发送给服务器,服务再使用它的密钥。对密钥进行解密得到密钥,从而实现密钥的共享。它的缺点是虽然攻击者在发送过程中无法破解,但是如果它保存了所有的加密数据,由于证书过期无人维护等原因导致私钥泄露,那么它就可以用这个私钥来解密所有先前传输的数据。相反,使用ECDHE是一种更安全的密钥交换算法。如下图所示,双方通过ECDHE交换密钥:ECDHE全称EllipticCurveDiffie–HellmankeyExchangeEllipticCurveDiffie-HellmanKeyExchange,是对Diffie-Hellman密钥交换算法的改编改进。该算法的思想如下图所示:A为了得到共享秘钥K,用自己的私钥计算出一个数g^a,发送给B,B的私钥为b,B得到K=g^a^b,同时将g^b发送给A,A也得到K=g^b^a。这个应该比较容易理解,引入椭圆曲线加密可以增加破解难度。椭圆曲线加密证书的签名算法有两种:RSA和新EC。如下图,google.com是使用的ECC证书:上面我们讨论的是RSA,而破解RSA的难点在于公钥的N不能分解成质数。如果能把证书的N分成两个素数相乘,就可以算出证书的私钥,但是在现在的算力下是做不到的。破解ECC的难点在于找到指定点的系数。如下图所示,有一个椭圆曲线方程:y^3=x^2+ax+b:给定一个起点G(x,y),现在需要计算点P=2G的坐标,而过程在G点与曲线在-2G处作切线,相对x轴做-2G的反射,得到2G点。为了计算3G的坐标,如下图所示:将2G与G相连,曲线接近-3G,再做反射得到3G。同样,计算4G就是把G和3G连接起来,然后做反射。如果连接最后一个点和起点的线垂直于x轴,则所有点都已使用。EC的难点在于给定起点G和点K:K=kG,求得K非常困难(K足够大)。这个K就是私钥,K=kG就是公钥。ECC如何加密和解密数据?假设要加密的数据为m,以该点为x坐标得到曲线上的一个点M,取一个随机数r,计算该点C1=rG,C2=M+rK。将这两点作为加密后的数据发送给对方,对方收到后用私钥K解密数据。过程如下:M=C2-rK=C2-rkG=C2-rkG=C2-kC1通过上面的计算可以还原出M,不知道私钥K的人无法解密。有关更多详细信息,请参阅这篇Medium文章《ECC elliptic curve encryption》。这样我们就了解了ECC的原理,那么如何使用ECC进行密钥交换呢?ECC密钥交换的原理很简单,如下图所示:之前交换的数是2的幂,现在变成了曲线上两点的交换。指定曲线方程。例如CurveX25519使用的曲线方程为:y^2=x^3+486662x^2+x。使用的曲线方程会在keyexchange中指定,如下图:mozilla。org使用的曲线方程是secp256r1,也是比较流行的,参数比CurveX25519大很多。密钥交换也是使用证书的私钥进行签名,保证交换的密钥不会被篡改,但是这里的私钥是mozilla自己的私钥。也就是说,从建立连接到现在,都是明文传输。接下来,双方发送ChangeCipherSpec报文通知,后续的报文按照之前约定的方法进行加密。至此,整个安全连接就建立起来了。HTTPS证书的应用那么谁在做HTTPS加密呢?服务器端通常由Nginx、Apache等反向代理服务器完成,具体业务服务器无需处理。客户端通常通过浏览器等进行加密和解密。Chrome它使用boringSSL库,从openssl派生而来。我们可以通过let'sencrypt申请免费的TLS证书,需要每3个月手动更新一次。证书分为三种类型:DV、OV和EV。DV适合个人,OV和EV需要身份验证,EV是最高端。EV证书会在浏览器的地址栏显示证书的企业名称:但是新版Chrome好像去掉了这个,所以我们在打开mediumconsole的时候可以看到提示:作为实验的一部分,Chrome暂时只在地址栏中显示锁定图标。您的带有扩展验证的SSL证书仍然有效。另外,我们可以使用openssl生成自签名证书,执行如下命令:opensslreq-x509-nodes-sha256-days365-newkeyrsa:2048-keyouttest.com.key-outtest.com.crt会得到两个文件,test.com.crt是证书,test.com.key是证书的私钥,如下图:然后用这两个文件让Nginx使用HTTPS访问,如下代码所示:server{listen443;server_nametest.com;sslon;ssl_certificatetest.com.crt;ssl_certificate_keytest.com.key;}可以把这个证书加入到系统证书中,让浏览器信任,也可以直接使用mkcert工具一步到位。还有一种叫做客户端证书的客户端证书。您还需要向CA申请客户端证书。与服务端TLS证书的区别在于,服务端证书通常绑定域名,而客户端证书可以签署任何本地可执行文件。签名验证算法与上面讨论的TLS证书相同。为什么可执行文件需要签名,因为如果不签名,系统会拦截安装或者运行,比如Mac双击未签名的dmg包提示:不让你直接运行,以及Windows也有类似的提示,Windows会给出一个警告:当我们运行签名的exe文件时,会是一个正常的提示,比如Chrome的提示:综上所述,本文主要讨论对称加密和非对称加密的原理,以及介绍如何使用RSA签署证书验证连接服务器身份的测试,如何使用ECC进行数据加密和密钥交换,介绍如何生成和使用HTTPS证书,介绍客户端证书。相信看完这篇文章,你会对HTTPS加解密有一个更全面的认识。