Skip to content

Commit

Permalink
Add public key pinning
Browse files Browse the repository at this point in the history
Signed-off-by: DBL2017 <[email protected]>
  • Loading branch information
DBL2017 authored and blduan committed Apr 26, 2024
1 parent 0521dc5 commit fba194b
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/MQTTAsync.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
free((void*)m->c->sslopts->privateKeyPassword);
if (m->c->sslopts->enabledCipherSuites)
free((void*)m->c->sslopts->enabledCipherSuites);
if (m->c->sslopts->publicKey)
free((void*)m->c->sslopts->publicKey);
if (m->c->sslopts->struct_version >= 2)
{
if (m->c->sslopts->CApath)
Expand Down
5 changes: 4 additions & 1 deletion src/MQTTAsync.h
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,9 @@ typedef struct
/** The password to load the client's privateKey if encrypted. */
const char* privateKeyPassword;

/** This setting points to the file in PEM format containing the server's public key, can be used public key pinning*/
const char* publicKey;

/**
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
Expand Down Expand Up @@ -1176,7 +1179,7 @@ typedef struct
unsigned int protos_len;
} MQTTAsync_SSLOptions;

#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }

/** Utility structure where name/value pairs are needed */
typedef struct
Expand Down
5 changes: 4 additions & 1 deletion src/MQTTClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,9 @@ typedef struct
/** The password to load the client's privateKey if encrypted. */
const char* privateKeyPassword;

/** This setting points to the file in PEM format containing the server's public key, can be used public key pinning*/
const char* publicKey;

/**
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
Expand Down Expand Up @@ -779,7 +782,7 @@ typedef struct
unsigned int protos_len;
} MQTTClient_SSLOptions;

#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }
#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 }

/**
* MQTTClient_libraryInfo is used to store details relating to the currently used
Expand Down
94 changes: 93 additions & 1 deletion src/SSLSocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
extern Sockets mod_s;

static int SSLSocket_error(char* aString, SSL* ssl, SOCKET sock, int rc, int (*cb)(const char *str, size_t len, void *u), void* u);
static int SSLSocket_certificate_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
char* SSL_get_verify_result_string(int rc);
void SSL_CTX_info_callback(const SSL* ssl, int where, int ret);
char* SSLSocket_get_version_string(int version);
Expand Down Expand Up @@ -78,6 +79,8 @@ static ssl_mutex_type sslCoreMutex;

/* Used to store MQTTClient_SSLOptions for TLS-PSK callback */
static int tls_ex_index_ssl_opts;
/* Used to store MQTTClient_SSLOptions for TLS Certificate verify callback */
static int tls_ex_index_ssl_opts_for_verify_cb;

#if defined(_WIN32) || defined(_WIN64)
#define iov_len len
Expand Down Expand Up @@ -122,6 +125,92 @@ static int SSLSocket_error(char* aString, SSL* ssl, SOCKET sock, int rc, int (*c
return error;
}

static int SSLSocket_certificate_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
int error = X509_STORE_CTX_get_error(x509_ctx);
FUNC_ENTRY;

int iVerifyOK = 0;

/* depth==0 server certificate */
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
Log(TRACE_MIN, -1, "preverify_ok=%d depth=%d", preverify_ok, depth);
if (depth == 0)
{
/* 1. Extracting the public key from a certificate. */
X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
if (cert == NULL)
goto exit;

EVP_PKEY* pubkey_from_cert = X509_get_pubkey(cert);
if (pubkey_from_cert == NULL)
{
Log(TRACE_MIN, -1, "Error extracting public key from certificate");
goto exit;
}

/* 2. The public key from the configuration. */
SSL* ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
if (ssl == NULL)
{
Log(TRACE_MIN, -1, "Error SSL get_ex_data");
goto exit;
}
SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
if (ssl_ctx == NULL)
{
Log(TRACE_MIN, -1, "Error SSL_CTX get_ex_data");
goto exit;
}

MQTTClient_SSLOptions* opts = SSL_CTX_get_ex_data(ssl_ctx, tls_ex_index_ssl_opts_for_verify_cb);
if (opts == NULL)
{
Log(TRACE_MIN, -1, "Error opts get_ex_data");
goto exit;
}

if (opts->publicKey == NULL || strlen(opts->publicKey) <= 0 || access(opts->publicKey, R_OK) != 0)
{
Log(TRACE_MIN, -1, "Error opts pubKey invalid");
goto exit;
}

FILE* pubkey_file = fopen(opts->publicKey, "r");
if (pubkey_file == NULL)
{
Log(TRACE_MIN, -1,"Error opening public key file");
goto exit;
}

EVP_PKEY* pubkey_from_file = PEM_read_PUBKEY(pubkey_file, NULL, NULL, NULL);
fclose(pubkey_file);
if (pubkey_from_file == NULL)
{
Log(TRACE_MIN, -1, "Error reading public key file");
goto exit;
}

// 3. compare
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) /* 3.0.0 and later */
if (EVP_PKEY_eq(pubkey_from_file, pubkey_from_cert) == 1)
#else
if (EVP_PKEY_cmp(pubkey_from_file, pubkey_from_cert) == 1)
#endif
iVerifyOK = 1;

// 4. cleanup
EVP_PKEY_free(pubkey_from_cert);
EVP_PKEY_free(pubkey_from_file);
}
else
iVerifyOK = preverify_ok;

exit:
FUNC_EXIT_RC(error);
return iVerifyOK;
}

static struct
{
int code;
Expand Down Expand Up @@ -490,6 +579,7 @@ int SSLSocket_initialize(void)
SSL_create_mutex(&sslCoreMutex);

tls_ex_index_ssl_opts = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);
tls_ex_index_ssl_opts_for_verify_cb = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);

exit:
FUNC_EXIT_RC(rc);
Expand Down Expand Up @@ -675,6 +765,8 @@ int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts)
SSL_CTX_set_psk_client_callback(net->ctx, call_ssl_psk_cb);
}
#endif
if (opts->publicKey !=NULL )
SSL_CTX_set_ex_data(net->ctx, tls_ex_index_ssl_opts_for_verify_cb, opts);

#if (OPENSSL_VERSION_NUMBER >= 0x010002000) /* 1.0.2 and later */
if (opts->protos != NULL && opts->protos_len > 0)
Expand Down Expand Up @@ -722,7 +814,7 @@ int SSLSocket_setSocketForSSL(networkHandles* net, MQTTClient_SSLOptions* opts,
SSL_CTX_set_info_callback(net->ctx, SSL_CTX_info_callback);
SSL_CTX_set_msg_callback(net->ctx, SSL_CTX_msg_callback);
if (opts->enableServerCertAuth)
SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, opts->publicKey != NULL ? SSLSocket_certificate_verify_cb : NULL);

net->ssl = SSL_new(net->ctx);

Expand Down

0 comments on commit fba194b

Please sign in to comment.