如何从代码中禁用服务器密钥交换?
Posted
技术标签:
【中文标题】如何从代码中禁用服务器密钥交换?【英文标题】:How to disable Server Key Exchange from the code? 【发布时间】:2015-10-11 20:06:25 【问题描述】:我在 Ubuntu 15.04 上从 nghttp2 运行 HTTP2 服务器示例 (libevent-server.c)。我想使用 Wireshark 嗅探客户端-服务器之间的 HTTP2 数据包。
我没有使用任何网络浏览器作为客户端,所以关于如何在 Wireshark 中解释的 HTTP2 消息的提示 here 不适用于我的项目。我使用 libcurl。
由于 HTTP2 数据包是 TLS 内部的应用层,Wireshark 必须知道如何解密它;因此,我按照here 的指南为 Wireshark 提供了密钥。但是该指南警告说,“如果服务器发送 ServerKeyExchange 消息,您将无法解密数据。”不幸的是,我的 Wireshark 显示服务器密钥交换是由服务器发送的。
以下是libevent-server.c代码的sn-p:
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
我在将OPENSSL_config(NULL)
更改为OPENSSL_no_config()
后重新编译了代码,但仍然发送了服务器密钥交换。
如何禁用服务器密钥交换?
【问题讨论】:
【参考方案1】:如果您的目标是从 Wireshark 中的 OpenSSL 应用程序读取解密的应用程序数据,请注意 RSA 私钥是 not your only option,您还可以将映射传递给(预)主密钥。此功能不仅限于 SSL 客户端,即使在使用 NSS 的应用程序中通常会使用 SSLKEYLOGFILE
。
有关从 OpenSSL 客户端和服务器应用程序检索预主密钥的方法,请参阅this Sec.SE post(不需要更改应用程序)。如果你想在你的应用程序中嵌入这样的调试功能,那么你可以通过在握手完成时设置一个回调来更干净地做到这一点。示例:
// converts bytes to hex
void to_hex(unsigned char *dst, unsigned char *src, size_t len)
unsigned char hex[] = "0123456789abcdef";
unsigned i;
for (i = 0; i < len; i++)
unsigned char c = src[i];
*dst++ = hex[c >> 4];
*dst++ = hex[c & 0xf];
*dst = '\0';
static FILE *ssl_keylog;
void info_callback(const SSL *ssl, int where, int ret)
unsigned char rnd[SSL3_RANDOM_SIZE*2+1];
unsigned char pmk[SSL_MAX_MASTER_KEY_LENGTH*2+1];
// skip in case the session is invalid, or if it is not
// a Handshake Done notification.
if (!ssl || !ssl->session || !ssl->s3 || !(where & SSL_CB_HANDSHAKE_DONE))
return;
// convert bytes to hex
to_hex(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
to_hex(pmk, ssl->session->master_key, ssl->session->master_key_length);
// write to SSL keylog file
fprintf(keylog_file, "CLIENT_RANDOM %s %s\n", rnd, pmk);
static void run(const char *service, const char *key_file,
const char *cert_file)
SSL_CTX *ssl_ctx;
...
ssl_ctx = create_ssl_ctx(key_file, cert_file);
if (!keylog_file)
// open key log file if any.
keylog_file = fopen("premaster.txt", "a");
// register callback (for SSL objects, use SSL_set_info_callback instead)
SSL_CTX_set_info_callback(ssl_ctx, info_callback);
...
【讨论】:
【参考方案2】:正如page you link to 解释的几乎正确,对于 Wireshark(或任何其他被动窃听者)解密 SSL/TLS 握手必须使用普通的 RSA 密钥交换,而不是任何Diffie-Hellman 选项(实践中的 DHE 或 ECDHE)或“临时”RSA。 (“几乎”是它说“RSA 密钥已用于加密数据”。实际上 RSA keyexchange 使用服务器 RSA 密钥来加密 premaster secret,它经过广泛的转换以产生用于加密和验证数据的多个密钥。)
实际上 DHE 或 ECDHE 是问题所在。这些密码套件提供了所谓的Perfect Forward Secrecy,这意味着即使窃听者捕获并记录了您的通信并稍后泄露了服务器密钥,他们也无法使用它追溯解密记录的加密数据。尽管这些套件已经存在并且大部分时间都得到了支持,但在斯诺登之后,更多的人(浏览器和其他应用程序和中间件制造商、系统管理员、网络管理员、CISO 和审计员等)更加重视使用它们。
“Ephemeral RSA”密钥交换仅用于 SSL/TLS 中一些早已过时的“EXPORT”密码套件。自 1995 年左右以来,这些就被认为是不安全的,并且大多数关心它们的人长期禁止使用它们,或者至少优先考虑完好的套件而不是它们,尽管几个月前 FREAK 研究人员发现了大约 5%(我认为样本)仍然支持它们的公共服务器(这使它们的攻击起作用)。最后我检查了 OpenSSL 默认情况下仍然启用它们,但优先级最低。
答案: 所以你应该配置你的客户端(这里是curl
和--ciphers
)或你的服务器(调用SSL_[CTX_]set_cipher_list
)——或者如果你愿意,两者都配置——以删除临时套件DEFAULT:!EDH:!EECDH:!EXPORT
或直接要求“普通” RSA RSA:!EXPORT:!eNULL:!SSLv2
。
这带来了另一个选项:如果您只需要检查基于 OpenSSL 的客户端和您控制的服务器之间的流量,就像这里一样,您可以使用空加密套件。这些设计是不安全的,主要是出于测试目的或可能非常严格的法律/外部限制,这些限制在 1990 年代被认为是可以想象的,并且不受许多实现的支持;它们受 OpenSSL 支持,但默认禁用,即使在 ALL
中也未启用,您可能在逻辑上希望包含它们。要获得它们,您必须明确指定eNULL
(更好的是eNULL+RSA
)或非常违反直觉的COMPLEMENTOFALL
。然后你可以在Wireshark中读取数据而无需解密。
【讨论】:
在服务器代码上添加了一行SSL_CTX_set_cipher_list(ctx, "DEFAULT:!EDH:!EECDH:!EXPORT")
,则ServerKeyExchange不是服务器发送的。我现在可以看到 HTTP2 流量。 The guide 暗示通配符端口 0 可用于配置 Wireshark,但这对我不起作用。我必须专门输入端口号(443)。【参考方案3】:
我想使用 Wireshark 嗅探客户端-服务器之间的 HTTP2 数据包。
为此,您通常参考 Wireshark Wiki 上的Wireshark and TLS。
我的 Wireshark 显示服务器密钥交换是由服务器发送的。
我相信始终会发送 Serer Key Exchange 消息。我认为您能做的最好的事情就是在 Server Hello 中抑制 Server Certificate 消息。通过启用only匿名协议来做到这一点。匿名协议放弃服务器身份验证,并允许您拦截流量。
我相信您要查找的密码字符串是 "ADH:AECDH"
或 "aNULL"
(通常,您明确声明 "!aNULL"
)。它需要在客户端和服务器上都可用。最后,您可以在ciphers(3)
OpenSSL 手册页获取令牌列表。
大多数客户端断然拒绝匿名协议,因此可能很难真正进入该配置(并使一切正常)。
【讨论】:
ServerKeyExchange 并不总是发送。但是,它发送给所有匿名(aNULL)套件,正如你所说,这无论如何都是一个非常糟糕的主意。以上是关于如何从代码中禁用服务器密钥交换?的主要内容,如果未能解决你的问题,请参考以下文章
使用 RSACryptoServiceProvider,如何将密钥交换密钥和签名密钥存储在同一个密钥容器中?
Diffie-Hellman 密钥交换&ElGamal协议的安全密钥交换