Skip to content

Commit fba194b

Browse files
DBL2017blduan
authored and
blduan
committed
Add public key pinning
Signed-off-by: DBL2017 <1578770584@qq.com>
1 parent 0521dc5 commit fba194b

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

src/MQTTAsync.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,8 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options)
753753
free((void*)m->c->sslopts->privateKeyPassword);
754754
if (m->c->sslopts->enabledCipherSuites)
755755
free((void*)m->c->sslopts->enabledCipherSuites);
756+
if (m->c->sslopts->publicKey)
757+
free((void*)m->c->sslopts->publicKey);
756758
if (m->c->sslopts->struct_version >= 2)
757759
{
758760
if (m->c->sslopts->CApath)

src/MQTTAsync.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,9 @@ typedef struct
10951095
/** The password to load the client's privateKey if encrypted. */
10961096
const char* privateKeyPassword;
10971097

1098+
/** This setting points to the file in PEM format containing the server's public key, can be used public key pinning*/
1099+
const char* publicKey;
1100+
10981101
/**
10991102
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
11001103
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
@@ -1176,7 +1179,7 @@ typedef struct
11761179
unsigned int protos_len;
11771180
} MQTTAsync_SSLOptions;
11781181

1179-
#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 }
1182+
#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 }
11801183

11811184
/** Utility structure where name/value pairs are needed */
11821185
typedef struct

src/MQTTClient.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,9 @@ typedef struct
698698
/** The password to load the client's privateKey if encrypted. */
699699
const char* privateKeyPassword;
700700

701+
/** This setting points to the file in PEM format containing the server's public key, can be used public key pinning*/
702+
const char* publicKey;
703+
701704
/**
702705
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
703706
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
@@ -779,7 +782,7 @@ typedef struct
779782
unsigned int protos_len;
780783
} MQTTClient_SSLOptions;
781784

782-
#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 }
785+
#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 }
783786

784787
/**
785788
* MQTTClient_libraryInfo is used to store details relating to the currently used

src/SSLSocket.c

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
extern Sockets mod_s;
4848

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

7980
/* Used to store MQTTClient_SSLOptions for TLS-PSK callback */
8081
static int tls_ex_index_ssl_opts;
82+
/* Used to store MQTTClient_SSLOptions for TLS Certificate verify callback */
83+
static int tls_ex_index_ssl_opts_for_verify_cb;
8184

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

128+
static int SSLSocket_certificate_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
129+
{
130+
int error = X509_STORE_CTX_get_error(x509_ctx);
131+
FUNC_ENTRY;
132+
133+
int iVerifyOK = 0;
134+
135+
/* depth==0 server certificate */
136+
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
137+
Log(TRACE_MIN, -1, "preverify_ok=%d depth=%d", preverify_ok, depth);
138+
if (depth == 0)
139+
{
140+
/* 1. Extracting the public key from a certificate. */
141+
X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
142+
if (cert == NULL)
143+
goto exit;
144+
145+
EVP_PKEY* pubkey_from_cert = X509_get_pubkey(cert);
146+
if (pubkey_from_cert == NULL)
147+
{
148+
Log(TRACE_MIN, -1, "Error extracting public key from certificate");
149+
goto exit;
150+
}
151+
152+
/* 2. The public key from the configuration. */
153+
SSL* ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
154+
if (ssl == NULL)
155+
{
156+
Log(TRACE_MIN, -1, "Error SSL get_ex_data");
157+
goto exit;
158+
}
159+
SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
160+
if (ssl_ctx == NULL)
161+
{
162+
Log(TRACE_MIN, -1, "Error SSL_CTX get_ex_data");
163+
goto exit;
164+
}
165+
166+
MQTTClient_SSLOptions* opts = SSL_CTX_get_ex_data(ssl_ctx, tls_ex_index_ssl_opts_for_verify_cb);
167+
if (opts == NULL)
168+
{
169+
Log(TRACE_MIN, -1, "Error opts get_ex_data");
170+
goto exit;
171+
}
172+
173+
if (opts->publicKey == NULL || strlen(opts->publicKey) <= 0 || access(opts->publicKey, R_OK) != 0)
174+
{
175+
Log(TRACE_MIN, -1, "Error opts pubKey invalid");
176+
goto exit;
177+
}
178+
179+
FILE* pubkey_file = fopen(opts->publicKey, "r");
180+
if (pubkey_file == NULL)
181+
{
182+
Log(TRACE_MIN, -1,"Error opening public key file");
183+
goto exit;
184+
}
185+
186+
EVP_PKEY* pubkey_from_file = PEM_read_PUBKEY(pubkey_file, NULL, NULL, NULL);
187+
fclose(pubkey_file);
188+
if (pubkey_from_file == NULL)
189+
{
190+
Log(TRACE_MIN, -1, "Error reading public key file");
191+
goto exit;
192+
}
193+
194+
// 3. compare
195+
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) /* 3.0.0 and later */
196+
if (EVP_PKEY_eq(pubkey_from_file, pubkey_from_cert) == 1)
197+
#else
198+
if (EVP_PKEY_cmp(pubkey_from_file, pubkey_from_cert) == 1)
199+
#endif
200+
iVerifyOK = 1;
201+
202+
// 4. cleanup
203+
EVP_PKEY_free(pubkey_from_cert);
204+
EVP_PKEY_free(pubkey_from_file);
205+
}
206+
else
207+
iVerifyOK = preverify_ok;
208+
209+
exit:
210+
FUNC_EXIT_RC(error);
211+
return iVerifyOK;
212+
}
213+
125214
static struct
126215
{
127216
int code;
@@ -490,6 +579,7 @@ int SSLSocket_initialize(void)
490579
SSL_create_mutex(&sslCoreMutex);
491580

492581
tls_ex_index_ssl_opts = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);
582+
tls_ex_index_ssl_opts_for_verify_cb = SSL_get_ex_new_index(0, "paho ssl options", NULL, NULL, NULL);
493583

494584
exit:
495585
FUNC_EXIT_RC(rc);
@@ -675,6 +765,8 @@ int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts)
675765
SSL_CTX_set_psk_client_callback(net->ctx, call_ssl_psk_cb);
676766
}
677767
#endif
768+
if (opts->publicKey !=NULL )
769+
SSL_CTX_set_ex_data(net->ctx, tls_ex_index_ssl_opts_for_verify_cb, opts);
678770

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

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

0 commit comments

Comments
 (0)