如何从代码中禁用Server Key Exchange?

时间:2015-07-22 05:48:39

标签: curl openssl http2 nghttp2

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

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

由于HTTP2数据包是TLS内部的应用程序层,因此Wireshark必须知道如何解密它;因此,我按照here的指南为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()后重新编译了代码,但仍然发送了服务器密钥交换。

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

3 个答案:

答案 0 :(得分:2)

由于page you link to几乎正确解释,对于Wireshark(或任何其他被动窃听者)解密,SSL / TLS握手必须使用普通RSA密钥交换,不是任何Diffie-Hellman选项(DHE或ECDHE在实践中)或"短暂的" RSA。 ("几乎"它说" RSA密钥已用于加密数据"。实际上RSA密钥交换使用服务器RSA密钥加密预主密钥,通过广泛的转换生成用于加密和验证数据的多个密钥。)

在实践中,DHE或ECDHE是问题所在。这些密码套件提供了所谓的Perfect Forward Secrecy,这意味着即使窃听者捕获并记录您的通信并且稍后会破坏服务器密钥,他们也无法使用它来追溯解密已记录的加密数据。虽然这些套件已经存在并且大部分时间都得到了支持,但是后Snowden更多人(浏览器和其他应用程序和中间件制造商,系统管理员,网络管理员,CISO和审计员等)更加重视使用它们。

"短暂的RSA" keyexchange仅用于一些长期过时的" EXPORT" SSL / TLS中的密码套件。自1995年左右以来,人们已经知道这些是不安全的,并且大多数关心的人已经禁止使用它们很长时间,或者至少优先考虑不破坏套房,尽管FREAK研究人员几个月前发现了5%(我认为是公共服务器的样本仍然支持它们(使他们的攻击能够工作)。最后我检查过OpenSSL默认情况下仍会启用它们,但优先级最低。

答案:因此,您应该配置您的客户端(此处为curl --ciphers)或您的服务器(致电SSL_[CTX_]set_cipher_list) - 或两者兼而有之 - 删除短暂的套房DEFAULT:!EDH:!EECDH:!EXPORT或直接要求"普通" RSA RSA:!EXPORT:!eNULL:!SSLv2

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

答案 1 :(得分: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 :(得分:0)

  

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

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

  

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

我相信始终会发送Serer Key Exchange消息。我认为您可以做的最好的事情是在服务器Hello中禁止服务器证书消息。通过启用 匿名协议来实现此目的。匿名协议放弃服务器身份验证,并允许您拦截流量。

我认为您要查找的密码字符串是 <bean id="websocket" class="co.syntx.example.websocket.handler.WebsocketEndPoint"/> <websocket:handlers> <websocket:mapping path="/websocket" handler="websocket"/> <websocket:handshake-interceptors> <bean class="co.syntx.example.websocket.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> "ADH:AECDH" (通常,您明确声明 "aNULL" )。它需要在客户端和服务器上都可用。最后,您可以在ciphers(3) OpenSSL手册页中获取令牌列表。

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