SSL 证书和 Boost asio
SSL certificates and Boost asio
您好,我正在尝试通过 C++ 从使用 https 的网页下载内容。我从 Boost asio 示例中获取的非常基本的客户端程序编译并运行良好,但是当我使用 Google: www.google.co.uk/?gws_rd=ssl 测试它时,它给了我错误 "handshake: certificate verify failed"。
我认为这是因为 ctx.set_default_verify_paths() 不包含带有 Google 证书的路径(我在 Windows 上)。
我非常 SSL 新手,请您帮我解决以下问题:
1) 当我安装 openSSL 时,它是否在我的计算机上粘贴了一份受信任的证书颁发机构列表?如果是,什么会导致 Google 的证书无法验证?
2) 有没有说我不关心验证,无论如何都要继续连接,就像你在 firefox 中手动添加例外?我对连接是否可信不是特别感兴趣,因为我没有传输任何需要安全的东西。
任何一个的答案将不胜感激!
#include <iostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cout << argc;
std::cout << "Usage: sync_client <server> <path>\n";
std::cout << "Example:\n";
std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n";
return 1;
}
boost::asio::io_service io_service;
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query(argv[1], "https");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
ssl_socket socket(io_service, ctx);
boost::asio::connect(socket.lowest_layer(), endpoint_iterator);
socket.lowest_layer().set_option(tcp::no_delay(true));
// Perform SSL handshake and verify the remote host's
// certificate.
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::rfc2818_verification("host.name"));
socket.handshake(ssl_socket::client);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
request_stream << "Host: " << argv[1] << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
std::cout << "Invalid response\n";
return 1;
}
if (status_code != 200)
{
std::cout << "Response returned with status code " << status_code << "\n";
std::cout << status_message << "\n";
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
while (std::getline(response_stream, header) && header != "\r")
std::cout << header << "\n";
std::cout << "\n";
return 1;
}
//code to read the data goes here, which works fine for http pages
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
受信任的证书通常通过 OS、浏览器或单个软件包安装或更新。例如,在 *nix 世界中,证书通常可以通过 ca-certificates
包获得,并且证书安装到 boost::asio::ssl::context::set_default_verify_paths()
可以找到的位置。
证书验证失败,因为客户端正在尝试使用主机名验证 (rfc2818) 来验证对等方的证书,并且正在检查证书中的文字 "host.name"
,并且服务器的证书未将 "host.name"
列为名称。尝试更改:
socket.set_verify_callback(ssl::rfc2818_verification("host.name"));
至:
socket.set_verify_callback(ssl::rfc2818_verification(argv[1]));
要禁用对等验证,请提供 boost::asio::ssl::verify_none
to the boost::asio::ssl::stream::set_verify_mode()
:
socket.set_verify_mode(boost::asio::ssl::verify_none);
Boost.Asio 提供其他同行 verify_mode
s.
当对等验证失败时,向 boost::asio::ssl::stream::set_verify_callback
提供自定义回调以提供诊断信息会很有帮助。如文档中所述,处理程序签名必须是:
bool verify_callback(
bool preverified, // True if the certificate passed pre-verification.
verify_context& ctx // The peer certificate and other context.
);
这是打印证书主题名称的自定义仿函数:
///@brief Helper class that prints the current certificate's subject
/// name and the verification results.
template <typename Verifier>
class verbose_verification
{
public:
verbose_verification(Verifier verifier)
: verifier_(verifier)
{}
bool operator()(
bool preverified,
boost::asio::ssl::verify_context& ctx
)
{
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
bool verified = verifier_(preverified, ctx);
std::cout << "Verifying: " << subject_name << "\n"
"Verified: " << verified << std::endl;
return verified;
}
private:
Verifier verifier_;
};
///@brief Auxiliary function to make verbose_verification objects.
template <typename Verifier>
verbose_verification<Verifier>
make_verbose_verification(Verifier verifier)
{
return verbose_verification<Verifier>(verifier);
}
及其用法:
socket.set_verify_callback(make_verbose_verification(
boost::asio::ssl::rfc2818_verification(argv[1])));
在我的机器上,当使用它并且未调用 set_default_verify_paths()
时,我得到以下输出:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 0
Exception: handshake: certificate verify failed
并且当 set_default_verify_paths()
被调用时:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
Verified: 1
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 1
Verifying: /C=US/O=Google Inc/CN=Google Internet Authority G2
Verified: 1
Verifying: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com
Verified: 1
而当使用rfc2818_verification("host.name")
时:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
Verified: 1
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 1
Verifying: /C=US/O=Google Inc/CN=Google Internet Authority G2
Verified: 1
Verifying: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com
Verified: 0
Exception: handshake: certificate verify failed
您说过“在将此变量设置为指向 Mozilla 的 cacert.pem 文件后,一切都按照您的示例运行”。我可以知道是否可以使用“load_verify_file(//这里是CA证书路径和文件)”来验证您的证书吗?似乎比将环境变量点更改为单个 pem 文件更容易。
您好,我正在尝试通过 C++ 从使用 https 的网页下载内容。我从 Boost asio 示例中获取的非常基本的客户端程序编译并运行良好,但是当我使用 Google: www.google.co.uk/?gws_rd=ssl 测试它时,它给了我错误 "handshake: certificate verify failed"。
我认为这是因为 ctx.set_default_verify_paths() 不包含带有 Google 证书的路径(我在 Windows 上)。
我非常 SSL 新手,请您帮我解决以下问题:
1) 当我安装 openSSL 时,它是否在我的计算机上粘贴了一份受信任的证书颁发机构列表?如果是,什么会导致 Google 的证书无法验证?
2) 有没有说我不关心验证,无论如何都要继续连接,就像你在 firefox 中手动添加例外?我对连接是否可信不是特别感兴趣,因为我没有传输任何需要安全的东西。
任何一个的答案将不胜感激!
#include <iostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cout << argc;
std::cout << "Usage: sync_client <server> <path>\n";
std::cout << "Example:\n";
std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n";
return 1;
}
boost::asio::io_service io_service;
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query(argv[1], "https");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
ssl_socket socket(io_service, ctx);
boost::asio::connect(socket.lowest_layer(), endpoint_iterator);
socket.lowest_layer().set_option(tcp::no_delay(true));
// Perform SSL handshake and verify the remote host's
// certificate.
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::rfc2818_verification("host.name"));
socket.handshake(ssl_socket::client);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
request_stream << "Host: " << argv[1] << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
std::cout << "Invalid response\n";
return 1;
}
if (status_code != 200)
{
std::cout << "Response returned with status code " << status_code << "\n";
std::cout << status_message << "\n";
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
while (std::getline(response_stream, header) && header != "\r")
std::cout << header << "\n";
std::cout << "\n";
return 1;
}
//code to read the data goes here, which works fine for http pages
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
受信任的证书通常通过 OS、浏览器或单个软件包安装或更新。例如,在 *nix 世界中,证书通常可以通过 ca-certificates
包获得,并且证书安装到 boost::asio::ssl::context::set_default_verify_paths()
可以找到的位置。
证书验证失败,因为客户端正在尝试使用主机名验证 (rfc2818) 来验证对等方的证书,并且正在检查证书中的文字 "host.name"
,并且服务器的证书未将 "host.name"
列为名称。尝试更改:
socket.set_verify_callback(ssl::rfc2818_verification("host.name"));
至:
socket.set_verify_callback(ssl::rfc2818_verification(argv[1]));
要禁用对等验证,请提供 boost::asio::ssl::verify_none
to the boost::asio::ssl::stream::set_verify_mode()
:
socket.set_verify_mode(boost::asio::ssl::verify_none);
Boost.Asio 提供其他同行 verify_mode
s.
当对等验证失败时,向 boost::asio::ssl::stream::set_verify_callback
提供自定义回调以提供诊断信息会很有帮助。如文档中所述,处理程序签名必须是:
bool verify_callback(
bool preverified, // True if the certificate passed pre-verification.
verify_context& ctx // The peer certificate and other context.
);
这是打印证书主题名称的自定义仿函数:
///@brief Helper class that prints the current certificate's subject
/// name and the verification results.
template <typename Verifier>
class verbose_verification
{
public:
verbose_verification(Verifier verifier)
: verifier_(verifier)
{}
bool operator()(
bool preverified,
boost::asio::ssl::verify_context& ctx
)
{
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
bool verified = verifier_(preverified, ctx);
std::cout << "Verifying: " << subject_name << "\n"
"Verified: " << verified << std::endl;
return verified;
}
private:
Verifier verifier_;
};
///@brief Auxiliary function to make verbose_verification objects.
template <typename Verifier>
verbose_verification<Verifier>
make_verbose_verification(Verifier verifier)
{
return verbose_verification<Verifier>(verifier);
}
及其用法:
socket.set_verify_callback(make_verbose_verification(
boost::asio::ssl::rfc2818_verification(argv[1])));
在我的机器上,当使用它并且未调用 set_default_verify_paths()
时,我得到以下输出:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 0
Exception: handshake: certificate verify failed
并且当 set_default_verify_paths()
被调用时:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
Verified: 1
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 1
Verifying: /C=US/O=Google Inc/CN=Google Internet Authority G2
Verified: 1
Verifying: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com
Verified: 1
而当使用rfc2818_verification("host.name")
时:
$ ./a.out www.google.co.uk /?gws_rd=ssl
Verifying: /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
Verified: 1
Verifying: /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
Verified: 1
Verifying: /C=US/O=Google Inc/CN=Google Internet Authority G2
Verified: 1
Verifying: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com
Verified: 0
Exception: handshake: certificate verify failed
您说过“在将此变量设置为指向 Mozilla 的 cacert.pem 文件后,一切都按照您的示例运行”。我可以知道是否可以使用“load_verify_file(//这里是CA证书路径和文件)”来验证您的证书吗?似乎比将环境变量点更改为单个 pem 文件更容易。