Zamień proste gniazdo w gniazdo SSL

115

Napisałem proste programy w C, które używają gniazd („klient” i „serwer”). (Użycie UNIX / Linux)

Po stronie serwera po prostu tworzy gniazdo:

sockfd = socket(AF_INET, SOCK_STREAM, 0);

A potem wiąże go z sockaddr:

bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

I słucha (i akceptuje i czyta):

listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
read(newsockfd,buffer,255);

Klient tworzy gniazdo, a następnie zapisuje w nim.

Teraz chcę przekształcić to proste połączenie w połączenie SSL w najprostszy, najbardziej idylliczny, schludny i najszybszy sposób.

Próbowałem dodać OpenSSL do mojego projektu, ale nie mogę znaleźć prostego sposobu na wdrożenie tego, co chcę.

David Mape
źródło
Jeśli szukasz „bezpiecznego połączenia”, a nie SSL w szczególności, możesz spojrzeć na coś takiego jak proxychains.sourceforge.net, który znajduje się poza twoją aplikacją i ustawić to tak, aby wysyłał ruch przez połączenie SSH. Jeśli chodzi o SSL w aplikacji, OpenSSL jest całkiem proste, jeśli zrozumiesz, jak powinien działać SSL / TLS. Jeśli potrzebujesz alternatywy, wypróbuj yaSSL lub gnuTLS.
Borealid
3
Zdefiniuj „łatwy sposób”. OpenSSl to standard dla programistów C. Jeśli masz z tym trudności, powinieneś o to zapytać.
Markiz Lorne
Sprawdź to Wprowadzenie do programowania OpenSSL (część I) . Część II jest dla mnie zbyt zaawansowana i trudna. Ale warto przyjrzeć się part2.
Rick
Sprawdź także Bezpieczne programowanie z OpenSSL API . Ale właśnie usłyszałem opinie o tym, jak zły jest Openssl i inne alternatywy, które warto wypróbować.
Rick
Inną opcją jest użycie zewnętrznego narzędzia otoki SSL, takich jak stunnelThe stunnel4pakiet jest w dystrybucjach opartych na Debianie i jest łatwy w użyciu. Istnieją pewne ograniczenia w porównaniu z dodaniem odpowiedniej obsługi SSL na serwerze, ale może to być dobre rozwiązanie dla szybkiego rozwiązania. Lubię stunnel, ponieważ wydaje się pasować do podejścia narzędzi programowych UNIX.
Sam Watkins

Odpowiedzi:

150

Korzystanie z OpenSSL obejmuje kilka kroków. Musisz mieć wykonany certyfikat SSL, który może zawierać certyfikat z kluczem prywatnym, pamiętaj o określeniu dokładnej lokalizacji certyfikatu (w tym przykładzie jest ona w katalogu głównym). Istnieje wiele dobrych tutoriali.

Niektóre z nich obejmują:

#include <openssl/applink.c>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

Będziesz musiał zainicjować OpenSSL:

void InitializeSSL()
{
    SSL_load_error_strings();
    SSL_library_init();
    OpenSSL_add_all_algorithms();
}

void DestroySSL()
{
    ERR_free_strings();
    EVP_cleanup();
}

void ShutdownSSL()
{
    SSL_shutdown(cSSL);
    SSL_free(cSSL);
}

Teraz większość funkcjonalności. Możesz chcieć dodać pętlę while na połączeniach.

int sockfd, newsockfd;
SSL_CTX *sslctx;
SSL *cSSL;

InitializeSSL();
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd< 0)
{
    //Log and Error
    return;
}
struct sockaddr_in saiServerAddress;
bzero((char *) &saiServerAddress, sizeof(saiServerAddress));
saiServerAddress.sin_family = AF_INET;
saiServerAddress.sin_addr.s_addr = serv_addr;
saiServerAddress.sin_port = htons(aPortNumber);

bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

sslctx = SSL_CTX_new( SSLv23_server_method());
SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);
int use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);

int use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);

cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, newsockfd );
//Here is the SSL Accept portion.  Now all reads and writes must use SSL
ssl_err = SSL_accept(cSSL);
if(ssl_err <= 0)
{
    //Error occurred, log and close down ssl
    ShutdownSSL();
}

Możesz wtedy czytać lub pisać za pomocą:

