本文全程干货,但稍微苦涩难懂,假如能耐心看完,我相信您一定能得到想要的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位密钥的最小化实现:53bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0j5%4y4U0b7J5y4e0M7@1y4b7`.`.
还有另一种更为流行的非对称加密算法即DSA,此算法的数学原理跟RSA类似,利用了大质数分解难题,但是另一种数论模型:寻找大质数的原根。作者将会继续写一篇DSA数学原理和实现的文章。
本人还有一篇关于RC4加密的说明文章:191K9s2c8@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
五. 加解密过程
rsa+rc4128套件加解密过程:e89K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2X3M7X3g2W2j5Y4g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5i4u0@1K9h3y4D9k6i4y4Q4x3V1k6F1k6i4c8%4L8%4u0C8i4K6u0r3x3e0V1J5x3K6x3H3i4K6u0W2K9s2c8E0L8l9`.`.
mbedtls中使用rsa,发现解密过程跟上文描述一致。上文用到的工具链接:f89K9s2c8@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包都需要计算校验,如果失败将会断开连接。
八. 参考链接
HTTPS 温故知新(五) —— TLS 中的密钥计算
国密(3)- 预主密钥/主密钥计算和Finished消息的加解密_已有客户端或服务端私钥情况下,能够解密或计算出tlcp中的预主密钥、主密钥和工作-CSDN博客
一文读懂https中密钥交换协议的原理及流程-腾讯云开发者社区-腾讯云
HTTPS网络流量解密方法探索系列(一) - FreeBuf网络安全行业门户
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-4-28 09:51
被satadrover编辑
,原因: