<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>我吃你家米了 - 计算机网络</title><link href="/" rel="alternate"></link><link href="feeds/ji-suan-ji-wang-luo.atom.xml" rel="self"></link><id>/</id><updated>2020-11-16T00:00:00+01:00</updated><entry><title>https通信过程</title><link href="httpstong-xin-guo-cheng.html" rel="alternate"></link><published>2020-11-16T00:00:00+01:00</published><updated>2020-11-16T00:00:00+01:00</updated><author><name>12138</name></author><id>tag:None,2020-11-16:httpstong-xin-guo-cheng.html</id><summary type="html">&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;前段时间给自己的网站上了个https证书，因此想借此机会深入了解一下https通信的过程，借助wireshark和万能的搜索引擎，稍微进行了一些调查，总结出这篇文章&lt;/p&gt;
&lt;p&gt;参 …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;前段时间给自己的网站上了个https证书，因此想借此机会深入了解一下https通信的过程，借助wireshark和万能的搜索引擎，稍微进行了一些调查，总结出这篇文章&lt;/p&gt;
&lt;p&gt;参考链接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.comparitech.com/blog/information-security/diffie-hellman-key-exchange/#Security_issues_of_the_Diffie-Hellman_key_exchange"&gt;https://www.comparitech.com/blog/information-security/diffie-hellman-key-exchange/#Security_issues_of_the_Diffie-Hellman_key_exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://crypto.stackexchange.com/questions/58283/why-is-a-diffie-hellman-key-exchange-required-when-rsa-is-already-being-used-for"&gt;https://crypto.stackexchange.com/questions/58283/why-is-a-diffie-hellman-key-exchange-required-when-rsa-is-already-being-used-for&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cryptologie.net/article/340/tls-pre-master-secrets-and-master-secrets/"&gt;https://www.cryptologie.net/article/340/tls-pre-master-secrets-and-master-secrets/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tutorialspoint.com/cryptography/message_authentication.htm"&gt;https://www.tutorialspoint.com/cryptography/message_authentication.htm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"&gt;https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange&lt;/a&gt;   &lt;/li&gt;
&lt;li&gt;&lt;a href="https://asecuritysite.com/encryption/curve"&gt;https://asecuritysite.com/encryption/curve&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cryptobook.nakov.com/asymmetric-key-ciphers/elliptic-curve-cryptography-ecc"&gt;https://cryptobook.nakov.com/asymmetric-key-ciphers/elliptic-curve-cryptography-ecc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/28566058"&gt;https://zhuanlan.zhihu.com/p/28566058&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;TLS中的key exchange（密钥交换）&lt;/h1&gt;
&lt;p&gt;以前我一直以为https交换密钥的过程就是客户端随机出来一个对称加密算法的密钥，然后使用证书的公钥加密发送给服务器，服务器使用自己的密钥解密即可，深入了解之后才发现并不是这么简单&lt;/p&gt;
&lt;p&gt;下面是使用wireshark抓取的TLS握手阶段的client hello包中所支持的cipher suite：&lt;/p&gt;
&lt;p&gt;&lt;img alt="1605469580769" src="https://s2.loli.net/2024/06/14/zbg1XQRCrch2ADT.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到&lt;code&gt;ECDHE&lt;/code&gt;是最多的协商方式，即椭圆曲线DH算法，E代表Ephemeral （短暂的），表示密钥在每次会话结束后就会销毁（保证前向保密性）&lt;/p&gt;
&lt;p&gt;可以看到几乎一半的cipher suite都使用了DH算法进行密钥的交换，下面我们来了解一下到底什么是DH算法&lt;/p&gt;
&lt;h2&gt;DH算法&lt;/h2&gt;
&lt;p&gt;DH的主要作用是安全地产生一个共享secret，然后使用这个共享secret派生出两个密钥（对称加密）用于后续的消息加密和完整性验证&lt;/p&gt;
&lt;p&gt;当今流行的安全协议大多都实现了自己的DH算法，如TLS、SSh、IPsec、PGP等&lt;/p&gt;
&lt;p&gt;首先我们用颜色来对DH算法的大致流程进行解释：&lt;/p&gt;
&lt;p&gt;&lt;img alt="img" src="https://s2.loli.net/2024/06/14/oFWBcYuS9At4jQI.png"&gt;&lt;/p&gt;
&lt;p&gt;整个过程可以描述为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;双方以一个约定的颜色作为初始颜色&lt;/li&gt;
&lt;li&gt;双方各自随机挑选一个颜色，与初始颜色进行混合&lt;/li&gt;
&lt;li&gt;双方交换各自混合过后的颜色&lt;/li&gt;
&lt;li&gt;双方使用前面的随机颜色与交换过来的颜色进行混合&lt;/li&gt;
&lt;li&gt;双方得到最终的共享secret&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从上面的过程来看，中间人可以获得初始颜色以及交换的混合后的颜色，通过爆破的方式可以猜解出用于混合的那个随机颜色，最终获得共享secret，当然颜色的爆破可能会比较简单，但是如果是数学计算，就会花费很长的时间和计算资源来进行爆破，而对于DH算法而言，这个时间长（&lt;strong&gt;取决于初始数字的长度&lt;/strong&gt;）到无法接受，因此算法是安全的&lt;/p&gt;
&lt;p&gt;可以看到在整个过程中共享secret是从未被传输过的，这就保证了我们可以在不安全的通信信道中安全地进行密钥的交换&lt;/p&gt;
&lt;p&gt;下面我们用数学的方式来演示一下整个过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首先，Alice和Bob会协商出来一个数字p（这个就是初始数字）和数字g这个数字相对较小（太大的话计算会比较复杂），我们这里假设p为17，g为4&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Alice随机生成一个数字a，假设为3&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Bob随机生成一个数字b，假设为6&lt;/li&gt;
&lt;li&gt;然后Alice进行运算：&lt;img alt="1605174230765" src="https://s2.loli.net/2024/06/14/r1djgTsX7h3LNCG.png"&gt;，计算出A的值为13&lt;/li&gt;
&lt;li&gt;Bob进行同样的运算，计算出B的值为16&lt;/li&gt;
&lt;li&gt;双方交换AB值&lt;/li&gt;
&lt;li&gt;然后各自用交换得到的值替换掉g，进行同样的运算&lt;strong&gt;，计算出最后的共享值s，值为16&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里B和s的值相等只是一个巧合，因为我们挑选的数字太小了，实际应用中是不会出现这种情况的&lt;/p&gt;
&lt;p&gt;其实就是下面这个等式（&lt;strong&gt;注意下面的公式中ab其实不是同一行的，而是指数形式，即g的a次方的b次方&lt;/strong&gt;）：&lt;/p&gt;
&lt;p&gt;&lt;img alt="image-20201114094756381" src="https://s2.loli.net/2024/06/14/3Ho5MPZb2gjs7V6.png"&gt;&lt;/p&gt;
&lt;p&gt;上述过程中的p、g、A、B都是明文传输的， DH算法的安全性基于一个数学上的事实：在仅知道g、ga(A)、gb(B)的值的情况下，无法计算出a和b的值&lt;/p&gt;
&lt;p&gt;上面讲解的仅仅涉及到两个人，事实上DH算法支持多方同时进行共享key的创建&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在实际应用中，DH不会单独使用，因为它本身不提供认证，无法避免中间人攻击，如果有一个中间人位于Alice和Bob中间，那么最后Alice、Bob和中间人都会得到共享秘钥，从而导致安全信道建立失败，一般的实现都会包括一个电子证书以及公钥加密算法进行签名，比如RSA，我们在上面看到的client hello包中的cipher suite中的RSA以及ECDSA都是用于签名的公钥加密算法&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;DH算法和RSA的关系&lt;/h2&gt;
&lt;p&gt;相信不少人都存在这样的疑问：&lt;strong&gt;既然RSA同样可以达到让完全陌生的双方进行安全通信（进行秘钥交换），而且还能提供身份认证，那为什么还要再使用DH算法进行秘钥交换呢&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个有几方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DH算法不同于RSA，在对称秘钥交换方面速度要更快一些&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在秘钥交换过程中，由于DH不会传输秘钥&lt;/strong&gt;，且每次的secret key（这里指的是DH算法计算过程中的随机secret，不是最终的共享secret）都是随机生成的，并会在本次会话完成之后清除交换秘钥过程中产生的所有key，因此是&lt;strong&gt;前向安全&lt;/strong&gt;的（即使本次会话中的long-term key（共享secret）被破解出来，也不会影响到之前会话的安全性），&lt;strong&gt;但是RSA就不一样了，如果使用RSA进行秘钥交换，那就需要使用服务器的私钥解密使用服务器公钥加密的共享secret，这样一旦服务器的私钥被破解出来，所有会话的共享secret都会被解密出来（因为公钥并不会经常更换），从而无法保证前向安全&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此RSA算法在秘钥交换过程中起到的作用主要是验证通信双方的身份，服务器向客户端提供自己的证书，客户端使用CA的公钥验证证书的合法性，然后根据证书中提供的服务器相关信息与当前正在进行通信的服务器进行比对以确认服务器身份&lt;/p&gt;
&lt;h1&gt;TLS中的Pre-Master Secrets和Master Secrets&lt;/h1&gt;
&lt;p&gt;首先我们来介绍一下MAC算法&lt;/p&gt;
&lt;h2&gt;何为MAC&lt;/h2&gt;
&lt;p&gt;这里的MAC是密码学中的名词而不是指网卡，MAC全称Message Authentication Code，翻译过来就是消息认证码&lt;/p&gt;
&lt;p&gt;MAC是一种对称加密算法，用于提供消息认证，进行认证的前提条件是收件人和发件人共享一个密钥&lt;code&gt;K&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在TLS中MAC被用来进行消息完整性的验证，这个&lt;code&gt;K&lt;/code&gt;会由master secrets派生出来&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实质上MAC就是一个被加密的校验值，你可以理解为它是用于加密文件的hash值的&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="MAC" src="https://s2.loli.net/2024/06/14/CkB82zovjeUuTWt.jpg"&gt;&lt;/p&gt;
&lt;p&gt;可以看到在发送前发件人先将共享密钥K和要发送的消息消息传给MAC算法，计算出一个MAC值，然后和原始消息（未加密，这里仅作说明只用，实际中传输的消息是被加密的）一起发送给收件人，收件人向MAC算法提供共享密钥K和发送过来的消息，计算出MAC值，如果和发送过来的MAC值相等，则证明消息没有被篡改，同时确认消息来源是可信的&lt;/p&gt;
&lt;p&gt;MAC具有以下两个局限性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不提供安全的共享密钥传输渠道&lt;/li&gt;
&lt;li&gt;不具备签名功能，因为收发双发均可以计算出相同的MAC，因此收件人可以伪造发件人不曾发送过的消息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过这两个问题都可以通过公钥加密算法来解决，前者可以使用DH解决，后者可以使用RSA算法进行签名&lt;/p&gt;
&lt;h2&gt;PRF（伪随机数函数）&lt;/h2&gt;
&lt;p&gt;在我们加密或者MAC任何东西的时候，我们都绕不过密钥交换的问题&lt;/p&gt;
&lt;p&gt;&lt;img alt="Simplified SSLv3/TLS" src="https://s2.loli.net/2024/06/14/NPaU5hKSbI21Hf7.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pre-master key，也就是图中的S，指的是双方最后协商出来的共享key&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;协商的方式与client和server在握手阶段选择的cipher suite有关，大部分情况下是DH，不过也有使用rsa的&lt;/p&gt;
&lt;p&gt;根据选择的cipher suite，{S}可能是使用服务端证书的公钥（比较少见）进行加密的，也可能是通过DH协商出来的密钥进行加密的&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为了保证最后用于加密通信的密钥足够&lt;/strong&gt;“随机”，我们还需要将pre-master key、client.random和server.random作为参数传到一个函数（prf）中继续进行计算&lt;/p&gt;
&lt;p&gt;pre-master key的长度取决于采用的DH算法（DH算法有多种版本）和向算法传递的参数&lt;/p&gt;
&lt;p&gt;最终计算出来的master key一般是一个定长的值（也跟选择的cipher suite有关）&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tools.ietf.org/html/rfc5246#section-8.1"&gt;RFC&lt;/a&gt;中的计算方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;master_secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PRF&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pre_master_secret&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;master secret&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nv"&gt;ClientHello&lt;/span&gt;.&lt;span class="k"&gt;random&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ServerHello&lt;/span&gt;.&lt;span class="k"&gt;random&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;[&lt;span class="mi"&gt;0&lt;/span&gt;..&lt;span class="mi"&gt;47&lt;/span&gt;]&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;其中两个random是在client hello和server hello消息中发送的，PRF代表伪随机函数&lt;/p&gt;
&lt;p&gt;最终生成的master key长度总是48字节，从master key中可以派生出成四个key：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client_write_MAC_key&lt;/li&gt;
&lt;li&gt;server_write_MAC_key&lt;/li&gt;
&lt;li&gt;client_write_key&lt;/li&gt;
&lt;li&gt;server_write_key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;四个key，分为两类，一个是MAC key，用于验证消息的完整性和来源的可靠性，另一个write key是用于加密消息的对称加密的密钥，虽然是4个key，其实client和server的key是一样的&lt;/p&gt;
&lt;p&gt;在handshake阶选择的cipher suite（对称加密算法）决定了这些key的长度，不过其中有一个例外，就是&lt;code&gt;AEAD&lt;/code&gt;算法（其实该算法已经是主流的解决方案），它不需要&lt;code&gt;MAC key&lt;/code&gt;和&lt;code&gt;write key&lt;/code&gt;，而是需要&lt;code&gt;write_IV&lt;/code&gt;，AEAD算法的特点就是将加密和认证集成到了一起&lt;/p&gt;
&lt;p&gt;常见的 AEAD 算法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AES-128-GCM&lt;/li&gt;
&lt;li&gt;AES-192-GCM&lt;/li&gt;
&lt;li&gt;AES-256-GCM&lt;/li&gt;
&lt;li&gt;ChaCha20-IETF-Poly1305&lt;/li&gt;
&lt;li&gt;XChaCha20-IETF-Poly1305&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据&lt;a href="https://tools.ietf.org/html/rfc5246#section-6.3"&gt;RFC&lt;/a&gt;的描述，上面提到的4个key采用下面的方式生成，根据采用的算法，key block产生的数量也会有所不同&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;key_block = PRF(SecurityParameters.master_secret,
                &amp;quot;key expansion&amp;quot;,
                SecurityParameters.server_random +
                SecurityParameters.client_random);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;现在我们来解释一下PRF函数&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tools.ietf.org/html/rfc5246#section-5"&gt;RFC中关于TLS v1.1中PRF函数的说明&lt;/a&gt;，和&lt;a href="https://tools.ietf.org/html/rfc4346#section-5"&gt;TLS v1.2&lt;/a&gt;有所不同，PRF的定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;PRF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;P_&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;采用go语言实现的PRF函数：&lt;a href="https://golang.org/src/crypto/tls/prf.go#L145"&gt;https://golang.org/src/crypto/tls/prf.go#L145&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;P_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HMAC_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="nx"&gt;HMAC_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="nx"&gt;HMAC_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;其中函数A的定义为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;
