如何从OpenSSL的SSL会话中提取会话密钥及相关参数
OpenSSL TLS 会话密钥类参数提取方案
握手完成(SSL_accept()/SSL_connect()返回成功)且持有有效SSL句柄时,你需要的所有参数都可以通过OpenSSL提供的能力获取,具体方式如下:
- master key(主密钥)
直接调用SSL_SESSION_get_master_key接口即可获取,你代码里注释的调用逻辑是对的,注意主密钥是二进制数据,不能直接用%s格式化打印,需要逐字节转十六进制输出。参考调用代码:
TLS1.2及更早版本主密钥固定为48字节,TLS1.3主密钥长度根据协商的密码套件存在差异,可以先给第二个参数传NULL拿到实际长度后再分配缓冲区。SSL_SESSION *sess = SSL_get_session(ssl); unsigned char master_key[128] = {0}; size_t mk_len = SSL_SESSION_get_master_key(sess, master_key, sizeof(master_key)); - client random、server random(客户端/服务端随机数)
两个随机数长度均为32字节,直接调用OpenSSL对外暴露的专用接口即可获取:unsigned char client_random[32] = {0}; unsigned char server_random[32] = {0}; SSL_get_client_random(ssl, client_random, sizeof(client_random)); SSL_get_server_random(ssl, server_random, sizeof(server_random)); - client write key、server write key(客户端/服务端写密钥)
OpenSSL没有对外暴露直接获取这两个工作密钥的公开API,有两种可行实现路径:- 自行按协议规范计算:拿到前述主密钥、两端随机数后,按照当前连接协商的TLS版本对应的PRF(伪随机函数)规则,自行扩展计算出两端的写密钥、IV、MAC校验密钥。TLS1.2的PRF规则对应标准文档RFC5246,TLS1.3对应RFC8446。
- 注册信息回调抓取:通过
SSL_CTX_set_info_callback给SSL上下文注册握手过程回调,在密钥派生完成的回调事件触发时,直接从SSL连接的内部安全参数结构体中读取已经计算完成的写密钥。这种方式不需要自行实现PRF逻辑,适配所有TLS版本,注意不同OpenSSL大版本的内部结构体成员偏移有差异,生产环境使用需要做版本兼容判断。
场景适配提示
针对你需要解密原始报文做畸形包检测的需求,有两个更简便的实现方式,不需要手动提取密钥后自行解密:
- 调用
SSL_set_msg_callback接口注册报文回调,OpenSSL会在处理每个TLS报文(包括握手报文、应用数据报文)时,把明文载荷直接传入回调函数,不需要你从socket端口读取原始密文再解密。- 如果必须从socket层抓包解密,注意TLS1.3支持连接中途密钥更新,一旦触发密钥更新,之前拿到的写密钥会失效,需要同步捕获密钥更新事件重新获取新密钥,否则后续报文无法正常解密。
内容的提问来源于stack exchange,提问作者user1897937
相关产品推荐
相关产品推荐

