【问题标题】:Handshake Failure After Client Hello客户端 Hello 后握手失败
【发布时间】:2016-11-19 13:36:49
【问题描述】:

我用 C++ 和 OpenSSL 开发了一个应用程序来监控 MySQL 数据包。我的应用程序充当代理。在没有 SSL 连接的情况下一切正常,但是当我使用 SSL 连接时,它在收到 Client Hello 后正好面临“握手失败”。

我使用 openssl 开发了一个 ssl 服务器并使其阻塞并在正确的时间等待接受 ssl 连接。

Wireshark 截图

Openssl 输出:

140167999850080:错误:1408A0C1:SSL 例程:SSL3_GET_CLIENT_HELLO:没有共享密码:s3_srvr.c:1352:

140167999850080:错误:1408A0C1:SSL 例程:SSL3_GET_CLIENT_HELLO:没有共享密码:s3_srvr.c:1352:


已编辑:这是在检测到 ssl 连接选项打开时与客户端交互的代码的一部分。收到登录请求包后,此代码将运行。

void mysqlNegotiation(X509* cert, EVP_PKEY* key, int fd) {
    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();

    SSL_CTX* context = SSL_CTX_new(TLSv1_server_method());

    SSL_CTX_use_certificate(context, cert);
    SSL_CTX_use_PrivateKey(context, key);
    SSL_CTX_check_private_key(context);

    SSL_CTX_set_ecdh_auto(context, 1);
    SSL_CTX_set_verify(context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
    SSL_CTX_set_verify_depth(context, 4);
    SSL_CTX_load_verify_locations(context, "ca-cert.pem", "/etc/mysql/");

    SSL* ssl = SSL_new(context);
    SSL_set_fd(ssl, fd);

    int r = SSL_accept(ssl);
    if (r != 1) {
        ERR_print_errors_fp(stderr);
        int err_SSL_get_error = SSL_get_error(ssl, r);

        switch (err_SSL_get_error) {
            case SSL_ERROR_NONE:
                printf("%d", 0);
                break;
            case SSL_ERROR_SSL:
                printf("%d", 1);
                break;
            case SSL_ERROR_WANT_READ:
                printf("%d", 2);
                break;
            case SSL_ERROR_WANT_WRITE:
                printf("%d", 3);
                break;
            default:
                printf("%d", -1);
                break;
        }
    }
}

【问题讨论】:

  • 我怀疑这段代码是否正确:` SSL_CTX* context = = SSL_CTX_new(TLSv1_server_method()); `。它甚至可能无法编译。请制作一个可用于重现问题的实际工作示例。
  • 您是否查看过客户端提供了哪些密码?也许进行数据包捕获?例如,如果客户端只提供 DH 密码,这将失败,因为您没有设置任何 DH(不是 ECDH)参数。
  • 源代码修改!是的,两者都提供相同的密码列表
  • 您如何检查服务器提供的密码?您是如何检查客户的?
  • 您的代理使用什么类型的证书,代理两端启用的密码套件列表是什么?这也强制客户端使用客户端证书 (IIRC):SSL_VERIFY_FAIL_IF_NO_PEER_CERT。最后,你看过 OpenSSL wiki 上的 TLS Client 吗?

标签: c++ mysql ssl openssl


【解决方案1】:

此错误可能有多种原因,如前所述,这表明客户端和服务器未实现可协商的通用密码。一些原因是:

  1. TLS 协议级别不匹配。例如,您可能使用SSLv23_method 通过SSL_CTX_set_options() 设置SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1,表示仅支持 TLS 1.2 或更高版本,但客户端不支持高于 TLS 1.1 的任何内容。这只是一个例子,协议不匹配也可能以许多其他方式发生。

  2. 通过 SSL_CTX_set_cipher_list() 配置的 SSL 密码列表不包括对等方支持的任何密码。

  3. SSL 上下文未使用 DH 参数初始化,因此不支持 forward secrecy,对等方仅支持支持前向保密的密码。

也可能有其他原因。如果不检查客户端和服务器的配置,就不可能提供权威的答案。由于只有您可以访问客户端和服务器的代码,因此由您自己决定。这里唯一真正的答案是:找到另一个与您的客户端一起工作的服务器代码,并查看它的代码以找出它与您的代码的不同之处。

【讨论】:

  • 客户端和服务器都使用 TLSv1 并且都使用相同的密码列表。我还初始化了 DH 参数,但它以相同的方式进行。让我感到困惑的一件事是为什么在客户打招呼之后和任何谈判发生之前立即发生?
  • 协商失败返回此错误信息。协商阶段没有单独的错误消息。 “没有共享密码”表示密码协商失败。
【解决方案2】:

一个常见的错误是您在中间应用程序中的人没有设置任何证书,这意味着无法使用需要身份验证的密码,即可能是客户端提供的所有密码。然后这会导致“无共享密码”警报作为对 ClientHello 的响应。当然这只是猜测,因为中间代理的人的代码是未知的。

如果这种推测是错误的,我建议您提供更多信息,即create a Minimal, Complete, and Verifiable example,以便其他人能够真正看到您的设置细节,甚至可能重现您的问题。

【讨论】:

  • @Heil_Me:设置证书和私钥没有错误检查,所以很可能这些操作失败了。
  • 在实现的原始代码错误检查中,这段代码只是一个简单的代码来显示我在原始代码中所做的事情。由于我的工作政策,我不能完全分享原始代码。
  • @Heil_Me:如果客户端支持 ECDH 密钥交换或 RSA 密钥交换(如果证书是 RSA),则代码看起来不错。如果客户端只支持 DH 密钥交换,它将失败。您是否查看了客户端支持的密码?此外,虽然共享至少一些代码很好,但可能是由于剥离与工作相关的内容,错误也消失了。因此,如果您可以共享足够多的代码以实际用于重现问题,而不仅仅是您认为可能相关的部分,那就更好了。
猜你喜欢
  • 2017-10-09
  • 2015-12-08
  • 1970-01-01
  • 2016-05-04
  • 1970-01-01
  • 2015-08-02
  • 2020-03-19
  • 2020-02-14
  • 2016-01-18
相关资源
最近更新 更多