2015-02-01 38 views
8

Xin chào, tôi đang cố gắng tải xuống nội dung từ trang web sử dụng https qua C++. Chương trình khách hàng cơ bản của tôi được lấy từ các ví dụ Boost asio biên dịch và chạy tốt, nhưng khi tôi thử nghiệm nó với Google: www.google.co.uk/?gws_rd=ssl, nó cho tôi lỗi "bắt tay: xác minh chứng chỉ không thành công" .Chứng chỉ SSL và Tăng asio

Tôi nghĩ điều này là do ctx.set_default_verify_paths() không chứa đường dẫn có chứng chỉ cho Google (Tôi đang sử dụng Windows).

Tôi rất mới để SSL, xin vui lòng có thể bạn giúp tôi với các câu hỏi sau:

1) Khi tôi cài đặt OpenSSL, sao nó dính vào một danh sách các cơ quan chức năng xác nhận đáng tin cậy trên máy tính của tôi? Nếu có, điều gì sẽ khiến chứng chỉ của Google không được xác minh?

2) Có cách nào nói rằng tôi không quan tâm đến việc xác minh, hãy tiếp tục kết nối, như khi bạn thêm ngoại lệ theo cách thủ công trong firefox? Tôi không đặc biệt quan tâm đến việc liệu kết nối có đáng tin cậy hay không vì tôi không truyền tải bất cứ thứ gì cần được bảo mật.

Câu trả lời cho một trong hai sẽ được đánh giá rất nhiều!

#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; 
} 

Trả lời

7

Chứng chỉ tin cậy thường được cài đặt hoặc cập nhật qua hệ điều hành, trình duyệt hoặc gói cá nhân. Ví dụ: trong thế giới * nix, các chứng chỉ thường có sẵn trong gói ca-certificates và chứng chỉ được cài đặt cho các vị trí mà boost::asio::ssl::context::set_default_verify_paths() sẽ tìm thấy.

Việc xác minh chứng nhận là không bởi vì các khách hàng đang nỗ lực để xác minh giấy chứng nhận của ngang hàng với xác minh hostname (rfc2818), và được kiểm tra cho literal "host.name" là trong giấy chứng nhận, chứng chỉ của máy chủ không liệt kê "host.name" như một Tên. Hãy thử thay đổi:

socket.set_verify_callback(ssl::rfc2818_verification("host.name")); 

tới:

socket.set_verify_callback(ssl::rfc2818_verification(argv[1])); 

Để vô hiệu hóa xác minh ngang hàng, cung cấp boost::asio::ssl::verify_none đến boost::asio::ssl::stream::set_verify_mode():

socket.set_verify_mode(boost::asio::ssl::verify_none); 

Boost.Asio cung cấp ngang hàng khác verify_mode s.


Khi xác minh ngang hàng không thành công, có thể hữu ích khi cung cấp gọi lại tùy chỉnh tới boost::asio::ssl::stream::set_verify_callback cung cấp thông tin chẩn đoán.Như đã đề cập trong tài liệu, chữ ký xử lý phải là:

bool verify_callback(
    bool preverified, // True if the certificate passed pre-verification. 
    verify_context& ctx // The peer certificate and other context. 
); 

Đây là một functor tùy chỉnh mà in tên chủ đề chứng chỉ:

///@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); 
} 

Và sử dụng của nó:

socket.set_verify_callback(make_verbose_verification(
    boost::asio::ssl::rfc2818_verification(argv[1]))); 

On máy của tôi, khi sử dụng nó và set_default_verify_paths() không được gọi, tôi nhận được kết quả sau:

$ ./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 

Và khi set_default_verify_paths() được gọi:

$ ./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 

Và khi rfc2818_verification("host.name") được sử dụng:

$ ./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 
+0

Xin chào cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn. Khi tôi đưa vào chức năng chẩn đoán, tôi nhận được kết quả đầu tiên mà bạn tìm thấy: 'Xác minh:/C = US/O = GeoTrust Inc./CN=GeoTrust Toàn cầu CA Đã xác minh: 0 Ngoại lệ: bắt tay: chứng nhận xác minh thất bại ' ... sau khi tôi xóa lỗi "host.name". Tôi có nghĩ rằng điều này cho thấy set_default_verify_paths() không hoạt động tốt? ... Khi tôi thực hiện 'socket.set_verify_mode (boost :: asio :: ssl :: verify_none);' Tôi nhận được một ngoại lệ đọc ngắn mà tôi đoán là một vấn đề không liên quan. Một lần nữa xin cảm ơn sự giúp đỡ của bạn – Jez

+0

@Jez Có thể cài đặt không bao gồm chứng chỉ tin cậy, đường dẫn không được tìm thấy thông qua 'set_default_verify_paths()' hoặc ứng dụng không có quyền đọc từ thư mục. Tham khảo tài liệu của trình cài đặt hoặc duyệt qua hệ thống tệp. Nếu bạn tìm thấy đường dẫn chứa chứng chỉ, hãy thêm đường dẫn đó vào ngữ cảnh thông qua ['add_verify_path()'] (http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/ssl__context/ add_verify_path/overload1.html). –

+0

Hóa ra tôi không có chứng chỉ được cài đặt như một phần của OpenSSL. Tôi đã giữ danh sách các chứng chỉ mozilla trong một tệp PEM duy nhất nhưng khi tôi đi sâu vào những gì 'add_verify_path()' [thực sự là một trình bao bọc cho] (https://www.openssl.org/docs/ssl/ SSL_CTX_load_verify_locations.html), có vẻ như tôi cần mỗi chứng chỉ nằm trong một tệp riêng biệt và phần mở rộng của các tệp này cần phải là .0 not .pem. Đây có phải là chính xác và nếu như vậy làm thế nào để tôi lấy tập tin PEM duy nhất của tôi và chia nó ra? Chúc mừng – Jez

Các vấn đề liên quan