3

我在 Ubuntu 15.04 上从nghttp2运行 HTTP2 服务器示例 (libevent-server.c) 。我想使用 Wireshark 嗅探客户端-服务器之间的 HTTP2 数据包。

我没有使用任何网络浏览器作为客户端,所以这里解释的如何在 Wireshark 中读取 HTTP2 消息的提示不适用于我的项目。我使用 libcurl。

由于 HTTP2 数据包是 TLS 内部的应用层,Wireshark 必须知道如何解密它;因此,我按照这里的指南为 Wireshark 提供了密钥。但是该指南警告说,“如果服务器发送 ServerKeyExchange 消息,您将无法解密数据。” 不幸的是,我的 Wireshark 显示服务器密钥交换是由服务器发送的。

以下是 libevent-server.c 代码片段:

 SSL_load_error_strings();
 SSL_library_init();
 OpenSSL_add_all_algorithms();
 OPENSSL_config(NULL);

OPENSSL_config(NULL)更改为后我重新编译了代码,OPENSSL_no_config()但仍然发送了服务器密钥交换。

如何禁用服务器密钥交换?

4

3 回答 3

2

正如您链接到的页面几乎正确解释的那样,Wireshark(或任何其他被动窃听者)解密SSL/TLS 握手必须使用普通的 RSA keyexchange,而不是任何 Diffie-Hellman 选项(实际上是 DHE 或 ECDHE)或“临时“ RSA。(“几乎”是它说“RSA 密钥已用于加密数据”。实际上 RSA keyexchange 使用服务器 RSA 密钥来加密premaster secret,它经过广泛的转换以产生用于加密和验证数据。)

在实践中,DHE 或 ECDHE 是问题所在。这些密码套件提供了所谓的完美前向保密,这意味着即使窃听者捕获并记录了您的通信并随后破坏了服务器密钥,他们也无法使用它追溯解密记录的加密数据。尽管这些套件已经存在并且大部分时间都得到了支持,但在斯诺登之后,更多的人(浏览器和其他应用程序和中间件制造商、系统管理员、网络管理员、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(better eNULL+RSA) 或非常违反直觉的COMPLEMENTOFALL。然后你可以在不解密的情况下读取 Wireshark 中的数据。

于 2015-07-22T11:54:47.850 回答
1

如果您的目标是从 Wireshark 中的 OpenSSL 应用程序中读取解密的应用程序数据,请注意 RSA 私钥不是您唯一的选择,您还可以将映射传递给(预)主密钥。此功能不仅限于 SSL 客户端,即使在SSLKEYLOGFILE使用 NSS 的应用程序中也很常见。

有关从 OpenSSL 客户端和服务器应用程序检索预主密钥的方法,请参阅此 Sec.SE 帖子(不需要更改应用程序)。如果你想在你的应用程序中嵌入这样的调试功能,那么你可以通过在握手完成时设置一个回调来更干净地做到这一点。例子:

// 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);
  ...
}
于 2015-07-23T16:56:47.693 回答
0

我想使用 Wireshark 嗅探客户端-服务器之间的 HTTP2 数据包。

为此,您通常参考Wireshark Wiki 上的Wireshark 和 TLS 。


我的 Wireshark 显示服务器密钥交换是由服务器发送的。

我相信始终发送 Serer Key Exchange 消息。我认为您能做的最好的事情就是在 Server Hello 中抑制 Server Certificate 消息。通过启用匿名协议来做到这一点。匿名协议放弃服务器身份验证,并允许您拦截流量。

我相信您正在寻找的密码字符串是"ADH:AECDH"or "aNULL"(通常,您明确声明"!aNULL")。它需要在客户端和服务器上都可用。ciphers(3)最后,您可以在OpenSSL 手册页中获取令牌列表。

大多数客户端断然拒绝匿名协议,因此可能很难真正进入该配置(并使一切正常)。

于 2015-07-22T10:24:13.280 回答