【网络】HTTPS协议

介绍

本博客主要介绍HTTPS协议的详细知识,HTTPS协议基于TLS协议,当前TLS协议的主要版本是1.2, 最新版本是1.3。
TLS协议的主要基础设施是各种加密算法,以及用于验证的公共CA机构以及客户端验证证书的机制。

本博客参考以下链接:

  • TLSv1.2协议RFC文档:https://tools.ietf.org/html/rfc5246
  • TLSv1.3协议RFC文档:https://tools.ietf.org/html/rfc8446
  • JDK官方博客:https://blogs.oracle.com/java-platform-group/jdk-8-will-use-tls-12-as-default
  • Baeldung博客: 如何在Java7中使用TLSv1.2 https://www.baeldung.com/java-7-tls-v12
  • AES加解密Java示例:https://www.devglan.com/corejava/java-aes-encypt-decrypt

加密基础知识

加密传输的两种方式:PSK或者Certificate(即公共证书)。
加密的作用:加密前的数据称为明文(plaintext),加密的作用是使得加密后的数据看上去符合随机分布。

Public Key

公共证书加密,是非对称加密。
证书会分为两个部分:公钥和私钥。公钥加密的数据,只有私钥能够解密;私钥加密的数据,只有公钥能够解密。

通过将公钥释放出去,而保留私钥,能够确保:1.只有一个人能够使用私钥加密数据,并用公钥解密出来;因此,接收方如果成功使用公钥解密数据,意味着它只能来自于唯一的发送方。2.能够有多个发送方使用公钥加密数据,但只有一个接收方能够解密数据;因此,发送方能够确保数据只被目标接受者解密。

RSA和Diffie-Hellman使用非对称加密。

在通常的HTTPs服务器中,服务器持有私钥,访问者持有公钥。

PKI(Public Key Infrastructure, 公钥结构)

即证书颁发机构认证证书的使用方式。
证书机构分为三级:IPRA=根证书颁发机构,PCA=证书颁发机构,CA=某个域的证书颁发机构

CA证书可分为三类:交叉证书,自颁发证书和自签名证书。
交叉证书:颁发者和主体不同,描述的是CA机构之间的相互信任关系
自颁发证书:颁发者和主体相同,用于支持策略变化
自签名证书:颁发者和主体相同,且该证书的数字签名可以被绑定在该证书上公钥(pubkey)验证

证书撤销(CRL)

每个证书都有时间限制,当证书的有效性因为外部条件变化而失效时,证书即失效。比如名称改变。
每个CA机构都会定期生成一个证书撤回列表(CRL,包含证书的流水号),该列表可被公开读取。当使用者需要验证某个证书时,需要检查该证书是否在最新的CRL列表中,如果存在,则该证书失效。

Cipher Suite

加密组件,即将多种加密方式的组合。

命名方式:TLS_{交换密钥的方法}_{加密算法}_{消息摘要算法}
映射表:

CipherSuite Key Exchange Cipher MAC
TLS_NULL_WITH_NULL_NULL NULL NULL NULL
TLS_RSA_WITH_NULL_MD5 RSA NULL MD5
TLS_PSK_WITH_RC4_128_SHA PSK RC4_128 SHA
TLS_PSK_WITH_3DES_EDE_CBC_SHA PSK 3DES_EDE_CBC SHA
TLS_PSK_WITH_AES_128_CBC_SHA PSK AES_128_CBC SHA
TLS_PSK_WITH_AES_256_CBC_SHA PSK AES_256_CBC SHA
TLS_DHE_PSK_WITH_RC4_128_SHA DHE_PSK RC4_128 SHA
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA DHE_PSK 3DES_EDE_CBC SHA
TLS_DHE_PSK_WITH_AES_128_CBC_SHA DHE_PSK AES_128_CBC SHA
TLS_DHE_PSK_WITH_AES_256_CBC_SHA DHE_PSK AES_256_CBC SHA
TLS_RSA_PSK_WITH_RC4_128_SHA RSA_PSK RC4_128 SHA
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA RSA_PSK 3DES_EDE_CBC SHA
TLS_RSA_PSK_WITH_AES_128_CBC_SHA RSA_PSK AES_128_CBC SHA
TLS_RSA_PSK_WITH_AES_256_CBC_SHA RSA_PSK AES_256_CBC SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 DHE_RSA AES_128_CBC SHA256

其中,TLS_RSA_WITH_AES_128_CBC_SHA是强制支持的。

