【编程技术-TLS协议全解析】此文章归类为:编程技术。
本文全程干货,但稍微苦涩难懂,假如能耐心看完,我相信您一定能得到想要的TLS细节。如果有什么疑问,请留言,必定知无不言。
一. 概述
mbedtls完整实现了tls/https协议,大量使用于嵌入式设备。使用mbedtls过程中,一些细节需记录和深入学习,出于学习目的,记录了mbedtls、https/tls的学习心得。
首先是一些基础概念。
MAC: Message Authentication Code
SHA3:跟AES一样,该名词是一个标准,其实现算法为keccak算法。
HMAC: Hash-based Message Authentication Code
DSA:Digital Signature Algorithm,一种离散对数的数字签名算法。注意,它不是加密方法。
Elgamal:有限域上的离散对数加密方案。
ECC:椭圆曲线离散对数加解密方案。椭圆曲线加密算法的强度比RSA、Elgamal都要高,也就是说,同样的位数,破解难度更大。
椭圆曲线方程:
该方程是求椭圆弧长的椭圆积分的反函数。该方程因变量是,因此该函数的图像必然关于轴对称。????2=????????3+????????2+????????+????该方程是求椭圆弧长的椭圆积分的反函数。该方程因变量是????2,因此该函数的图像必然关于????轴对称。
椭圆曲线上的加法
椭圆曲线上的2倍运算
椭圆曲线的取反运算
椭圆曲线算法一般使用有限域的椭圆曲线算法,具体方程为:
下图是离散域椭圆方程
添加图片注释,不超过 140 字(可选)
Diffie-Hellman:一种密钥交换算法。适用于离散对数问题。
Sbox:或者写为S-box,即subsitution box,汉语表示替换盒,即将数据替换为盒子中的值。
Rijndael:AES加密算法使用的加密标砖。AES只是代表一个加密代号,他的具体实现算法由Rijndael完成。
TLS和SSL:tls1.0就相当于ssl3.1
tls版本字段定义:
tls版本
tls版本号最小为0x0300,也就是ssl3.0;最大为0x304,也就是tls1.3
二. 数据包
ssl/tls协议有两层。第一层长度5字节分为3个字段,主要用于封装第二层的记录协议;第二层记录协议下有多个子协议,但是长度、格式不固定。
TLS协议层次图
TLS协议号
类型 | 标志 |
---|---|
ClientHello | 1 |
ServerHello | 2 |
Certificate | 11 |
ClientKeyExchange | 16 |
MsgAlert | 21 |
EncryptedHandshakeMessage | 22 |
ServerHelloDone | 14 |
ApplicationData | 23 |
ChangeCipherSpec | 20 |
tls命令号
mbedtls中的定义:
添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)
关于msgAlert消息,有如下定义:
struct{ AlertLevel level; //警告错误级别 AlertDescription description; //警告协议的详细描述信息 } Alert; enum{ warning(1), fatal(2), (255) } AlertLevel; enum { close_notify(0), //正常关闭通知,表示会话正常结束。 unexpected_message(10) , //表示收到了不适当或未预期的消息类型 bad_record_mac(20), // 记录层的消息认证码(MAC)验证失败 decryption_failed_RESERVED(21), //解密接收到的数据时出现了问题 record_overflow(22), //接收到的数据记录长度超过了协议所规定的最大长度,违反了协议的格式要求 decompression_failure(30), //解压缩操作失败 handshake_failure(40), // 握手过程中发生错误,无法完成握手 no_certificate_RESERVED(41), //未接收到有效证书 bad_certificate(42), //证书无效或格式错误 unsupported_certificate(43), //对端提供的证书不受本地信任存储支持 certificate_revoked(44), //证书已被吊销 certificate_expired(45), //证书已过期 certificate_unknown(46), //无法验证对端提供的证书的有效性 illegal_parameter(47), //在握手消息中遇到了非法或不支持的参数 unknown_ca(48), //验证过程中使用的证书颁发机构未知 access_denied(49) , //访问被拒绝 decode_error(50), //解码消息时出错 decrypt_error(51) , //解密消息时出错 export_restriction_RESERVED(60), protocol_version(70), //不支持的协议版本 insufficient_security(71), //安全度低于最低要求 internal_error(80), //发生了内部错误 user_canceled(90) , // 用户主动取消了相关操作 no_renegotiation(100) , //客户端或服务器拒绝重新协商请求时产生 unsupported_extension(110) , //一方接收到的SSL/TLS扩展不被另一方支持或理解 (255) } AlertDescription;
第一层握手协议截图
添加图片注释,不超过 140 字(可选)
以下几点需要注意:
记录协议中的长度字段,表示从本字段往后到数据包结尾的长度。这点比较奇怪,因为本字段之后,还有协议版本字段。也就是说,此值等于第一层中的长度值-4。此外,若本字段第一个字节不为0,则丢弃该数据包,也就是说,虽然本层的长度字段为3字节,但是表示的长度不得大于64kb。
加密套件是由服务器端决定的。客户端只是列出本机支持的加密套件,但是决定使用哪一种,取决于服务端。
加密套件选用ECDHE等,tls握手协议中才会有Sever Key Exchange消息
会话密钥就由 "客户端随机数" 、 "服务端随机数 "、密钥交换算法算出的 "PreMasterKey"三者结合生成的。
CryptedHandshakeMessage是使用对称秘钥加密的第一个报文,并且包含MAC校验。该数据包若是可以正确解密、并且校验正确,在wireshark中会被显示为Client/Server Finished包。
session key的作用。当客户端再次请求Client Hello,服务器端从客户端携带的SessionID,找到该客户端上次和服务端SSL加密通信的上下文信息,直接复原对称加密信道,而不用再进行SSL握手密钥协商。
sesson key重用连接
7. 加解密函数中,若使用_mm_load_si128等simd并行计算加速指令,需使用calloc函数分配内存,即将内存分配地址值清0!
tls握手过程数据包
三. X509证书
主要是sig字段。该字段的计算方式如下:
对证书做sha计算,并用私钥加密。
客户端计算本证书中除了此字段外的sha值,并用公钥解密该字段,比较两者是否相同。
RSA和对称加密是网络安全的核心支柱理论,在作者的一篇文章中详细描述了RSA为例的非对称加密的数学原理和64位密钥的最小化实现:b9dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0j5%4y4U0b7J5y4e0M7@1y4b7`.`.
还有另一种更为流行的非对称加密算法即DSA,此算法的数学原理跟RSA类似,利用了大质数分解难题,但是另一种数论模型:寻找大质数的原根。作者将会继续写一篇DSA数学原理和实现的文章。
本人还有一篇关于RC4加密的说明文章:4faK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0j5%4y4e0V1@1y4U0x3H3y4b7`.`.
四. 加解密套件
每个加密套件有四个功能:认证算法(身份验证)、密钥交换算法KeyExchange(密钥协商)、对称加密算法Enc(信息加密)和信息摘要Mac(完整性校验)。
以TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384为例:
添加图片注释,不超过 140 字(可选)
本问例子使用tls_rsa_with_rc4_128_md5
五. 加解密过程
mbedtls中使用rsa,发现解密过程跟上文描述一致。上文用到的工具链接:a34K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6z5k6i4N6n7k6h3f1I4x3e0W2Q4x3V1k6Z5N6s2c8H3M7#2)9#2k6Y4m8S2j5$3E0W2N6s2y4Q4y4h3k6V1k6h3y4J5P5i4m8@1
PremasterKey生成过程。PremasterKey即客户端 Client Key Exchange 消息内容。解密后为48字节的随机数。其中前两个字节为TLS版本,后面46字节为随机数。
Client Key Exchange 数据包
MasterKey计算。即tls1_prf函数。其中secret参数为PremasterKey,label为字符串"master secret",random为64字节的random_c和random_s
tls1_prf函数定义
对称密钥的计算。还是调用tls1_prf函数,使用masterKey完成计算,randbytes还是64字节的random_c和random_s
tls_prf函数参数
tls_prf实现过程:
构造如下结构体:
typedef struct key char sha1[20]; char tag[15] or char tag[13]; //"server finished" or "client finished" or "master key" or "key expansion" char random_s[32]; char random_c[32]; };
2. 计算masterkey前24字节的md5
3. 计算16轮md5
4. 计算masterkey后24字节的md5
5. 计算16轮md5
static int tls1_prf( const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen ) { size_t nb, hs; size_t i, j, k; const unsigned char *S1, *S2; unsigned char *tmp; size_t tmp_len = 0; unsigned char h_i[20]; const mbedtls_md_info_t *md_info; mbedtls_md_context_t md_ctx; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; mbedtls_md_init( &md_ctx ); tmp_len = 20 + strlen( label ) + rlen; tmp = mbedtls_calloc( 1, tmp_len ); if( tmp == NULL ) { ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; goto exit; } hs = ( slen + 1 ) / 2; S1 = secret; S2 = secret + slen - hs; nb = strlen( label ); memcpy( tmp + 20, label, nb ); memcpy( tmp + 20 + nb, random, rlen ); nb += rlen; /* * First compute P_md5(secret,label+random)[0..dlen] */ if( ( md_info = mbedtls_md_info_from_type( MBEDTLS_MD_MD5 ) ) == NULL ) { ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; goto exit; } if( ( ret = mbedtls_md_setup( &md_ctx, md_info, 1 ) ) != 0 ) { goto exit; } mbedtls_md_hmac_starts( &md_ctx, S1, hs ); mbedtls_md_hmac_update( &md_ctx, tmp + 20, nb ); mbedtls_md_hmac_finish( &md_ctx, 4 + tmp ); for( i = 0; i < dlen; i += 16 ) { mbedtls_md_hmac_reset ( &md_ctx ); mbedtls_md_hmac_update( &md_ctx, 4 + tmp, 16 + nb ); mbedtls_md_hmac_finish( &md_ctx, h_i ); mbedtls_md_hmac_reset ( &md_ctx ); mbedtls_md_hmac_update( &md_ctx, 4 + tmp, 16 ); mbedtls_md_hmac_finish( &md_ctx, 4 + tmp ); k = ( i + 16 > dlen ) ? dlen % 16 : 16; for( j = 0; j < k; j++ ) dstbuf[i + j] = h_i[j]; } mbedtls_md_free( &md_ctx ); /* * XOR out with P_sha1(secret,label+random)[0..dlen] */ if( ( md_info = mbedtls_md_info_from_type( MBEDTLS_MD_SHA1 ) ) == NULL ) { ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; goto exit; } if( ( ret = mbedtls_md_setup( &md_ctx, md_info, 1 ) ) != 0 ) { goto exit; } mbedtls_md_hmac_starts( &md_ctx, S2, hs ); mbedtls_md_hmac_update( &md_ctx, tmp + 20, nb ); mbedtls_md_hmac_finish( &md_ctx, tmp ); for( i = 0; i < dlen; i += 20 ) { mbedtls_md_hmac_reset ( &md_ctx ); mbedtls_md_hmac_update( &md_ctx, tmp, 20 + nb ); mbedtls_md_hmac_finish( &md_ctx, h_i ); mbedtls_md_hmac_reset ( &md_ctx ); mbedtls_md_hmac_update( &md_ctx, tmp, 20 ); mbedtls_md_hmac_finish( &md_ctx, tmp ); k = ( i + 20 > dlen ) ? dlen % 20 : 20; for( j = 0; j < k; j++ ) dstbuf[i + j] = (unsigned char)( dstbuf[i + j] ^ h_i[j] ); } exit: mbedtls_md_free( &md_ctx ); mbedtls_platform_zeroize( tmp, tmp_len ); mbedtls_platform_zeroize( h_i, sizeof( h_i ) ); mbedtls_free( tmp ); return( ret ); }
虽然tls1_prf也调用了hmac初始化函数mbedtls_md_hmac_starts,但是hmac计算在函数mbedtls_ssl_cf_hmac中。下面会介绍。
本文以rsa密钥交换协议为例,简要描述一下加解密过程。而像更常用的ecdh等密钥交换算法,要双方交换premasterkey,只是比rsa多一个密钥交换的数据包,读者可自行查阅资料。
clientHello指定cipherSuite、随机数random_c(32B), ServerHello选择CipherSuite、随机数random_s(32B)。计算随机数的函数为mbedtls_ctr_drbg_random,而不是prf函数。
server发送证书、客户端收到后,客户端使用prf_tls1函数生成PreMasterKey(48字节,头两个字节为tls版本号,后面46字节为随机数),并使用服务器证书的公钥,加密后封装为Client Key Exchange消息传输。服务端接收到后用私钥解密该PreMasterKey,完成密钥交换。
客户端、服务器调用ssl_compute_master函数(服务端需要先用私钥解密Client Key Exchange包得到PreMasterKey),各自计算MasterKey(长度48字节)。参数包括三个随机数:ClientHello包中的random_c, ServerHello中的random_s,PreMasterKey。还包括一个字符串参数: "master secret"。
调用mbedtls_ssl_tls_prf函数产生对称密钥、mac密钥、IV向量,每个向量16字节。参数包括上一步的MasterKey,且字符串参数为:"key expansion"。对称密钥通过mbedtls_ssl_derive_keys函数实现,通过ssl_populate_transform函数安装。其中hmac密钥通过mbedtls_md_hmac_starts安装,hmac填充中opad和ipad填充也不同。对称密钥分为加密和解密两个,即:接收数据使用一个密钥解密,发送数据使用另外一个密钥加密;本例中RC4没有初始化向量,一般像AES、DES需要初始化向量;
发送Server/Client Finished消息,也即Crypted HandShake Message消息。
添加图片注释,不超过 140 字(可选)
RC4对称密钥的设置
添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)
六. 校验过程
下面简要描述一下校验过程。
校验是对整个通信中、不包括tls包头5字节之外,双方所有参与握手协议的数据包的校验。
ChangeCipherSpec总是和CryptedHandshakeMessage包一起发送,且ChangeCipherSpec总是在前面首先发送。
1. 第一个包从ClientHello开始,重要的事情说三遍:最后一个是客户端发送的Client Key Exchange包!最后一个包是客户端发送的Client Key Exchange包!最后一个包是客户端发送的Client Key Exchange包!当然,Client Key Exchange包以后的包也会校验,但是不参与CryptedHandshakeMessage包的校验,也就是握手中的mac校验不包含该数据包。并且,双方的CryptedHandshakeMessage包,都是需要解密后去掉sha1和padding计算校验的。
2. 校验既包括自己发送的数据包,也包括接收到的数据包。也就是说,既包括客户端也包括服务器的包,从ClientHello开始,到Client Key Exchange中间的所有数据包。在收到包的同时,计算包的校验值。
3. 客户端与服务器各自计算校验值。客户端先发送CryptedHandshakeMessage消息,服务器后发送CryptedHandshakeMessage消息。收到对方的CryptedHandshakeMessage后,先用对方的解密密钥解密数据包,然后取出对方的校验码(解密后的头部16字节。并且,客户端先向服务端发送CryptedHandshakeMessage,服务端校验成功后,才会向客户端发送CryptedHandshakeMessage,若是CryptedHandshakeMessage校验不通过,双方都会主动断开连接。计算时,客户端和服务端分别使用"client finish"和"server finish"字符串参与随机数的生成,可以防止重放攻击。
包中剩下20字节sha1是finish包内容,跟本地计算的校验比较,看是否相同。
具体实现算法是,得出全部参与校验的数据包的md5+sha1的36字节校验值,使用随机数生成函数tls1_prf生成12字节的随机数,调用时的参数如下:
第一个参数为master key;
第2个参数为长度字段,一般是48;
第3个参数是sender字段,关于sender字段,服务器端验证客户端时为 "client finished",客户端验证服务器端时为"server finished";
第4个参数即padbuf,为上述包计算出的MD5+sha1 36字节拼接值(如果选择sha256校验就为sha256,或者sha512);
第5个参数为padbuf长度,一般为36;
第6个单数buf,为sha256的计算输出;
第7个参数为输出长度,一般为12字节。
mac校验过程,实际就是计算参数校验的ClientHello、ServerHello、Certificate、ServerHelloDone、ClientKeyExchange这几个包的md5+sha1,然后使用prf函数生成12字节校验码,校验值长度一般为12字节。
添加图片注释,不超过 140 字(可选)
上图为对称密钥解密后的EncryptedHandshakeMessage包。
需要计算校验的字段为最前面的16字节(包括类型、长度字段);
最后12字节紫色字段为padding字段,填充方式为:剩余字段大小-1,此处为12-1=11
中间黄色的20字节是sha1,该字段由函数mbedtls_ssl_cf_hmac计算得来。需要的参数是最前面的16字节,并添加额外的13字节add_data字段(包括tls协议版本、tls类型等字段),并依据如下算法: HMAC(msg) = HASH(okey + HASH(ikey + msg)) 计算得出,具体细节看源代码。
因为需要16字节对齐,一般CryptedHandshakeMessage最小为48字节左右。
mac计算关键代码,其中len字段值为12
mac比较函数:
添加图片注释,不超过 140 字(可选)
综上所述,tls1_prf函数是计算PreMasterKey,MasterKey,校验三者时共同使用的函数。
对称加密算法大多是rc4、des、aes等流加密方式,前面的序列加解密会影响到后面序列的加解密,会导致key的变化;因此,每次加解密的长度都是16字节对齐的,而且每种算法的填充方式也不一样,这方面也需要注意。
七. HMAC计算过程
masterkey生成对称密钥后,调用ssl_populate_transform函数初始化对称密钥、iv、hmac密钥时,调用hmac初始化函数mbedtls_md_hmac_starts,密钥为hmac密钥,长度为20字节。
接下来的计算在mbedtls_ssl_cf_hmac中。首先用key值初始化okey和ikey。如果key长度大于64,则计算key的md5值当作key。
用0x5c填充okey,0x3c填充ikey
用key值异或okey和ikey,并计算ikey的md5值。
计算okey的md5
int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key, size_t keylen ) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; unsigned char sum[MBEDTLS_MD_MAX_SIZE]; unsigned char *ipad, *opad; size_t i; if( ctx == NULL || ctx->md_info == NULL || ctx->hmac_ctx == NULL ) return( MBEDTLS_ERR_MD_BAD_INPUT_DATA ); if( keylen > (size_t) ctx->md_info->block_size ) { if( ( ret = mbedtls_md_starts( ctx ) ) != 0 ) goto cleanup; if( ( ret = mbedtls_md_update( ctx, key, keylen ) ) != 0 ) goto cleanup; if( ( ret = mbedtls_md_finish( ctx, sum ) ) != 0 ) goto cleanup; keylen = ctx->md_info->size; key = sum; } ipad = (unsigned char *) ctx->hmac_ctx; opad = (unsigned char *) ctx->hmac_ctx + ctx->md_info->block_size; memset( ipad, 0x36, ctx->md_info->block_size ); memset( opad, 0x5C, ctx->md_info->block_size ); for( i = 0; i < keylen; i++ ) { ipad[i] = (unsigned char)( ipad[i] ^ key[i] ); opad[i] = (unsigned char)( opad[i] ^ key[i] ); } if( ( ret = mbedtls_md_starts( ctx ) ) != 0 ) goto cleanup; if( ( ret = mbedtls_md_update( ctx, ipad, ctx->md_info->block_size ) ) != 0 ) goto cleanup; cleanup: mbedtls_platform_zeroize( sum, sizeof( sum ) ); return( ret ); }
添加图片注释,不超过 140 字(可选)
对称密钥的偏移分布:
客户端:
mac加密密钥偏移0;
mac解密密钥偏移20;
key1密钥偏移40;
key2密钥偏移56;
加密iv偏移72;
解密iv偏移88;
服务端:
mac加密密钥偏移0;
mac解密密钥偏移20;
key1密钥偏移40;
key2密钥偏移56;
加密iv偏移72;
解密iv偏移88;
添加图片注释,不超过 140 字(可选)
/* * Compute HMAC of variable-length data with constant flow. * * Only works with MD-5, SHA-1, SHA-256 and SHA-384. * (Otherwise, computation of block_size needs to be adapted.) */ MBEDTLS_STATIC_TESTABLE int mbedtls_ssl_cf_hmac( mbedtls_md_context_t *ctx, const unsigned char *add_data, size_t add_data_len, const unsigned char *data, size_t data_len_secret, size_t min_data_len, size_t max_data_len, unsigned char *output ) { /* * This function breaks the HMAC abstraction and uses the md_clone() * extension to the MD API in order to get constant-flow behaviour. * * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means * concatenation, and okey/ikey are the XOR of the key with some fixed bit * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx. * * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to * minlen, then cloning the context, and for each byte up to maxlen * finishing up the hash computation, keeping only the correct result. * * Then we only need to compute HASH(okey + inner_hash) and we're done. */ const mbedtls_md_type_t md_alg = mbedtls_md_get_type( ctx->md_info ); /* TLS 1.0-1.2 only support SHA-384, SHA-256, SHA-1, MD-5, * all of which have the same block size except SHA-384. */ const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64; const unsigned char * const ikey = ctx->hmac_ctx; const unsigned char * const okey = ikey + block_size; const size_t hash_size = mbedtls_md_get_size( ctx->md_info ); unsigned char aux_out[MBEDTLS_MD_MAX_SIZE]; mbedtls_md_context_t aux; size_t offset; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; mbedtls_md_init( &aux ); #define MD_CHK( func_call ) \ do { \ ret = (func_call); \ if( ret != 0 ) \ goto cleanup; \ } while( 0 ) MD_CHK( mbedtls_md_setup( &aux, ctx->md_info, 0 ) ); /* After hmac_start() of hmac_reset(), ikey has already been hashed, * so we can start directly with the message */ MD_CHK( mbedtls_md_update( ctx, add_data, add_data_len ) ); MD_CHK( mbedtls_md_update( ctx, data, min_data_len ) ); /* For each possible length, compute the hash up to that point */ for( offset = min_data_len; offset <= max_data_len; offset++ ) { MD_CHK( mbedtls_md_clone( &aux, ctx ) ); MD_CHK( mbedtls_md_finish( &aux, aux_out ) ); /* Keep only the correct inner_hash in the output buffer */ mbedtls_ssl_cf_memcpy_if_eq( output, aux_out, hash_size, offset, data_len_secret ); if( offset < max_data_len ) MD_CHK( mbedtls_md_update( ctx, data + offset, 1 ) ); } /* Now compute HASH(okey + inner_hash) */ MD_CHK( mbedtls_md_starts( ctx ) ); MD_CHK( mbedtls_md_update( ctx, okey, block_size ) ); MD_CHK( mbedtls_md_update( ctx, output, hash_size ) ); MD_CHK( mbedtls_md_finish( ctx, output ) ); /* Done, get ready for next time */ MD_CHK( mbedtls_md_hmac_reset( ctx ) ); #undef MD_CHK cleanup: mbedtls_md_free( &aux ); return( ret ); }
上述代码中,可以看到,除了上述的ClientHello、ServerHello、Certificate、ServerHelloDone、ClientKeyExchange几个数据包外,包括CryptedHandshakeMessage头部16字节、extra_data字段的13字节,也参与了mac校验计算。
extra_data字段13字节内容,即8字节的nouce值加server/client finished包头5字节:
添加图片注释,不超过 140 字(可选)
另外,比较疑惑的一点是,为什么CryptedHandshakeMessage消息参加校验的长度是从tls记录层开始的28字节?而没有绕过中间的20字节sha1呢?通过调试mbedtls_ssl_cf_hmac函数,发现计算了16字节校验值和12字节的对端sha1,但是最后12字节的对端sha1被丢弃,所以只有前16字节校验码的下一步计算对比。
除此之外,还要啰嗦两句。除了CryptedHandshakeMessage消息外,其他的消息,比如ApplicationData,校验值都是放在数据包中最后的,比如放在最后的20字节。校验字段也是需要加密和解密的。每个ApplicationData包都需要计算校验,如果失败将会断开连接。
八. 参考链接
更多【编程技术-TLS协议全解析】相关视频教程:www.yxfzedu.com