如何从代码中禁用服务器密钥交换?

How to disable Server Key Exchange from the code?

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

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

既然HTTP2数据包是TLS里面的应用层,Wireshark肯定知道如何解密;因此,我按照 here 的指南为 Wireshark 提供密钥。然而指南警告说,"If the server sends a ServerKeyExchange message, you will not be able to decrypt the data."不幸的是,我的 Wireshark 显示服务器密钥交换是由服务器发送的。

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

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

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

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

I would like to sniff the HTTP2 packet between the client-server using Wireshark.

为此,您通常会参考 Wireshark Wiki 上的 Wireshark and TLS


my Wireshark shows that the Server Key Exchange was sent by the server.

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

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

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

正如 page you link to 几乎正确解释的那样,对于 Wireshark(或任何其他被动窃听器)解密 SSL/TLS 握手 必须使用普通的 RSA keyexchange,而不是任何 Diffie-Hellman 选项(实际上是 DHE 或 ECDHE)或 "ephemeral" RSA。 ("almost" 是说 "RSA keys have been used to encrypt the data"。事实上,RSA 密钥交换使用服务器 RSA 密钥来加密 premaster secret,它经过广泛的转换以产生用于加密和验证 数据 的多个密钥。)

实际上 DHE 或 ECDHE 是问题所在。这些密码套件提供所谓的 Perfect Forward Secrecy 这意味着即使窃听者捕获并记录您的通信并且 后来 泄露服务器密钥,他们也无法使用它追溯解密记录的加密数据。尽管这些套件已经存在并且大部分都得到了很长时间的支持,post-斯诺登让更多的人(浏览器和其他应用程序和中间件制造商、系统管理员、网络管理员、首席信息安全官和审计员等)更加重视使用它们.

"Ephemeral RSA" 密钥交换仅用于 SSL/TLS 中一些长期过时的 "EXPORT" 密码套件。自从大约 1995 年以来,人们就已经知道这些是不安全的,而且大多数关心它们的人都禁止使用它们很长时间,或者至少优先考虑未损坏的套件而不是它们,尽管 FREAK 研究人员几个月前发现了大约 5% 的(我认为的示例)public 个服务器仍然支持他们(这使他们的攻击有效)。最后我检查了 OpenSSL 默认情况下仍然启用它们,但优先级最低。

答案:所以你应该配置你的客户端(这里curl--ciphers)或者你的服务器(调用SSL_[CTX_]set_cipher_list)--或者,如果你愿意的话——删除临时套件 DEFAULT:!EDH:!EECDH:!EXPORT 或直接要求 "plain" RSA RSA:!EXPORT:!eNULL:!SSLv2.

这带来了另一个选择:如果您只需要检查基于 OpenSSL 的客户端和您控制的服务器之间的流量,就像这里一样,您可以使用空加密套件.这些 设计 是不安全的,主要用于测试目的或可能非常严格的 legal/external 限制,这些限制在 1990 年代被认为是可以想象的,并且不受许多实现的支持;它们受 OpenSSL 支持,但默认情况下禁用,甚至在 ALL 中也未启用,您可能逻辑上希望包含它们。要获得它们,您必须明确指定 eNULL(更好的 eNULL+RSA)或非常违反直觉的 COMPLEMENTOFALL。然后不用解密就可以读取Wireshark中的数据了

如果您的目标是从 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 = '[=10=]';
}

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);
  ...
}