实践中使用比较多key交换算法是ECDHE_RSA,这种算法是Diffie-Hellman算法的改进,安全性有所提升,同时能够减少能耗。

一个简单的SSL客户端可以选择支持最简单的AES加密算法(Java名称: AES/ECB/PKCS5Padding).

加密算法类型:

Cipher Key Type IV Material Block Size Size
NULL Stream 0 0 N/A
RC4_128 Stream 16 0 N/A
3DES_EDE_CBC Block 24 8 8
AES_128_CBC Block 16 16 16
AES_256_CBC Block 32 16 16

注:IV = Initializing Vector

MAC算法:

MAC Algorithm mac_length mac_key_length
NULL N/A 0 0
MD5 HMAC-MD5 16 16
SHA HMAC-SHA1 20 20
SHA256 HMAC-SHA256 32 32
PSK

即对称加密密钥,旧称:Symmetric Key. 全称:Pre-Shared Key.优点:1.依赖当前支持的cipher suite 2.无需公共证书。
RFC文档:https://tools.ietf.org/html/rfc4279

通常用在小型私有网络中,PSK的存储方式通常由:密码明文,密钥(指纹),16进制字符串

使用python3库cryptography学习加密知识

TLSv1.2

简介

TLSv1.2的消息类型包括:握手协议,alert协议,加密方式变更协议,应用层数据协议。
每一个TLS协议都工作在一个上下文中,该上下文定义了:1.压缩算法 2.加密算法 3.MAC(消息认证code)算法

为了对数据进行加密,通信双方首先需要约定好加密算法和加密使用的密钥(称为master key)。
由于直接传输密钥不安全,所以需要约定好交换key的算法,比如:RSA或者Diffie-Hellman。

TLS协议使用TCP协议传递数据,每个TCP数据报可以包含多个TLS Record结构体。

ClientHello

ClientHello是客户端向服务器发送的第一个消息。它包含:

  • client version, 客户端支持的最高TLS版本号;
  • random, 随机数
  • session_id 会话id,为空则建立新的会话;
  • 加密套件,通常是为了适应客户端的性能而自主选择的。每个加密套件定义了:密钥交换算法,块加密算法,MAC算法以及PRF;服务器需要从列表中选择一个算法,如果没有,则服务器响应failure alert然后关闭连接;
  • 压缩方法列表(当前版本未定义)
  • 扩展

当客户端发出ClientHello之后,它等待响应ServerHello。

ServerHello

ServerHello是服务器向客户端发送的响应ClientHello的消息,包含:

  • server_version, 客户端和服务器都支持的版本号;
  • random,随机数
  • session_id, 如果客户端提供的session_id存在,则使用它,这种情况下,会话处于恢复状态,客户端应当响应Finish信息;否则,服务器会生成一个会话id
  • cipher_suite, 服务器选择的加密套件
  • compression_method,加密方法
  • extensions, 扩展字段

siguature_algorithms:签名算法扩展

客户端使用siguature_algorithms(仅客户端使用该扩展)来指示服务器应当使用什么算法来作为电子签名算法/摘要。因为并非所有的hash算法和加密算法都是兼容的,因此它们需要成对出现。

如果客户端使用这个扩展,则服务器必须从这个列表中选择对应的算法而不能忽略。如果客户端未使用这个扩展,则服务器应当具备下面的行为:

  • 如果密钥交换算法是 {RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA}, 则服务器可认为客户端使用的是{sha1,rsa}
  • 如果密钥交换算法是{DHE_DSS, DH_DSS},则认为是{sha1,dsa}
  • 如果密钥交换算法是{ECDH_ECDSA,ECDHE_ECDSA},则认为是{sha1,ecdsa}

Server Certificate:服务器证书

如果服务器和客户端协定好了使用某个密钥交换方法,如果该方法需要证书来校验,则该消息必须在ServerHello之后由服务器发送。该消息向客户端发送服务器的证书链。
所谓的证书链,是指从第一个证书开始,后面一个证书需要能够证明前面一个证书的有效性;最后一个证书是根证书。

  • 证书链的公钥必须能够支持下面的算法:RSA, RSA_PSK, DHE_RSA, ECDHA_RSA, DHE_DSS等。(注:RSA的公钥能够支持所有hash算法)
  • 扩展字段:server_nametrusted_ca_keys用于寻找证书
  • 服务器提供的证书必须使用客户端提供的signature_algorithms扩展中的一个算法来加密
  • 如果服务器有多个证书可选,则必须选择一个能够满足上面约束条件的证书。

