简体   繁体   English

为什么来自OpenSSL的BIO_do_connect()无法与GDAX(又名cloudflare)沙箱配合使用?

[英]Why would BIO_do_connect() from OpenSSL not work right with GDAX (a.k.a. cloudflare) sandbox?

I wrote some software in C++ and I'm trying to get the GDAX /products list for now (mainly as a test at this time.) 我用C ++写了一些软件,现在我想获取GDAX /products列表(目前主要是作为测试)。

UPDATE: I wanted to add that the connection is actually to cloudflare and not directly to GDAX. 更新:我想补充一下,该连接实际上是到cloudflare的,而不是直接到GDAX的。 So it is likely a problem with cloudflare and not directly GDAX servers. 因此,可能是cloudflare而不是直接与GDAX服务器有关的问题。

Only, the BIO_do_connect() function returns -1 each time. BIO_do_connect()函数每次都返回-1。 It does not give me much to go on with it. 它并没有给我带来太多好处。 I write the following in my log. 我在日志中写了以下内容。 So the main info is the error occurs on line 794 of s23_clnt.c ... 所以主要的信息是错误发生在s23_clnt.c第794 s23_clnt.c ...

OpenSSL: [336031996/20|119|252]:[]:[]:[]:[s23_clnt.c]:[794]:[(no details)] OpenSSL:[336031996/20 | 119 | 252]:[]:[]:[]:[s23_clnt.c]:[794]:[((无详细信息)]

I can tell that this means the TCP connection itself happens, but somehow it's not able to get an acceptable secure connection. 我可以说这意味着TCP连接本身发生了,但是不知何故它无法获得可接受的安全连接。 I've see similar behaviors before when a machine would only some old encryption methods. 在机器仅使用一些旧的加密方法之前,我已经看到类似的行为。 But I checked with nmap and the connection definitely supports TLS 1.2. 但是我检查了nmap,连接肯定支持TLS 1.2。 I ran the following command and got: 我运行以下命令并得到:

nmap --script ssl-enum-ciphers api-public.sandbox.gdax.com

And I get the following output which proves that port 443 is open and has all the necessary encryption schemes necessary. 我得到以下输出,证明端口443是开放的,并且具有所有必要的必要加密方案。

Starting Nmap 7.01 ( https://nmap.org ) at 2018-03-24 21:57 PDT
Nmap scan report for api-public.sandbox.gdax.com (104.28.30.142)
Host is up (0.016s latency).
Other addresses for api-public.sandbox.gdax.com (not scanned): 104.28.31.142
Not shown: 996 filtered ports
PORT     STATE SERVICE
80/tcp   open  http
443/tcp  open  https
| ssl-enum-ciphers: 
|   TLSv1.0: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.1: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|_  least strength: C
8080/tcp open  http-proxy
8443/tcp open  https-alt
| ssl-enum-ciphers: 
|   TLSv1.0: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.1: 
|     ciphers: 
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|_  least strength: C

Nmap done: 1 IP address (1 host up) scanned in 8.07 seconds

Now, I tested my code against the normal REST API address ( api.gdax.com ) and my own website ( www.m2osw.com ) and the encryption part works just fine. 现在,我针对正常的REST API地址( api.gdax.com )和我自己的网站( www.m2osw.com )测试了我的代码,加密部分工作正常。 I really don't see what I would be doing wrong that it would fail like that with the sandbox URL ( api-public.sandbox.gdax.com ) unless its SSL setup is weird. 我真的看不到我会做错什么,除非它的SSL设置很奇怪,否则沙盒URL( api-public.sandbox.gdax.com )会失败。

Note that when I try to connect to port 80 (which is wrong, I know), it works as expected. 请注意,当我尝试连接到端口80(我知道这是错误的)时,它会按预期工作。 That is, I get a 301 with a Location to the same address with protocol HTTPS. 也就是说,我得到了一个301,其位置与协议HTTPS的地址相同。

Anyone has had some problem with connecting to the sandbox? 有人在连接到沙箱时遇到了问题吗?

There are all the functions that get called. 有所有被调用的函数。 The full implementation is available on github in the libsnapwebsites around line 1111 at this point (bio_client constructor). 此时,可以在1111行附近的libsnapwebsites中的 github上找到完整的实现(bio_client构造函数)。

// called once on initialization
SSL_library_init();
ERR_load_crypto_strings();
ERR_load_SSL_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
crypto_thread_setup();

// call each time we connect
SSL_CTX * ssl_ctx = SSL_CTX_new(SSLv23_client_method();
SSL_CTX_set_verify_depth(ssl_ctx, 4);
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_COMPRESSION);

// just in case I tried with "ALL", but no difference
//SSL_CTX_set_cipher_list(ssl_ctx, "ALL");
SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");

SSL_CTX_load_verify_locations(ssl_ctx, NULL, "/etc/ssl/certs");
BIO * bio = BIO_new_ssl_connect(ssl_ctx);
SSL * ssl(nullptr);
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(bio, const_cast<char *>(addr.c_str()));
BIO_set_conn_int_port(bio, &port);

int const cr(BIO_do_connect(bio));
// here cr == -1 when I use api-public.sandbox.gdax.com

Again, this code works find and cr > 0 if I use api.gdax.com so I'm really at a loss at this point!? 同样,如果我使用api.gdax.com ,则此代码可以正常工作,并且cr > 0 ,所以我现在真的很茫然! And I know that the TCP connection itself happens since it gets in that s23_clnt.c which is after that part happens. 而且我知道TCP连接本身会发生,因为它进入了那部分发生之后的s23_clnt.c。

Okay, I spent the whole day (okay about half a day) working on this one comparing my code with libcurl 's code which also uses the SSL_CTX and SSL structures of OpenSSL. 好的,我花了整整一天的时间(大约半天),将我的代码与libcurl的代码进行了比较,后者也使用OpenSSL的SSL_CTXSSL结构。 The code looks very much the same... except that the libcurl version includes this: 该代码看起来几乎相同……除了libcurl版本包括以下内容:

[...]
switch(data->set.ssl.version) {
case CURL_SSLVERSION_DEFAULT:
  [...]
  use_sni(TRUE);
  break;

[...]
if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
   (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
   sni &&
   !SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
  infof(data, "WARNING: failed to configure server name indication (SNI) "
        "TLS extension\n");
[...]

As we can see, they have something called SNI and if true they set the TLS extension called Hostname . 如我们所见,它们有一个名为SNI的东西,如果为true,则设置名为Hostname的TLS扩展。 If that Hostname parameter is not included in the SSL HELLO message, then the GDAX server (or most probably the cloudflare one) refuses the connection immediately. 如果该主机名参数未包含在SSL HELLO消息中,则GDAX服务器(或者很可能是cloudflare服务器)立即拒绝连接。

So, on my end I will be forcing the SNI (Server Name Identification) and that way it is likely to work on way more servers. 因此,最终,我将强制使用SNI(服务器名称标识),这样一来,它很可能可以在更多服务器上使用。 libcurl allows to not include it, but it looks like you should always have it. libcurl允许不包含它,但是看起来您应该始终拥有它。 It shouldn't hurt, at least. 至少不应该伤害。

BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
SSL_set_tlsext_host_name(ssl, const_cast<char *>(addr.c_str()));
BIO_set_conn_hostname(bio, const_cast<char *>(addr.c_str()));

Note that the SSL_set_tlsext_host_name() function must be given the correct hostname, not an IPv4 or IPv6 address. 请注意,必须给SSL_set_tlsext_host_name()函数正确的主机名,而不是IPv4或IPv6地址。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM