(双向认证,需要和server一样配置,如果需要密码需要set密码的回调)
参考:https://stackoverflow.com/questions/30925952/boost-asio-exception-use-private-key-file-key-values-mismatch
https://stackoverflow.com/questions/27218516/boost-asio-ssl-password-callback-not-called-if-private-key-passed-with-context
1、证书生成
1.1证书生成
a 分别生成ca,server,client的秘钥
#生成证书
1.首先要生成服务器端的私钥(key文件):openssl genrsa -des3 -out server.key 1024
genras表示生成RSA私有密钥文件,-des3表示用DES3加密该文件,1024是我们的key的长度。
2.openssl req -new -key server.key -out server.csr -config openssl.cnf 生成Certificate Signing Request(CSR)
3.对客户端也作同样的命令生成key及csr文件:openssl genrsa -des3 -out client.key 1024
openssl req -new -key client.key -out client.csr -config openssl.cnf
b 生成ca的根证书
openssl genrsa -des3 -out ca.key 2048
openssl req -new -x509 -key ca.key -out ca.crt -days 3650 -config openssl.cnf
-x509:本option将产生自签名的证书。
c 生成server,client的申请书
d 用ca签名,生成server和client的证书
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config openssl.cnf
openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key -config openssl.cnf
e 生成dh1024(需要大于dh512,最好用2048)
openssl dhparam -out dh1024.pem 1024
2、boost设置
2.1 服务端配置
//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <stdio.h>
#include <string>
#include <vector>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
// an experiment to see if we can use ASIO with LibreSSL.
using namespace boost;
using boost::asio::ip::tcp;
using boost::asio::ssl::context;
//--------------------------------------------------------------
class server_session {
public:
server_session(asio::io_context *context, asio::ssl::context *ssl_context)
: _ssl_stream(*context, *ssl_context) {}
void do_read() {
try {
std::cout << "got a new connection" << std::endl;
// read message
char buf[5]; // message will be "hello\n"
boost::system::error_code ec;
std::size_t n = _ssl_stream.read_some(asio::buffer(buf, 5));
std::cout << "read " << n << " bytes: " << buf << std::endl;
// respond? do something useful, close connection
} catch (const std::exception &e) {
std::cout << "Exception: " << e.what() << std::endl;
}
}
void do_handshake() {
_ssl_stream.async_handshake(
asio::ssl::stream_base::server, [this](std::error_code ec) {
if (ec) {
std::cout << "handshake error: " << ec.message() << std::endl;
return;
}
do_read();
});
}
asio::ssl::stream<tcp::socket> *stream() { return &_ssl_stream; }
private:
asio::ssl::stream<asio::ip::tcp::socket> _ssl_stream;
};
//--------------------------------------------------------------
class server {
public:
server(char *port)
: _context(), _ssl_context(context::sslv23),
_endpoint(tcp::v4(), std::atoi(port)), _acceptor(_context, _endpoint) {
_ssl_context.use_tmp_dh_file("dh1024.pem");
_ssl_context.set_options(context::default_workarounds | context::no_sslv2 |
context::single_dh_use);
_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer |boost::asio::ssl::verify_fail_if_no_peer_cert); //double
_ssl_context.set_verify_mode(1); // server side
_ssl_context.use_certificate_file("server.crt", context::pem);
_ssl_context.use_private_key_file("server.key", context::pem);
}
void run() {
listen();
_context.run();
}
void listen() {
std::unique_ptr<server_session> new_session(
new server_session(&_context, &_ssl_context));
_sessions.push_back(std::move(new_session));
_acceptor.async_accept(_sessions.back()->stream()->lowest_layer(),
[this](std::error_code ec) {
if (!ec) {
_sessions.back()->do_handshake();
}
listen();
// todo clean up old sessions
});
}
private:
asio::io_context _context;
asio::ssl::context _ssl_context;
tcp::endpoint _endpoint;
tcp::acceptor _acceptor;
std::vector<std::unique_ptr<server_session>> _sessions;
};
//--------------------------------------------------------------
int main(int argc, char *argv[]) {
try {
// take port from cmd line
if (argc != 2) {
std::cerr << "Usage: ssl_server <port>\n";
return 1;
}
server server{argv[1]};
server.run();
return 0;
} catch (const std::exception &e) {
std::cerr << "Exception: " << e.what() << "\n";
return 1;
}
}
2.1 客户端配置
不需要客户证书和ca即可登录收发数据,只需要
//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
using std::placeholders::_1;
using std::placeholders::_2;
enum { max_length = 1024 };
class client
{
public:
client(boost::asio::io_context& io_context,
boost::asio::ssl::context& context,
const tcp::resolver::results_type& endpoints)
: socket_(io_context, context)
{
socket_.set_verify_mode(boost::asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verify_certificate, this, _1, _2));
connect(endpoints);
}
private:
bool verify_certificate(bool preverified,
boost::asio::ssl::verify_context& ctx)
{
// The verify callback can be used to check whether the certificate that is
// being presented is valid for the peer. For example, RFC 2818 describes
// the steps involved in doing this for HTTPS. Consult the OpenSSL
// documentation for more details. Note that the callback is called once
// for each certificate in the certificate chain, starting from the root
// certificate authority.
// In this example we will simply print the certificate's subject name.
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);
std::cout << "Verifying " << subject_name << "\n";
return preverified;
}
void connect(const tcp::resolver::results_type& endpoints)
{
boost::asio::async_connect(socket_.lowest_layer(), endpoints,
[this](const boost::system::error_code& error,
const tcp::endpoint& /*endpoint*/)
{
if (!error)
{
handshake();
}
else
{
std::cout << "Connect failed: " << error.message() << "\n";
}
});
}
void handshake()
{
socket_.async_handshake(boost::asio::ssl::stream_base::client,
[this](const boost::system::error_code& error)
{
if (!error)
{
send_request();
}
else
{
std::cout << "Handshake failed: " << error.message() << "\n";
}
});
}
void send_request()
{
std::cout << "Enter message: ";
std::cin.getline(request_, max_length);
size_t request_length = std::strlen(request_);
boost::asio::async_write(socket_,
boost::asio::buffer(request_, request_length),
[this](const boost::system::error_code& error, std::size_t length)
{
if (!error)
{
receive_response(length);
}
else
{
std::cout << "Write failed: " << error.message() << "\n";
}
});
}
void receive_response(std::size_t length)
{
boost::asio::async_read(socket_,
boost::asio::buffer(reply_, length),
[this](const boost::system::error_code& error, std::size_t length)
{
if (!error)
{
std::cout << "Reply: ";
std::cout.write(reply_, length);
std::cout << "\n";
}
else
{
std::cout << "Read failed: " << error.message() << "\n";
}
});
}
boost::asio::ssl::stream<tcp::socket> socket_;
char request_[max_length];
char reply_[max_length];
};
std::string passwd_callback(std::size_t max_length,boost::asio::ssl::context::password_purpose){
return "666666";
};
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: client <host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(argv[1], argv[2]);
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_password_callback(passwd_callback); //set passwd,clent.key passwd
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
ctx.use_certificate_file("client.crt", boost::asio::ssl::context::pem);//load client.crt
ctx.use_private_key_file("client.key", boost::asio::ssl::context::pem);//load client.key
ctx.load_verify_file("ca.crt"); //load ca.crt--root crt
client c(io_context, ctx, endpoints);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
网友评论