Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

c++ - Client program to validate server certificate returned by SSL_get_peer_certificate?

I have a SSL/TLS client program using OpenSSL in C++ programming language. I am looking for methods to validate server certificate (X509) returned by SSL_get_peer_certificate function call. Also, I have my own CA certificate loaded using SSL_CTX_load_verify_locations function. The CA certified the server certificate.

I am able to make SSL session to my server. Now, i want to validate server certificate received during SSL handshake using my own CA. I couldn't find a way to do it in C or C++.

#include <iostream>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>


#define DEFAULT_PORT_NUMBER                     443

int create_socket(char *, uint16_t port_num);

int openSSL_client_init()
{
    OpenSSL_add_all_algorithms();
    ERR_load_BIO_strings();
    ERR_load_crypto_strings();
    SSL_load_error_strings();

    if (SSL_library_init() < 0)
        return -1;
    return 0;
}

int openSSL_create_client_ctx(SSL_CTX **ctx)
{
    const SSL_METHOD *method = SSLv23_client_method();

    if ((*ctx = SSL_CTX_new(method)) == NULL)
        return -1;


    //SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);

    return 0;
}

int main(int argc, char *argv[])
{
BIO *outbio = NULL;

X509 *cert;
X509_NAME *certname = NULL;

SSL_CTX *ctx;
SSL *ssl;
int server = 0;
int ret, i;

if (openSSL_client_init()) {
    std :: cerr << "Could not initialize the OpenSSL library !" << std     :: endl;
    return -1;
}

outbio  = BIO_new_fp(stdout, BIO_NOCLOSE);

if (openSSL_create_client_ctx(&ctx)) {
    std :: cerr << "Unable to create a new SSL context structure." << std :: endl;
    return -1;
}


std :: cout << "Adding Certifcate" << std :: endl;
if (SSL_CTX_load_verify_locations(ctx, "ca-cert.pem", NULL) <= 0) {
    std :: cerr << "Unable to Load certificate" << std :: endl;
    return -1;
}


ssl = SSL_new(ctx);
server = create_socket(argv[1], atoi(argv[2]));

if (server < 0) {
    std :: cerr << "Error: Can't create TCP session" << std :: endl;
    return -1;
}
std :: cout << "Successfully made the TCP connection to: " << argv[1] << " port: " << atoi(argv[2]) << std :: endl;

SSL_set_fd(ssl, server);

if (SSL_connect(ssl) != 1) {
    std :: cerr << "Error: Could not build a SSL session to: " << argv[1] << std :: endl;
    return -1;
}

std :: cout << "Successfully enabled SSL/TLS session to: " << argv[1] << std :: endl;
//SSL_SESSION *ss = SSL_get_session(ssl);

cert = SSL_get_peer_certificate(ssl);
if (cert == NULL) {
    std :: cerr << "Error: Could not get a certificate from: " <<  argv[1] << std :: endl;
    return -1;
}

certname = X509_NAME_new();
certname = X509_get_subject_name(cert);

std :: cout << "Displaying the certificate subject data:" << std :: endl;
X509_NAME_print_ex(outbio, certname, 0, 0);
std :: cout << std :: endl;


char msg[100000] = "GET / HTTP/1.1
HOST: www.siliconbolt.com

";
SSL_write(ssl, msg, strlen(msg));
SSL_read(ssl, msg, 100000);
std :: cout << "Message is " << msg << std :: endl;


SSL_free(ssl);
close(server);
X509_free(cert);
SSL_CTX_free(ctx);
std :: cout << "Finished SSL/TLS connection with server" << std :: endl;
return 0;
}


int create_socket(char *ip_cstr, uint16_t port_num)
{
int fd;
struct sockaddr_in dest_addr;

fd = socket(AF_INET, SOCK_STREAM, 0);

dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port_num);
dest_addr.sin_addr.s_addr = inet_addr(ip_cstr);

memset(&(dest_addr.sin_zero), '', 8);

if (connect(fd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == -1)
    return -1;

return fd;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Proper certificate verification is a complex thing and OpenSSL is only slightly helpful there. If you expect full code for each step I would consider the question as too broad. Therefore I'll focus on the essential parts needed in verification and mainly point out other resources for the details of the implementation of the specific parts.


The first step for validating a server certificate is building the trust chain to a trusted root CA certificate. This is implicitly done by openssl inside the TLS handshake if you've set a trusted root (i.e. call of SSL_CTX_load_verify_locations in your code) and also set the verification mode with SSL_CTX_set_verify to SSL_VERIFY_PEER. This built-in validation also includes checks if the leaf and chain certificates are already valid and not yet expired.

The next step is to validate if the subject of the certificate matches the expected one. For server certificates this usually means that the target hostname is somehow included in the common name or the subject alternative names of the certificate. The actual requirements depend on the application layer protocol, i.e. HTTP, SMTP, LDAP ... all have slightly different rules especially if wildcards are involved. Since OpenSSL 1.0.2 a a X509_check_host function is available and can be used to check against the rules in most cases. With earlier versions of OpenSSL you are on your own to implement such a function. Note that you explicitly need to validate the hostname, i.e. OpenSSL will not do this for you and omitting this step will make man in the middle attacks against your application easy.

At this point that you know that the certificate is directly or indirectly issued by a trusted root CA and that the certificate matches the expected hostname. You now need to check if the certificate is revoked. One way is to use a certificate revocation list (CRL) which you got somewhere, another is to use the the Online Certificate Status Protocol (OCSP).

For CRL see Does OpenSSL automatically handle CRLs (Certificate Revocation Lists) now? which hows how to use a CRL you've downloaded already. But OpenSSL will not help you with downloading the CRL in the first place. Instead you would need to extract the CRL distribution point from the leaf certificate, download the CRL yourself and then you can use it.

As for OCSP OpenSSL provides the necessary API to build OCSP requests and validate OCSP responses. You are still on your own to figure out where to sent this OCSP request to. This needs to be done again by parsing the leaf certificate and extracting the OCSP URL from the Authority Information Access field. And while there is API for the rest it is not easy to use and was mostly or fully undocumented at least before OpenSSL version 1.1.0. I don't know of a good and easy to understand example where OCSP validation is implemented using this API but you might have a look at the openssl ocsp command and its implementation.


In case you write a client application and want to accept only a single certificate anyway you can omit all the complex steps of validating against a PKI but only check if the certificate is exactly the expected one, i.e. certificate pinning. See OWASP: Certificate and Public Key Pinning for more information about this and for sample code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...