注:TLSv1.2使用的证书结构是X.509v3,参考:

  • RFC文档:PKI参考section 2,证书参考section 4
  • RFC文档:SSH证书
    详细的结构参考本博客 附录 > X.509v3证书结构

Server Key Exchange:服务器密钥交换

该消息需要在Server Certificate消息发送之后发送。如果是匿名协商(anonymous),则需要在ServerHello之后发送。
注意,该消息仅当服务器发送的Server Certificate不足以和客户端进行密钥交换时才需要。意味着使用下面的密钥交换方法需要发送该消息:DHE_DSS,DHE_RSA,DH_anon.而下面的方法则无需也不能发送该消息:RSA, DH_DSS, DH_RSA.
该消息包括公钥,如Diffie-Hellman的公钥,或者其他算法的公钥。

不同的密钥交换算法需要不同的参数,参见本节附录 > 密钥交换参数.

密钥计算方法

master_secret = PRF(pre_master_secret, "master secret",
                          ClientHello.random + ServerHello.random)[0..47];

最终结果是48字节。
其中,PRF函数
pre_master_secret取决于所使用的交换算法,下面分RSA和Diffie-Hellman进行介绍。

基于RSA进行密钥交换

客户端生成一个48字节的pre_master_secret,使用服务器提供的公钥加密后传回服务器,双方基于这个pre_master_secret计算master_secret.

基于Diffie-Hellman进行密钥交换

使用参数中协定的Z作为pre_master_secret,使用之前,Z所有开头的0应当被截断。

Certificate Request:服务器请求客户端证书(可选)

一个非匿名的服务器可以向客户端请求证书(也可以不请求,该请求时可选的)。如果需要请求,则要在Server Key Exchange或者Server Certificate之后请求。

Server Hello Done:服务器请求完成

当服务器响应完ClientHello之后,并且发送必要的信息之后,发送该消息然后等待客户端的响应。
该消息的结构体为空,通过Handshake type区分。

Client Certificate:客户端响应证书(可选)

仅当服务器发送了Certificate Request之后,注意,该消息需要在收到ServerHelloDone之后再发送。
消息体同ServerCertificate

Client Key Exchange:客户端交换密钥

在收到ServerHelloDone之后发送。
如果使用RSA交换,则参数中包括服务器公钥加密后的pre_master_secret;如果使用Diffie-Hellman交换,则参数中包含用于生成pre_master_secretDiffie-Hellman公钥

交换算法 结构
rsa 服务器公钥加密后的pre_master_secret,结构: { client_version(2字节), random(46字节)}
dhe_dss,dhe_rsa,dh_dss,dh_rsa,dh_anon Diffie-Hellman算法的公钥,用于计算pre_master_secret

Certificate Verify:验证客户端证书(可选)

Change Cipher Spec:数据开始加密传输

通知另一方后续的数据将会使用已经协定好的加密方式和密钥进行加密,对方也需要使用同样的加密方式以及对应密钥解密数据。
该消息的结构体包含单字节常量1。

Finished:Change Cipher Spec之后的消息

当一方发送Finished消息并且从对方收到Finished消息并验证之后,可以开始发送应用层数据。
该消息的结构包含一个12字节长的校验数据,校验数据生成:PRF(master_secret, label, Hash(handshake_messages))
如果是客户端发送的,则label为:“client finished”;如果是服务器发送的,则label为:“server finished”
handshake_messages是除了所以已交换的TLS Record结构体除头部以外的Handshake结构体的拼接(不包括HelloRequest和Finished),双方分别计算以进行验证。

PRF函数

格式:PRF(master_secret, label, Hash(handshake_messages))

测试

使用curl测试,https://www.baidu.com作为测试目标;
目前客户端的curl能够使用TLSv1.3发起ClientHello,但是baidu响应的协议是TLSv1.2;如下图所示:
【网络】HTTPS协议_第1张图片

HTTPS协议

基于Wireshark实战

本节内容包含如何从TLSv1.2(握手协议明文)的消息中提取出加密HTTP请求的算法、公钥以及相关参数。

验证Server Key Exchange的签名

