使用 Boost.Asio 和 OpenSSL 的 HTTPS 请求
我正在尝试读取 https://mtgox.com/api/ 处的股票代码0/data/ticker.php 来自我的 C++ 应用程序。 我使用 Boost.Asio 和 OpenSSL,因为该服务需要 HTTPS。
Boost 版本:1.47.0
OpenSSL:1.0.0d [2011 年 2 月 8 日] Win32
对于应用程序;我从 http:// /www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/example/ssl/client.cpp 开始并修改如下:
这是我想要连接的地方:
boost::asio::ip::tcp::resolver::query query("mtgox.com", "443");
我将验证设置为无,因为否则握手会失败。我不确定这是否是 mtgox 的问题,或者这个实现是否真的很严格,因为当我将证书打印到屏幕上时,它看起来是合法的(并且在访问股票页面时 chrome 没有问题)。
socket_.set_verify_mode(boost::asio::ssl::context::verify_none);
这是我发送的请求:(
std::stringstream request_;
request_ << "GET /api/0/data/ticker.php HTTP/1.1\r\n";
request_ << "Host: mtgox.com\r\n";
request_ << "Accept-Encoding: *\r\n";
request_ << "\r\n";
boost::asio::async_write(socket_, boost::asio::buffer(request_.str()), boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
完整代码:http://pastebin.com/zRTTqZVe)
我遇到出现以下错误:
Connection OK!
Verifying:
/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
Sending request:
GET /api/0/data/ticker.php HTTP 1.1
Host: mtgox.com
Accept-Encoding: *
Sending request OK!
Read failed: An existing connection was forcibly closed by the remote host
我的方向正确吗?错误消息并不能真正描述问题,我不知道我做错了哪一步。
更新: 我使用 cURL 来查看出了什么问题:(
curl --trace-ascii out.txt https://mtgox.com/api/0/data/ticker.php
完整输出:http://pastebin.com/Rzp0RnAK) 验证时失败。 当我使用“不安全”参数连接时
curl --trace-ascii out.txt -k https://mtgox.com/api/0/data/ticker.php
(完整输出:http://pastebin.com/JR43A7ux)
一切工作正常。
修复:
- 我修复了 HTTP 标头中的拼写错误
- ,添加了根证书并 重新打开 SSL 验证。
I'm trying to read the ticker symbol at https://mtgox.com/api/0/data/ticker.php from my C++ application.
I use Boost.Asio and OpenSSL because the service requires HTTPS.
Boost version: 1.47.0
OpenSSL: 1.0.0d [8 Feb 2011] Win32
For the application; I took the example from http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/example/ssl/client.cpp
to get started and modified it as follows:
This is where I want to connect to:
boost::asio::ip::tcp::resolver::query query("mtgox.com", "443");
I set verification to none because the handshake fails otherwise. I'm not sure if this is a problem with mtgox or that this implementation is really strict because when I print the certificate to the screen it looks legit (and chrome has no problem with it when visiting the ticker page).
socket_.set_verify_mode(boost::asio::ssl::context::verify_none);
This is the request I send:
std::stringstream request_;
request_ << "GET /api/0/data/ticker.php HTTP/1.1\r\n";
request_ << "Host: mtgox.com\r\n";
request_ << "Accept-Encoding: *\r\n";
request_ << "\r\n";
boost::asio::async_write(socket_, boost::asio::buffer(request_.str()), boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
(full code: http://pastebin.com/zRTTqZVe)
I run into the following error:
Connection OK!
Verifying:
/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
Sending request:
GET /api/0/data/ticker.php HTTP 1.1
Host: mtgox.com
Accept-Encoding: *
Sending request OK!
Read failed: An existing connection was forcibly closed by the remote host
Am I going in the right direction with this? The error message isn't really descriptive of the problem and I don't know which step I did wrong.
Update:
I used cURL to see what went wrong:
curl --trace-ascii out.txt https://mtgox.com/api/0/data/ticker.php
(full output: http://pastebin.com/Rzp0RnAK)
It fails during verification.
When I connect with the "unsafe" parameter
curl --trace-ascii out.txt -k https://mtgox.com/api/0/data/ticker.php
(full output: http://pastebin.com/JR43A7ux)
everything works fine.
Fix:
- I fixed the typo in the HTTP headers
- I added a root certificate and
turned SSL verification back on.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简而言之:
您发送“HTTP 1.1”而不是“HTTP/1.1”。这肯定足以让服务器拒绝您的请求。您的请求和 cURL 之间还有其他差异,您可能还需要更改这些参数 - 即使它们对我来说似乎有效。
与 Chrome 不同,OpenSSL 可能没有服务器使用的根证书,这就是验证失败的原因。
详细信息:
给定一个工作工具和一个不工作的工具,您应该始终比较正在发生的情况。这里你有 cURL 的输出和你的请求 - 比较它们显示出许多差异;通常,即使使用加密连接,您也可以使用 Wireshark 等功能强大的数据包嗅探器,它可以从数据包中解码尽可能多的信息。在这里,它可以看到服务器实际上发送的数据包较少(我期望);另一种可能性是您的客户端没有收到服务器发送的数据(比如因为客户端有一些错误)。
如果我理解正确的话,curl 只显示了您需要禁用验证的原因,对吧?该证书在 Chrome 上看起来对我来说也有效,但根证书颁发机构相当未知; curl提到了“CA cert”,即证书颁发机构的证书。根证书是可信的,因为它已经存在于客户端的证书数据库中 - 我认为 Chrome 可能拥有比 OpenSSL 更完整的数据库(cURL 和您的程序都使用它)。
In short:
You send "HTTP 1.1" instead of "HTTP/1.1". That's surely enough to make the server refuse your request. There are other differences between your request and cURL's, you might need to change those params as well - even if they seem valid to me.
Maybe OpenSSL does not have the root certificate used by the server, unlike Chrome, and that's why verification is failing.
Details:
Given a working and non-working tool, you should always compare what's happening. Here you have cURL's output and your request - comparing them showed a number of differences; usually, even with an encrypted connection, you can use a powerful packet sniffer like Wireshark, which decodes as much information from the packets as possible. Here it would allow to see that the server is actually sending less packets (I expect); another possibility would have been that your client was not getting the data sent by the server (say because the client had some bug).
If I understand correctly, curl showed only why you needed to disable verification, right? The certificate looks valid for me on chrome as well, but the root certification authority is quite unknown; curl mentions the "CA cert", i.e. the certificate of the certification authority. The root certificate is trusted because it is already present in a certificate DB on the client - I think that Chrome might have a more complete DB than OpenSSL (which is used by both cURL and your program).