Пишем код (main.cpp)
#include <arpa/inet.h>
#include <iostream>
#include <map>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
void print_usage(const char* prog) {
std::cout << "Usage: "
<< prog
<< " [client|server] <message>\n";
}
int create_socket(int port) {
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
return s;
}
int create_server_socket(int port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int s = create_socket(port);
if (bind(s, reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)) < 0) {
perror("Unable to bind");
exit(EXIT_FAILURE);
}
if (listen(s, 1) < 0) {
perror("Unable to listen");
exit(EXIT_FAILURE);
}
return s;
}
void connect_to_server(int socket, const std::string& ip, int port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
if (connect(socket,
reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr)) == -1) {
perror("Unable to connect");
exit(EXIT_FAILURE);
}
}
void do_read_write(SSL* ssl, const std::string& message) {
std::cout << "Send: " << message << '\n';
SSL_write(ssl, message.c_str(), message.length());
std::vector<char> data(100, '\0');
SSL_read(ssl, &data[0], data.size());
std::cout << "Recv: " << &data[0] << '\n';
}
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl() {
EVP_cleanup();
}
SSL_CTX *create_context() {
SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
void configure_context(SSL_CTX *ctx) {
SSL_CTX_set_ecdh_auto(ctx, 1);
if (SSL_CTX_use_certificate_file(ctx, "cert.pem",
SSL_FILETYPE_PEM) < 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem",
SSL_FILETYPE_PEM) < 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
void run_server(const std::string& message) {
std::cout << "Run in server mode\n";
init_openssl();
int sock = create_server_socket(5872);
SSL_CTX* ctx = create_context();
configure_context(ctx);
while (true) {
struct sockaddr_in addr;
uint len = sizeof(addr);
int client = accept(sock,
reinterpret_cast<struct sockaddr*>(&addr), &len);
if (client < 0) {
perror("Unable to accept");
exit(EXIT_FAILURE);
}
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) <= 0)
ERR_print_errors_fp(stderr);
else
do_read_write(ssl, message);
SSL_free(ssl);
close(client);
}
close(sock);
SSL_CTX_free(ctx);
cleanup_openssl();
}
void run_client(const std::string& message) {
std::cout << "Run in client mode\n";
init_openssl();
int socket = create_socket(5872);
SSL_CTX* ctx = create_context();
configure_context(ctx);
connect_to_server(socket, "127.0.0.1", 5872);
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, socket);
if (SSL_connect(ssl) <= 0)
ERR_print_errors_fp(stderr);
else
do_read_write(ssl, message);
SSL_free(ssl);
close(socket);
SSL_CTX_free(ctx);
cleanup_openssl();
}
typedef void (*Run)(const std::string&);
typedef std::map<std::string, Run> Runners;
int main(int argc, char* argv[]) {
Runners runners;
runners.insert(std::make_pair("server", run_server));
runners.insert(std::make_pair("client", run_client));
const std::string runner = 1 < argc ? argv[1] : "unknown";
const std::string message = 2 < argc ? argv[2] :
std::string("Hello stranger! I'm a ").append(runner);
Runners::const_iterator r = runners.find(runner);
runners.end() == r ? print_usage(argv[0]) : r->second(message);
return 0;
}
Компилируем
$ g++ -lssl -lcrypto main.cpp -o main
Генерируем самоподписанный сертификат
$ openssl req -new > new.ssl.csr
$ openssl rsa -in privkey.pem -out key.pem
$ openssl x509 -in new.ssl.csr \
-out cert.pem \
-req \
-signkey key.pem \
-days 365
Наслаждаемся
$ ./main server "Hello client! I'm a server!" &
[1] 31382
Run in server mode
$ ./main client "Hello server! I'm a client!"
Run in client mode
Send: Hello client! I'm a server!
Send: Hello server! I'm a client!
Recv: Hello server! I'm a client!
Recv: Hello client! I'm a server!
$ kill 31382
[1]+ Terminated ./main server "Hello client! I'm a server!"