计算签名的规则: ServerKeyExchange.signature = rsa(private_key, sha256( CientHello.random + ServerHello.random + ServerKeyExchange.params)
其中,params为去掉签名算法,签名长度和签名之外的数据。rsa为签名认证算法,sha256为hash算法,它们由ServerKeyExchange.signatureHashAndAlgorithm指定.

在Wireshark中,分别取出上面规则中涉及的数据,然后使用下面的命令生成签名,验证是否与消息体中的签名一致:

openssl dgst -sha256 -sign private.key data.bin -out signature

附录

密钥结构

参考:RFC3447 RSAPrivateKey

      RSAPrivateKey ::= SEQUENCE {
          version           Version,
          modulus           INTEGER,  -- n
          publicExponent    INTEGER,  -- e
          privateExponent   INTEGER,  -- d
          prime1            INTEGER,  -- p
          prime2            INTEGER,  -- q
          exponent1         INTEGER,  -- d mod (p-1)
          exponent2         INTEGER,  -- d mod (q-1)
          coefficient       INTEGER,  -- (inverse of q) mod p
          otherPrimeInfos   OtherPrimeInfos OPTIONAL
      }

从该密钥结构中能够同时提取公钥和私钥。因为其中包含了privateExponent。
注意,这里只是给出了私钥的结构,私钥不会被交换。

X.509v3证书结构

   Certificate  ::=  SEQUENCE  {
        tbsCertificate       TBSCertificate,
        signatureAlgorithm   AlgorithmIdentifier,
        signatureValue       BIT STRING
    }

   TBSCertificate  ::=  SEQUENCE  {
        version         [0]  EXPLICIT Version DEFAULT v1,
        serialNumber         CertificateSerialNumber,
        signature            AlgorithmIdentifier,
        issuer               Name,
        validity             Validity,
        subject              Name,
        subjectPublicKeyInfo SubjectPublicKeyInfo,
        issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
                             -- If present, version MUST be v2 or v3
        subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
                             -- If present, version MUST be v2 or v3
        extensions      [3]  EXPLICIT Extensions OPTIONAL
                             -- If present, version MUST be v3
        }

其中值得注意的是issuer(颁发者)和subject(主体名称)两个字段。

PEM文件

参考:https://stackoverflow.com/questions/39660181/understanding-ssl-tls-certificates-structure

.pem文件时PEM编码的,以-----BEGIN CERTIFICATE-----开头。因此pem文件的内容可能是key,证书,证书链;
.crt和.cert是证书文件
.csr是pem编码的,它是证书签名请求。用于向CA申请证书

所谓的PEM文件最初是用在EMAIL中的。PEM文件时BASE64编码的DER文件,并且以 —BEING XXX—和---END XXX—标记开始和结束,以及文件本身的类型。

密钥交换参数

算法 参数
Diffie-Hellman 匿名 p,g,Ys公钥
dhe_rsa, dhe_dss p,g,Ys公钥, 摘要(client_random, server_random,p,g,Ys公钥)
rsa,dh_dss,dh_rsa -

注:非匿名的交换需要摘要。

根证书

根证书具有两个特点,1.自身是证书链的起点,没有其他CA证书再能够证明它们的可信,它们是预设的受信任的证书;2.根证书能够用于颁发其他证书,从而其他证书也能够被信任。根证书都是自签名的证书。所谓的自签名,具体到证书细节上,就是证书的颁发者和证书的主体相同

通常,用户可以创建一个自签名的证书,然后将其加入到系统的受信任的根证书中,然后使用此证书来颁发其他的证书。

根证书的有效期通常都比较长,比如十年。通常,根证书的私钥如果泄露是很严重的危机,因为这意味着攻击者可能使用这个根证书颁发任意证书,使得这些证书都受信任,从而导致中间人攻击。

一般情况下根证书很少联网,其取用需要经过严格的审核。所以,通常会把颁发证书的功能下放给一些二级机构,证书签名的机制实际上是一种信任委托关系。

根证书机构例子:GlobalSign Root CA.

证书签名以及验证

证书的结构中包含三个部分: 证书内容,证书签名算法+参数以及签名。
证书签名算法,以SHA-1-With-RSA-Encryption为例,其id是:1.2.840.113549.1.1.11,

  • 1.首先将证书内容使用DER编码
  • 2.使用SHA-1计算出DER编码后的hash,共160位
  • 3.使用RSA + 上级CA提供的公钥对hash进行加密,获得最终结果

证书颁发的本质就是为请求者的证书添加一个只能由CA机构提供的公钥校验的签名。注意,由于每个证书都有一个serialNumber和一个绑定的公钥,而客户端理论上保存了CA机构的证书。因此,客户端在只信任已知的serialNumber的CA证书的情况下,能够检测到CA证书被篡改。

因此通过提供证书的签名,CA机构能够确保证书不被篡改,即CA机构确保了颁发出去的证书是受信任的,只要客户端能够校验这一点,客户端就能信任这个证书。

客户端不应当信任任何未知的serialNumber的证书;如果遇到需要新增的证书,客户端应当从官方机构获取。当然,或者证书的过程也能通过证书保证安全,因为获取证书也需要用到TLS协议,而且只能使用已有的证书去获取新的证书,所以客户端获取的证书在网络层面不会被篡改。至于在系统层面,比如被黑客入侵并篡改了证书,则客户端就不安全,但这不是证书要解决的问题。证书主要解决的是在不安全网络环境下进行安全的通信。

如果某个证书声称自己的根证书是某个CA,则客户端可以向该CA查询是否具有这个序列号的证书从而验证合法性。

HMAC

简而言之,HMAC是带有key的hash函数。
参考文档

  • RFC 2104 HMAC: Keyed-Hashing for Message Authentication

计算函数:

H = hashing function
B = 64, the block size for iterating hashing
L = hash output length, with 16 for MD5,20 for SHA1
ipad = 0x36 repeat B times
opad = 0x5C repeat B times
K = H(key) or key; 如果key长度大于B,需要先hash
ZK = K + zeros to B;K补齐到B位 
output = H(K XOR opad, H(ZK XOR ipad, text))

为了安全,key至少要大于L长度,并且应当随机选取。

示例:

  • 使用SHA1计算HMAC
openssl dgst -sha1 -hmac "key" file

PRF

RFC定义

  PRF(secret, label, seed) = P_<hash>(secret, label + seed)
  
      P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                             HMAC_hash(secret, A(2) + seed) +
                             HMAC_hash(secret, A(3) + seed) + ...


   A() is defined as:

      A(0) = seed
      A(i) = HMAC_hash(secret, A(i-1))

对于TLSv1.2,hash函数的选择是SHA-256.
迭代次数取决于所需的结果,SHA-256每次产生32个字节,如果需要80字节,就需要迭代3次,产生96字节,去掉16字节。

PRF计算的shell实现(SHA256):

# always use sha256
# usage:
#   prf secret_file label data_file out_size output
# example:
#   prf secret_file 'master secret' client_server_random 48 master_secret
set -e
hash_size=32
secret_file=$1
label=$2
data_file=$3
let out_size=$4
output=$5
dir=$output.dir

if [[ ! -e $secret_file ]];then
   echo "requires secret_file" >&2
   exit 1
fi
if [[ ! -e $data_file ]];then
  echo "requires data_file" >&2
   exit 1
fi

mkdir -p "$dir"

let 'iterations=out_size/hash_size + (out_size%hash_size==0?0:1)'

# function hmac DST_FILE SRC_FILE
# HMAC output 32 bytes per file
function hmac {
    # openssl dgst -sha256 -binary -mac hmac -macopt "hexkey:$hexkey" -out "$1" "$2"
    openssl dgst -sha256 -binary -hmac "$(cat "$secret_file")" -out "$1" "$2"
}

# seed
echo -n "$label" > "$dir/seed"
cat "$data_file" >> "$dir/seed"

cat "$dir/seed" > "$dir/A0"

i=1
while [[ $i -le $iterations ]];do
    # A(i) = hmac(A(i-1))
    hmac "$dir/A$i" "$dir/A$((i-1))"
    # P(i) = hmac(A(i) + seed)
    cat "$dir/A$i" "$dir/seed" > "$dir/A${i}_seed"
    hmac "$dir/P$i" "$dir/A${i}_seed"
    # result = concat
    cat "$dir/P${i}" >> "$dir/key"
    let i++
done

truncate -s "$out_size" "$dir/key"

cp "$dir/key" "$output"

hexdump -C "$output"

# keep this for debug purepose
rm -rf "$output.dir"

Extended Master Secret

如果客户端协商 Extended Master Secret,则Master Secret由RFC文档定义https://tools.ietf.org/html/rfc7627

master_secret = PRF(pre_master_secret, “extended master secret”, session_hash) [0…47];

其中,session_hash替换原来的client_random+server_random作为seed.

session_hash在TLSv1.2使用的算法是SHA256, 内容包含Finished之前的所有握手信息

session_hash = Hash(handshake_messages)

handshake_messages包含所有收到和发送的握手消息(不包括TLS Record头部),从ClientHello到ClientKeyExchange(包括), 因为在ClientKeyExchange中会发送pre_master_secret。

 cat client_hello_handshake.bin server_hello_handshake.bin server_certificate_handshake.bin server_hello_done_handshake.bin client_key_exchange_handshake.bin > session_handshake.bin

实践:

../prf.sh pre_master_secret.bin 'extended master secret' <(openssl dgst -sha256 -binary <(cat client_hello_handshake.bin server_hello_handshake.bin server_certificate_handshake.bin server_hello_done_handshake.bin client_key_exchange_handshake.bin)) 48 extended_master_secret

key_block的推导

  key_block = PRF(SecurityParameters.master_secret,
                  "key expansion",
                  SecurityParameters.server_random +
                  SecurityParameters.client_random);

until enough output has been generated. Then, the key_block is
partitioned as follows:

  client_write_MAC_key[SecurityParameters.mac_key_length]
  server_write_MAC_key[SecurityParameters.mac_key_length]
  client_write_key[SecurityParameters.enc_key_length]
  server_write_key[SecurityParameters.enc_key_length]
  client_write_IV[SecurityParameters.fixed_iv_length]
  server_write_IV[SecurityParameters.fixed_iv_length]

对于AES-128而言, enc_key_length=16,没有fixed_iv_length

实践:

../prf.sh pre_master_secret.bin 'key expansion' <(openssl dgst -sha256 -binary <(cat client_hello_handshake.bin server_hello_handshake.bin server_certificate_handshake.bin server_hello_done_handshake.bin client_key_exchange_handshake.bin)) 48 extended_master_secret

Encrypt-Then-MAC扩展

默认情况下,消息加密使用MAC-Then-Encrypt, 参考:

  • RFC 7366
  • Wireshark解决Encrypt-Then-MAC的计算

数据格式转变:

原始: encrypt( data || MAC || pad )
该扩展: encrypt( data || pad ) || MAC

注意,只有CBC加密模式有MAC,因此该扩展只应用于CBC加密算法;其他加密算算法应当忽略该扩展(Stream, AEAD)。

解密过程:首先验证MAC,如果MAC失效,则报错。然后再进行解密。

TLS Record的格式:

   struct {
          ContentType type;
          ProtocolVersion version;
          uint16 length;
          GenericBlockCipher fragment;
          opaque MAC;
          } TLSCiphertext;

CBC结构

      struct {
          opaque IV[SecurityParameters.record_iv_length];
          block-ciphered struct {
              opaque content[TLSCompressed.length];
              opaque MAC[SecurityParameters.mac_length];
              uint8 padding[GenericBlockCipher.padding_length];
              uint8 padding_length;
          };
      } GenericBlockCipher;

MAC的计算:
MAC(MAC_write_key, seq_num +
TLSCipherText.type +
TLSCipherText.version +
TLSCipherText.length +
IV +
ENC(content + padding + padding_length));
或者更简单的: MAC(MAC_write_key, seq_num + TLSCipherText excluding trailing MAC length bytes)

注意:seq_num为uint64,即8字节。
示例:

  • 计算Client Finished Record,注意长度时text的长度(48),不是整体长度(68),因为需要减去MAC的长度(20)
openssl dgst -sha1 -mac hmac -macopt "hexkey:$(hexdump -ve '1/1 "%02x"' key_block_client_write_MAC)" <(cat <(echo -ne '\x00\x00\x00\x00\x00\x00\x00\x00') <(echo -ne '\x16\x03\x03\x00\x30') <(head -c -20 client_finished_encrypt.bin))

Finished数据签名

签名数据=
PRF(master_secret, finished_label, Hash(handshake_messages))
[0…verify_data_length-1];

finished_label: 客户端"client finished", 服务器"server finished".
handshake_messages: 收到的所有消息(除去HelloRequest),不包含TLS Record头部。
Hash: 与PRF使用的hash相同,对于TLSv1.2, 就是sha256

实践:

../prf.sh extended_master_secret 'client finished' <(openssl dgst -sha256 -binary <(cat client_hello_handshake.bin server_hello_handshake.bin server_certificate_handshake.bin server_hello_done_handshake.bin client_key_exchange_handshake.bin)) 12 client_finished_verify_data

Application Data

解密示例:注意, 必须加上-nopad选项,否则可能导致bad decrypt.

openssl enc -aes-128-cbc -d -in <(tail -c +17 client_application_data.bin|head -c -20) -iv "$(hexdump -v -e '1/1 "%02x"' <(head -c 16 client_application_data.bin))" -md sha1 -K "$(hexdump -v -e '1/1 "%02x"' key_block_client_write_key)" -nopad

你可能感兴趣的:(高阶技术知识,网络,https)