SSL_read(cSSL, (char *)charBuffer, nBytesToRead);
SSL_write(cSSL, "Hi :3\n", 6);

Update The SSL_CTX_newpowinien być wywoływany metodą TLS, która najlepiej odpowiada Twoim potrzebom, w celu obsługi nowszych wersji zabezpieczeń zamiast SSLv23_server_method(). Zobacz: opis OpenSSL SSL_CTX_new

TLS_method (), TLS_server_method (), TLS_client_method (). Są to metody SSL / TLS ogólnego przeznaczenia z elastycznymi wersjami . Rzeczywista wersja używanego protokołu zostanie wynegocjowana do najwyższej wersji obsługiwanej wspólnie przez klienta i serwer. Obsługiwane protokoły to SSLv3, TLSv1, TLSv1.1, TLSv1.2 i TLSv1.3.

CaptainBli
źródło
9
nie tak „proste”, jak myślałem, ale w końcu (dzięki Bogu!) widzę kod. Czy jest to wieloplatformowe, czy tylko dla systemów typu unix / unix?
juliomalegria
3
Użyłem podobnego kodu na wielu platformach: arm, linux i windows.
CaptainBli
2
Ostatni ifjest jednak zły. Dopiero if (ssl_err <= 0) {wtedy jest to błąd. SSL_accept()powraca 1po sukcesie, 0po „kontrolowanej porażce” i -1„fatalnej porażce”. Zobacz stronę podręcznika.
Jite
2
Ponadto szyfry DH nie będą działać, jeśli SSL_CTX_set_tmp_dh[_callback]()nie zostaną wywołane. Właśnie odkrył w bolesny sposób, że szyfry aNULL nie będzie działać bez niego, tworząc szereg powiadomienie 40.
Roman Dmitrienko
5
@DevNull Stan SSLv23_server_method(), że serwer obsługuje SSLv2 i v3 i jest obecnie przestarzały. Aby obsługiwać protokoły TLS 1.1 i 1.2, zamień tę metodę na TLS_server_method(). źródło
ezPaint
17

OpenSSL jest dość trudny. Łatwo jest przypadkowo wyrzucić całe swoje bezpieczeństwo, nie wykonując negocjacji dokładnie tak, jak trzeba. (Do licha, osobiście ugryzł mnie błąd, w którym curl nie odczytywał alertów OpenSSL dokładnie i nie mógł rozmawiać z niektórymi witrynami.)

Jeśli naprawdę chcesz szybko i prosto, umieść stud przed swoim programem i zwołaj go dziennie. Posiadanie SSL w innym procesie nie spowolni: http://vincent.bernat.im/en/blog/2011-ssl-benchmark.html

BraveNewCurrency
źródło
11
To praktyczna odpowiedź, ale tak naprawdę nie odpowiada na pytanie.
Szwajcaria
4
STUD został opuszczony w 2016 roku. Readme poleca: github.com/varnish/hitch
Charles
7

Dla innych takich jak ja:

Był kiedyś przykład w źródle SSL w katalogu demos/ssl/z przykładowym kodem w C ++. Teraz jest dostępny tylko przez historię: https://github.com/openssl/openssl/tree/691064c47fd6a7d11189df00a0d1b94d8051cbe0/demos/ssl

Prawdopodobnie będziesz musiał znaleźć działającą wersję, pierwotnie opublikowałem tę odpowiedź 6 listopada 2015. I musiałem edytować źródło - niewiele.

Certyfikaty: .pem in demos/certs/apps/: https://github.com/openssl/openssl/tree/master/demos/certs/apps

18446744073709551615
źródło
-1

Tutaj moje przykładowe wątki serwera gniazda ssl (wiele połączeń) https://github.com/breakermind/CppLinux/blob/master/QtSslServerThreads/breakermindsslserver.cpp

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <iostream>

#include <breakermindsslserver.h>

using namespace std;

int main(int argc, char *argv[])
{
    BreakermindSslServer boom;
    boom.Start(123,"/home/user/c++/qt/BreakermindServer/certificate.crt", "/home/user/c++/qt/BreakermindServer/private.key");
    return 0;
}
Fanex
źródło
Prosimy o umieszczenie rozwiązania bezpośrednio w poście SO.
Maciej Jureczko