我们现在经常听说谁谁谁密码被盗了,谁谁谁信息又被劫持了。其中有一个原因:绝大部分网站用的是http这个明文协议。你以为很安全的在password框里填了隐藏的密码,他却一字一句明明白白的写到了网络上。于是乎好多网站开始从http迁移到https(至少登录部分)。我也准备做同样的事情,因此抽时间和小伙伴tt一起研究了一下https。 刚开始看https的时候,各种头大。国内网上讲相关的资料虽然一大堆,但是大部分是相互的抄,内容多而乱,且没有把事情讲清楚。后来查阅了一些外文资料(包括rfc、wikipedia等),读了JSSE的源代码以后,基本把这个事情的来龙去脉看懂了大部分,但是涉及到很多很细节的东西还是觉得不是完全懂,如有疏漏和错误,敬请大家指正和原谅 :-) 这篇文章的目标:用尽量简单和有趣的语言,把这个复杂的东东讲述清楚。所以,接下来我打算分成三部分来聊聊我理解的Https: 1、入门篇:主要用通俗的语言讲讲Https是什么东东,以及他大体的工作方式; 2、技术篇:结合抓包工具和源代码,分析Https的通讯流程和细节; 3、理论篇:不是特别深入的聊聊一些跟Https相关的算法。 ====入门篇的分割线==== What’s HTTPS? 简单的说,https就是给http带了一个安全套,即使别人拿到了信息,也不知道这个里面装的啥。客户端(包括browser、手机app等)和服务器每次发http包的时候,都对这个包加个密,让第三者看到的只是加密后的乱码(我只想对你说:你猜你猜你猜猜猜),到对端以后再解密。 这个安全套,原来是叫SSL(Secure Sockets Layer),最先是Netscape弄出来的,后来哥们儿完蛋了,就慢慢变了名字,叫TLS(Transport Layer Security Protocol)。具体的区别可以去wikipedia搜索TLS,他们之间的升级细节讲述的非常详细(这一点百度百科真的差的有点远~)。 这个安全套跑在TCP的上层,在TCP连接完成后且HTTP启动前,协商一些跟加密相关的工作,完成协商之后,就可以对要发送的http包加密/解密了。 那他到底协商了些啥呢?其实就是保证安全的几个问题: 1、服务器要证明自己是靠谱的、安全的,不然给一个假网站发加密的密文就跟裸奔没啥区别 2、服务器和客户端通讯需要的加密算法和加密密钥 就跟当年天地会和韦小宝通信一样,先要亮出身份,证明自己,然后再拿出暗语的书信。 ComeOn! How TLS works? 第一步,服务器证明自己是靠谱的。 一个哥们儿XX说他是天地会的。如果你是韦小宝,你会怎么确认他的身份呢? 其中有一种方案可能是这样的:他会说S1是他师傅,如果你知道S1并和他确认了,就ok了。如果不认识,就继续问S1的师傅S2……一直问道陈近南,只要陈近南确认了,那就可以证明他了。看起来好像设计模式里面的责任链 XX -> S1 -> S2 -> … -> ROOT 服务器证明自己也是同样的逻辑,服务器S0有一个证书,说我是谁谁谁,这个证书由上级签发机构S1核准,如果你本地有这个S1的证书,那验证一下就可以了。如果没有,就问S1的签发机构S2。直到根的签发机构。如果本地认证找到了其中任何一级的证书,就认为S0是靠谱的。否则就是不靠谱。S0 -> S1 -> S2 -> … -> Root CA 实际上非常像工商局发的营业执照,你上面有我盖的红坨坨才是靠谱的。 上图就是淘宝的认证级联关系。 这些靠谱的证书内置在操作系统、jdk等地方(百度或者谷歌上搜索“https数字证书设置”相关内容就可以看到)。 此图就是我本机证书列表的一部分。 这个就是基本逻辑,说白了,就是找一个我们都公认靠谱的人来证实你的靠谱。 第二步,协商加密算法+密钥。 加密和摘要算法有很多,常见的比如RSA、AES、DES、MD5、SHA等等。 大家把他们这样来分: 1、加密/解密算法:能加密同时能反解的,就是加解密算法。按照加解密的密钥是否一样,又分为对称和非对称算法。比如对称加密算法:AES、DES;非对称加密算法:RSA。 2、摘要算法:就是只用来做摘要、签名、验证防止被别人篡改,基本不能反解(有可能可以通过碰撞暴力破解)。比如:MD5、SHA。 那服务器和客户端接下来就协商一下,我们要用什么加密解密算法和密钥防止别人看见,用什么摘要算法,防止别人篡改。 一般来讲,对称加密算法效率会比非对称高,所以通常选择对称加密的AES较多。双方通过某种方式协商出一个密钥,后面就通过这个密钥和加密算法进行加解密。 客户端发送一个:“地振高冈,一派溪山千古秀” 服务端回复一个:“门朝大海,三河合水万年流” 整个过程大体就是这样,后面双方就开始发HTTP的加密包,对方解包得到对应的HTTP数据。 世界一下就清晰了,对吗? No No No 其实还是很复杂滴…… 如果要想了解详细的技术内容,就让我带着你继续往下看(你敢不敢跟我来) ===技术篇的分割线=== 工欲善其事,必先利其器 为了做详细的分析,我做了几个准备工作: 1、装了一个wireshark,用来抓取网络包 2、写了一个java程序,打开debug运行(java -Djavax.net.debug=all TestHttps),用来看交互细节 import java.net.URL; import java.net.URLConnection; public class TestHttps { public static void main(String[] args) throws Exception { final URL url = new URL('https://www.taobao.com'); final URLConnection conn = url.openConnection(); conn.connect(); } } 3、找到openjdk源代码:http:/// 通过前两个工作可以看到网络交互的过程和详细的数据包,第三个可以用来分析整个流程的代码。 (注:以下涉及到代码的分析,都是基于JDK8进行的,如果因为版本原因,相关函数和代码行数对接不上,请大家查找对应版本的代码) 好了,准备工作做好了,我们开始吧! 抓个包,先看看门道 先给taobao同学发个请求吧:curl https://www.taobao.com,看到整个交互过程大体是这样的(我把tcp三次握手,ACK包等无关的数据包都过滤掉了,只剩TLS相关的数据包): 上图有几个交互数据都合并到一个TCP包进行发送了,比如漂蓝的那一行(No = 49)的TCP包实际上包含了三个TLS包(Certificate、Server Key Exchange、Server Hello Done),下面分析的时候,我就把这个包展开。
上面抓的包全部展开就是这样的效果。怎么样,是不是差不多也看了个大概?我来翻译翻译吧。
看起来是不是很简单呢? 这实际上就是文章一开始,我说的要解决的两个大问题: 1、认证server端的靠谱性 2、交换加密算法和密钥 具体每个包里面都发了哪些数据?server端靠谱性是如何来证明的?加密算法和密钥是怎么交换的?接下来让我一一给你道来。 具体的交互流程和代码的实现 我们就按命令逐个来分析一下。 C:Client Hello 可以看到发送了很多数据,但是最关键的几个数据: 1、TLS的版本 2、随机数:这个是用来生成最后加密密钥的影响因子之一,包含两部分:时间戳(4-Bytes)和随机数(28-Bytes) 3、session-id:用来表明一次会话,第一次建立没有。如果以前建立过,可以直接带过去。 4、加密算法套装列表:客户端支持的加密-签名算法的列表,让服务器去选择。 5、压缩算法:似乎一般都不用 6、扩展字段:比如密码交换算法的参数、请求主机的名字等等 这一段的java实现,是在sun.security.ssl.HandshakeMessage.ClientHello里面: S:Server Hello 当服务器收到客户端的问候以后,立即做出了响应: 大体内容和客户端差不多,只是把加密算法的套装列表换成了服务器选择支持的具体算法。 通过这一步,客户端和服务器就完成了加密和签名算法的交换。这里的TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256拆分开看就是:TLS协议,用ECDH密钥交换算法交换对称加密密钥相关的参数,用RSA算法做签名,最后使用AES_128_CBC做内容的对称加密,SHA256做摘要。 具体实现在:sun.security.ssl.HandshakeMessage.ServerHello S:Certificate 这一步很关键,是服务器给客户端展示证书的时候。 证书是一个链,从最底层一直到最顶层,表示谁谁谁给我认证的。翻译过来就是文章一开始给大家看到的那个东东: 证书一般采用X.509标准,后面我会详细来讲述证书格式和如何级联认证。X509证书具体实现在:sun.security.x509.X509CertImpl Certificate消息的实现代码在:sun.security.ssl.HandshakeMessage.CertificateMsg 里面 S:Server Key Exchange 这个消息是用来发送密钥交换算法相关参数和数据的。这里要提前提一下,就是根据密钥交换算法的不同,传递的参数也是不同的。 常用的密钥交换算法:RSA、DH(Diffie-Hellman)、ECDH(Elliptic curve Diffie–Hellman) 后面会详细来讲这几个算法的某几个,现在就不详细走这个分支,只是知道他们可以交换密钥,有参数要传递即可。 (这里不得不感叹一句,老外对基础科学的研究真的是太深入了,这些算法十分的巧妙。希望有一天中国人也能对基础科学做出更多的贡献~) 可以看到这里用到的是ECDH算法,交换了一些参数,对数据做了签名,防止劫持者篡改。 在Java里,这个消息有多个实现,分别代表RSA、DH、ECDH算法,对应的类分别是: sun.security.ssl.HandshakeMessage.ServerKeyExchange sun.security.ssl.HandshakeMessage.RSA_ServerKeyExchange sun.security.ssl.HandshakeMessage.DH_ServerKeyExchange sun.security.ssl.HandshakeMessage.ECDH_ServerKeyExchange 以下是ECDH的实现: S:Server Hello Done Server要表达的信息基本表达完了,把主持人话筒交给客户端: 对应的实现:sun.security.ssl.HandshakeMessage.ServerHelloDone C:Client Key Exchange 这是客户端对Server Key Exchange的回应,用于交换密钥需要的参数。和服务器一样,不同的密钥交换算法实现是不一样的,因此需要的参数也是有差异的。 这里用的是ECDH交换算法。 Java的实现也对应的多个,分别是: sun.security.ssl.RSAClientKeyExchange sun.security.ssl.DHClientKeyExchange sun.security.ssl.ECDHClientKeyExchange 以下是ECDH的具体实现: 好了,经过以上的步骤,Server-Client已经将服务器认证的相关工作做完了,密文函数&密钥交换需要的参数也都相互传递了。剩下的,就是各自用一个叫做PRF(Pseudo-Random Function)的算法去生成加密密钥,具体的这个函数是一个对多因子多次迭代摘要运算等的实现,这里姑且就当做是一个很简单的随机运算函数吧,比如:key = rand_c + rand_s + C。 到这一步,客户端和服务器就完成了密钥相关的交换。 有了这个密钥,接下来,客户端和服务器就开始切换交流语言了(用密文开始说悄悄话),他们会各自发一个命令,说明自己已经准备好,开始切换语言了。 C:Change Cipher Spec 客户端切换成密文模式 这个在Java里的实现在:sun.security.ssl.Handshaker C:Finished(Encrypted Handshake Message) 这个包表明握手已经完成,并且对之前发过的数据进行加密发送给对方做校验,防止被篡改。同时也验证一下,加密算法、密钥工作是否正常。 具体代码在:sun.security.ssl.HandshakeMessage.Finished 在收到这两个消息以后,服务器也发出同样的消息,即: S:Change Cipher Spec S:Finished(Encrypted Handshake Message) 嘘~~~(此地长出一口气) 至此,整个身份验证、加密/解密算法&密钥的交换都已经结束,剩余的,就是进行正常的HTTP请求以及对请求数据的加密/解密。 以上这些步骤,都是对于使用ECDH密钥交换算法、没有session、不要求验证客户端有效性的情况。对于有session的、或者要求验证客户端有效性、或者使用其他密钥交换算法的,请求会有不一样,具体的可以看看实现代码,里面都非常详细也容易阅读,具体代码在: sun.security.ssl.ClientHandshaker sun.security.ssl.ServerHandshaker 里面有一个processMessage函数,是用switch...case写的相关的状态机。 看看,为了做安全的HTTP请求,需要额外付出多少的代价,来来回回需要多出多少次数据交换。 好了,如果还想了解其他更多更详细的东东,就继续跟我往下走,否则,在这里就可以return了 ^o^ ===有点难度的理论分割线=== 接下来准备聊聊关于x509证书&证书验证、加解密、签名、密钥交换、随机的一些算法。由于我自己对这部分没有专门的研究,只是借这次机会看了一些资料,所以了解的不是非常深入,可能只能涉及一些皮毛,大家多多谅解和指正。 1、关于X.509证书&证书验证 X.509就是一个数字证书的标准,就像工商营业执照一样,证明你这个网站是合法的。详细的可以参见wikipedia和RFC: https://en./wiki/X.509 http://www./rfc/rfc2459.txt 在TLS中使用到了这样一个证书来进行有效性的认证。因为不是专业研究这个的机构,我们就不深入去研究这个标准,而是看看他的数据格式和如何进行验证。 证书的模样 我们先看看wireshark抓到的包长什么样: 再看看Java的API文档给出的比较详细的定义: http://docs.oracle.com/javase/8/docs/api/java/security/cert/X509Certificate.html 可以看出来,整体分为证书和对证书的签名两大部分。 证书包含:版本、序号、证书的签名算法、签发者、主题(被签发者)、有效期等的信息。 为了方便阅读,我们直接用chrome来查看证书: 可以看到非常详细的信息,包括:被签发者的基本信息、签发者的基本信息,加密信息和签名。我们如果自己用openssl做证书的话,都会要求相关项的填写。有兴趣的同学可以自己做一个证书试试手 ^_^ 证书的验证 当TLS协议验证一个网站是否有效的时候,Server会给出一个X509的证书链。客户端收到这个证书链以后,对证书链进行验证,所做的工作如下: 1、用最底端(证书链第一个)的证书,去验证请求的主机和证书里的是否是一致 2、逐次验证证书链里每张证书的合法性,直到找到一张证书在系统中存在:这一步又包含每张证书是否在不信任名单里、检查签名算法、检查时间是否过期、检查证书的发布者和证书链的上一级是否匹配、证书链的签名检查 以上检查中,大多是按字节对比,相对比较简单,相关代码参见以下几个函数的实现: sun.security.ssl.X509TrustManagerImpl.checkTrusted sun.security.validator.SimpleValidator.engineValidate sun.security.x509.X509CertImpl.verify sun.security.x509.X509CertImpl.checkValidity 不过,其中证书链的签名检查是一个非常有意思的算法,这个算法我着实研究了一阵儿,还写了一个简单的程序去实验,在这里稍微详细讲述一下。 我们得到了一个证书链,将他扩展开成为一个层级关系: 对于每一级的证书,都是由上一级用私钥对证书的sha摘要值进行签名(Root一般由自己签发),签名一般使用RSA算法。验证的时候,用上一级的公钥对签名进行解密,还原对应的摘要值。如下图: 这里先提前最最最简单的插入一下RSA算法。RSA是非对称加密,有一个公钥(模数和指数)和一个私钥。M代表明文消息,C代表密文,n代表公钥模数,e代表公钥指数,d代表私钥。 如果我们用公钥对明文M加密,私钥解密,则是为了传递信息,对消息进行加密; 如果我们用私钥对明文M加密,公钥解密,则是为了保证消息是我签发的,没有伪造和篡改。 这里,我们介绍后一种(即防篡改和伪造)。 签名加密:C = (M ^ d) mod n 签名解密:M = (C ^ e) mod n 先请不要问我为什么,后面会单独讲,哈哈哈(是不是很贱)~ 好,回归正题,上一级认证机构用私钥(d)对下一级的证书的SHA数字签名(M) 进行RSA加密得到密文(C)。而第三方只要用上一级的公钥(e, n)对这个加密进行还原,并能得到相关的SHA数字签名(M),就可以认为是经过上一级认证过的(因为只有他才能签的出来)。 Come on,我们来实践一下吧: 下图淘宝上一级证书的公钥(这些机构就卖证书就可以赚翻了):n(图中的256字节公共密钥)和 e(图中的指数) 我们写一个程序来验证一下: 因为要用大数计算,所以用到了Java的大数类(这是用的最直接暴力的算法,其实还可以有很多优化,比如O(n) -> O(lgn),先取模再乘等等)。 最后输出的结果如下: 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420bedfc063c41b62e0438bc8c0fff669de1926b5accfb487bf3fa98b8ff216d650 其中1fff...ff00是PKCS #1 v1.5 标准的前导补位,详见:http://tools./html/rfc2313#page-9 接下来 303130…0420 是SHA-256摘要算法的标识,详见:http://tools./html/rfc3447#page-43 剩余部分正好32Bytes,是整个证书的SHA-256摘要。 是真的吗?确认那个32Bytes就是SHA-256的摘要么?来吧,我们写一个程序验证一下: 这个程序大致的意思,就是截获X509证书的验证,输出对应的证书信息。并用SHA-256对整个证书做摘要。得到的信息输出如下: ====================== cert-count: 3 subject: CN=*.tmall.com, O='Alibaba (China) Technology Co., Ltd.', L=HangZhou, ST=ZheJiang, C=CN cert-sign: 3ec0c71903a19be74dca101a01347ac1464c97e6e5be6d3c6677d599938a13b0db7faf2603660d4aec7056c53381b5d24c2bc0217eb78fb734874714025a0f99259c5b765c26cacff0b3c20adc9b57ea7ca63ae6a2c990837473f72c19b1d29ec575f7d7f34041a2eb744ded2dff4a2e2181979dc12f1e7511464d23d1a40b82a683df4a64d84599df0ac5999abb8946d36481cf3159b6f1e07155cf0e8125b17aba962f642e0817a896fd6c83e9e7a9aeaebfcc4adaae4df834cfeebbc95342a731f7252caa2a97796b148fd35a336476b6c23feef94d012dbfe310da3ea372043d1a396580efa7201f0f405401dff00ecd86e0dcd2f0d824b596175cb07b3d sha-256: bedfc063c41b62e0438bc8c0fff669de1926b5accfb487bf3fa98b8ff216d650 ====================== 这个证书签名以及对证书做的SHA-256摘要,和我们之前所得到的结果一样一样的~~ 好了,到此为止,X509证书的格式和验证基本上也讲了一个大概了,真是非常不容易啊(为了写文章,肚子已经饿的咕咕叫了)~ 2.关于RSA算法 仔细想想,RSA算法在哪些地方被用到了呢? 1、证书签名的时候(上面刚刚做了验证,对吧) 2、密钥交换 为了防止信息被截获篡改,需要对密钥交换的参数做签名。 所以,RSA算法是一个非常关键的加解密算法。那我们就来简单聊聊吧~ (最近看RSA,越看越觉得这个算法很有意思~) 注:为了在文本上打印方便,以下采用 ^ 这个符号作为乘方的运算符,即2^4代表2的4次方。 来看看他的定义吧,老复杂了! 1、选两个超级大的素数:p 和 q 2、把他们乘起来:n = p * q 3、然后把p-1和q-1也乘起来:m = (p - 1) * (q - 1) 4、再找一个和m互质的数:e -> gcd(e, m) = 1 5、最后,找一个d,满足:(e * d) mod m = 1 6、然后公钥就是(n, e)的组合,私钥就是(n, d)的组合 …… 我的妈,这么复杂,人都要疯了,是不是? 为什么要搞这么复杂呢? 其实用到了几个原理或者定理或者…… 他们分别是: 1、大数分解难题 2、费马小定理 3、中国剩余定理 4、扩展欧几里德算法 是不是被吓蒙了呢?哈哈哈,由于这一篇是讲Https的,所以就不详细讲这几个原理,简单表述一下(就算是简单,也要说很多,也要码很多的字……)。 首先,RSA建立的一个基本原则就是大数分解,如果没有这个原则,就扯淡了。 我们给两个素数,比如 5 和 11。我们能很容易求出他们的乘积:5 * 11 = 55 当然,在这个规模下,我们也很容易将 55 分解成 5 * 11。 但是,如果这两个素数很大呢,比如10的几百次方。我们还是很容易求出他们的乘积。不过,你再想分解他,就不容易咯~ 其次,著名的费马同学,发明了很多很多定理,其中比较著名的就是费马大小定理,小定理是这么说的:对于一个素数n和任意的正整数a,( a ^ ( n - 1 ) ) mod n = 1 我们来试试,比如 n = 5,a = 4, 那么 a ^ ( n - 1 ) = 4 ^ ( 5 - 1 ) = 256,256 mod 5 = 1 很神奇吧!网上可以搜一搜详细的证明。 这个公式演化一下,就可以得到a的n次方和a分别对n取模,结果是一样的:( a ^ n ) ≡ a (mod n) 比如上面的那个例子:( 4 ^ 5 ) mod 5 = 4 ; 4 mod 5 = 4 那就是说如果n是一个素数,任意一个数的i次方模n,都会呈现n-1个数的循环周期(不一定是最短的周期),比如: 4的1,2,3,4,5,6……次方模5的余数:4 1 4 1 4 1…… 如果n是两个素数p和q的乘积,那么这个周期会呈现 (p -1) * (q - 1)这么长。也就是说,a 和 a的(p - 1) * (q - 1) + 1次方模n,得到的结果是一样的。特别的,如果a <> 哈哈哈,绕了那么大一个弯子,最后的意思就是说,我只要知道 ( a ^ ( (p - 1) * (q - 1) + 1) ) mod n的余数,实际上就是知道了a,对吧! 那我只要变个花样儿就可以。 我先让m = (p - 1) * (q - 1) ,然后找一个和m互质的数e,再求出一个d,让 e * d = k * m + 1。这样,我给一个数字 T (其中T < n),计算出="" c="(T" ^="" e)="" mod="" n,="" 我拿着c做一个(c="" ^="" d="" )="" mod="" n,于是乎,神奇的一幕发生了:(c="" ^="" d)="" mod="" n="(" (t="" ^="" e)="" ^="" d="" )="" mod="" n="(" t="" ^="" (e="" *="" d)="" )="" mod="" n="(" t="" ^="" (k="" *="" m="" +="" 1)="" )="" mod="" n="T" mod="" n,因为="" t="">< n=""> 举个例子,我们让p = 5, q = 7,推算出来 n = 5 * 7 = 35, m = (5 - 1) * (7 - 1) = 24 我们找一个e,比方是11,那么一定可以找到一个d = 35,e * d = 11 * 35 = 385, ( e * d ) mod m = 385 mod 24 = 1 我们选一个数字T = 18, 计算 C = (T ^ e) mod n = 2 然后 计算 (C ^ d) mod n = 18 = T 以上就是RSA的原理,是不是要清楚一点了呢? 现在还有一个问题没有解答,就是我们是怎么找出d来的,实际上就是要解一个方程: ( e * x ) mod m = 1 等价于 e * x + m * y = 1,其中x就是d。 这个就是中国剩余定理里面讲到的东东,具体实现的时候,用扩展辗转相除法(这就是扩展欧拉算法)做个迭代就出来了。 补充一点,RSA涉及到指数运算,效率会比较低。实际上有很多优化的方法,比如O(n) -> O(lgn),先取模再乘,降维到p和q等等,就不在这里细展开了。 3.最后简单聊一个密钥交换的DH算法 我们在之前提到,TLS要做的事情就是两个:身份校验 & 加密算法和密钥协商。 身份校验我们前面已经比较详细的讲述过了。加密算法的协商我们之前也在流程中讲述过。剩下关于密钥交换和协商,我们之前轻描淡写的聊了下。下面稍微详细的讲述一下。 在密钥交换的过程中,会用到一个PRF的函数,是”Pseudo-Random Function”这个的简称,中间计算过程比较复杂,有兴趣的同学可以在网上搜索查阅(由于偷懒,我没有详细深入这个函数 ^_^)。 整个密钥的生成大体如下: master_secret = PRF(pre_master_secret, 'master secret', ClientHello.random + ServerHello.random) 具体代码可以参见: 抽象上来讲,就是 server和client 根据Hello时的两个随机数 加上 客户端产生的pre_master_secret来产生一个master_secret,最后由这个东东生成需要的MAC(Message Authentication Code)、key等等加密需要东东。 那其中就有一个关键问题,客户端的pre_master_secret怎么样告诉服务器的? 我们可以用之前讲过的RSA算法,客户端通过服务器公钥将这个值加密后传递给服务器,服务器再去解密。也可以通过一个叫做DH(Diffie-Hellman)的算法。维基百科对这个算法讲的十分详细。 我就简单翻译一下: 有两个哥们儿,Alice和Bob,他们想交换数据,于是乎也不知道怎么就想出了一个牛逼的算法: 1、取模数 p = 23,底数 g = 5 2、然后Alice想了一个整数a = 6,发送给Bob一个数:A = (g ^ a) mod p = (5 ^ 6) % 23 = 8 3、同理,Bob想了一个整数b = 15,发送给Alice一个数:B = (g ^ b) mod p = (5 ^ 15) % 23 = 19 4、Alice拿着Bob给的B = 19,计算了一个数:s = (B ^ a) mod p = (19 ^ 6) % 23 = 2 5、Bob也用同样的方法,算了一下 : s = (A ^ b) mod p = (8 ^ 15) % 23 = 2 就这样,在不泄露a、b的情况下,他们两都得到了一个一样的数。就这样,数据交换了。。。 其实理论基础就是: A ^ b ≡ (g ^ a) ^ b ≡ g ^ (a * b) ≡ (g ^ b) ^ a ≡ B ^ a (mod p) 更详细的说明,可以看维基百科的解释。 后来又有一个改进的算法ECDH,这里就不详细讲述了(偷懒了,以后有机会再补~) ====总结的分割线==== 好了,断断续续的抽大家睡觉的时间,把这篇文章写完了(总算没有当太监)。个人觉得把HTTPS整个的流程、交互的过程做了一个大体的了解。有一部分做的深入些,还有很多算法看了一个一知半解,越是深入却发现不懂或者不了解的越多。剩余还有几个点由于篇幅和时间的原因没有讲到,比如:如何访篡改、如何防回放攻击、如何做PRF、交互里面有些扩展参数等,后面准备再抽时间把这些补全。 由于之前主要是聚焦在常规算法和互联网技术架构方面,对信息相关的科学了解不是那么深入。后面准备把信息论、加解密算法的相关东西系统性的看下,完了之后分享出来。有兴趣的同学可以关注我的微信:simplemain 作为一个码工,就想安安静静做做技术。春天来了,可以有更多的时间听着优美的轻音乐写更多的东西了。下面这张照片是我2012年4月在山东济南拍的春天,4年时间过去了,又把他翻出来,感受一下当年的春意盎然。Hello, World! 附:部分参考资料 https://en./wiki/Transport_Layer_Security http://www./2009/06/first-few-milliseconds-of-https.html http://www-brs.ub./netahtml/HSS/Diss/MeyerChristopher/diss.pdf http:///snapshot/repository./java/root/jdk/openjdk/8u40-b25/ http://drops./tips/11232 http://tools./html/rfc3447#page-43 http://netsecurity.51cto.com/art/201505/476337_all.htm x509: http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/security/cert/X509Certificate.html RSA: http://blog.csdn.net/starryheavens/article/details/8536238 https://en./wiki/RSA_%28cryptosystem%29 Diffie-Hellman: https://en./wiki/Diffie%E2%80%93Hellman_key_exchange http://my.oschina.net/u/1382972/blog/330456 |
|