Пишем код (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!"