&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HMAC_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;当计算的结果满足我们需要的master key长度时，迭代计算就会停止，结果也就出来了，&lt;strong&gt;计算key_block同理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面是对go语言实现的prf的代码注释：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;//按照RFC中的说明，将pre-master key分成两部分&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;splitPreMasterSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//实现RFC中提到的P_hash函数&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//此处的hmac包所实现的函数是前面提到的MAC算法，对消息的hash进行加密，以进行消息发送者身份的验证&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//首先创建出一个hmac对象，它接收连个参数，前者是hash算法，后者是加密使用的key&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//seed即为要加密的消息&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//a就是最后求得的MAC值&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//就像我们在文章前面提到的，hmac会进行迭代运算直到满足预定的长度&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//这里的result是填充了指定个数的0的数组&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//等到长度满足要求之后master-key的计算也就完成了&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//重置h，清空之前的消息&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//写入前面计算出来的seed的MAC值&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//根据文章中提到的，每次进行MAC的计算都会加上seed，因此在写入a之后还要再写入seed&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//前面只写了seed是因为A(0)=seed&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//代码读到这里我们可以看到go语言的这段代码完全是按照RFC的说明进行实现的&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//将求得的结果写入result&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//记录一下本次写入的长度&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//对于第一次循环，i=1，A(1) = HMAC_hash(secret, A(0)) = HMAC_hash(secret, seed) = a&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//下面的计算是为第二次循环做准备，也就是要求A(2)的值&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;//这里求得的a其实就是A(2) = HMAC_hash(secret, A(2-1)) = HMAC_hash(secret, a)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//go语言实现的prf函数（这个是TLS 1.0版本），接收四个参数，其中第一个result是传出参数，用于保存计算得到的master-key&lt;/span&gt;
&lt;span class="c1"&gt;//第二个参数secret是之前计算出来的pre-master key&lt;/span&gt;
&lt;span class="c1"&gt;//seed字节数组就是client.random + server.random&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prf10&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//sha1和md5都是摘要算法（hash算法）&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hashSHA1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hashMD5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;New&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//创建出一个长度为label和seed长度之和的数组，然后将label和seed填充进去&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;):],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//将pre-master key分成s1和s2两部分&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;splitPreMasterSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;//将分开之后的pre-master key分别进行pHash运算，将求得的两个值进行异或运算&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hashMD5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hashSHA1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;^=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//这是TLS 1.2版本的prf实现代码，可以看到和我们文章中描述的是一致的，直接使用pre-master key进行计算，没有进行分割&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prf12&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hashFunc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;):],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;pHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;labelAndSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hashFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;文章中还有很多不够完善的地方（比如&lt;code&gt;write_IV&lt;/code&gt;密钥的计算方法以及对https通信进行抓包分析），后面如果有空会再继续进行更新  ：）&lt;/p&gt;</content><category term="计算机网络"></category></entry><entry><title>DNS之SPF记录</title><link href="dnszhi-spfji-lu.html" rel="alternate"></link><published>2020-07-15T00:00:00+02:00</published><updated>2020-07-15T00:00:00+02:00</updated><author><name>12138</name></author><id>tag:None,2020-07-15:dnszhi-spfji-lu.html</id><summary type="html">&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;晚上看文章的时候看到了hackerone提交的&lt;a href="https://github.com/upgoingstar/hackerone_public_reports/blob/master/HackeronPublicReports.csv"&gt;bug列表&lt;/a&gt;，其中一个很有意思，有一个赏金猎人向hackerone&lt;strong&gt;提交了一个类型为不符合安全设计规范&lt;/strong&gt;的 …&lt;/p&gt;</summary><content type="html">&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;晚上看文章的时候看到了hackerone提交的&lt;a href="https://github.com/upgoingstar/hackerone_public_reports/blob/master/HackeronPublicReports.csv"&gt;bug列表&lt;/a&gt;，其中一个很有意思，有一个赏金猎人向hackerone&lt;strong&gt;提交了一个类型为不符合安全设计规范&lt;/strong&gt;的bug，原因是hackerone.com域名没有SPF记录&lt;/p&gt;
&lt;p&gt;在时间线中可以看到&lt;a href="https://hackerone.com/reports/120"&gt;猎人和官方的对话&lt;/a&gt;，从中学到了一些知识&lt;/p&gt;
&lt;h1&gt;SPF记录和TXT记录&lt;/h1&gt;
&lt;p&gt;参考&lt;a href="https://www.ietf.org/rfc/rfc4408.txt"&gt;RFC 4408&lt;/a&gt;的3.1.1节&lt;/p&gt;
&lt;p&gt;该RFC定义了一个新的SPF资源记录，这个记录和TXT记录的格式是完全一样的，两者均使用US-ASCII编码&lt;/p&gt;
&lt;p&gt;使用TXT记录已经不被推荐，但是有些DNS服务器并未针对SPF类型的记录进行实现，考虑到兼容性的问题，一个合规的SPF记录应该同时具备SPF记录和TXT记录，如下所示：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;example.com. IN TXT &amp;quot;v=spf1 +mx a:colo.example.com/28 -all&amp;quot;
example.com. IN SPF &amp;quot;v=spf1 +mx a:colo.example.com/28 -all&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;SPF的全称是&lt;code&gt;Sender Policy Framework&lt;/code&gt;，该记录的主要作用是防止别人伪造发件人地址，配置了SPF记录的域名在收件方会检测发件人的使用的邮服（MTA）是否位于SPF记录中所指向的IP范围&lt;/p&gt;
&lt;h1&gt;后记&lt;/h1&gt;
&lt;p&gt;仅凭SPF记录未设置这一问题，该猎人就获得了500刀的赏金！！！&lt;/p&gt;</content><category term="计算机网络"></category></entry></feed>