Skip to content

Commit 7a7bd37

Browse files
authored
feat: Add the ability to get the peer certificate of an SSL connection
Summary New feature: Add the ability to get the peer certificate of an SSL connectio. This is useful for IoT when the root/cert trust chain has a shorter lifecylce than the device itself. Includes example code. It adds two methods to the WiFiClientSecure client: bool getFingerprintSHA256( uint8_t fingerprint_remote_sha256[32]) -- return true and the fingerprint (i.e. the SHA256 of the raw x509 as a DER - identical to what you see in for example your webbrowser). Or false on error. const mbedtls_x509_crt* getPeerCertificate(); -- return the actual X509 struct or NULL on error. Impact No impact; backwards compatible (only adds to the API)
2 parents a75602d + 1706af4 commit 7a7bd37

File tree

4 files changed

+126
-18
lines changed

4 files changed

+126
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// WiFiClientShowPeerCredentials
2+
//
3+
// Example of a establishing a secure connection and then
4+
// showing the fingerprint of the certificate. This can
5+
// be useful in an IoT setting to know for sure that you
6+
// are connecting to the right server. Especally in
7+
// situations where you cannot hardcode a trusted root
8+
// certificate for long periods of time (as they tend to
9+
// get replaced more often than the lifecycle of IoT
10+
// hardware).
11+
//
12+
13+
#include <WiFi.h>
14+
#include <HTTPClient.h>
15+
#include <WiFiClientSecure.h>
16+
17+
#ifndef WIFI_NETWORK
18+
#define WIFI_NETWORK "MyWifiNetwork"
19+
#endif
20+
21+
#ifndef WIFI_PASSWD
22+
#define WIFI_PASSWD "MySecretWifiPassword"
23+
#endif
24+
25+
#define URL "https://arduino.cc"
26+
27+
void demo() {
28+
WiFiClientSecure *client = new WiFiClientSecure;
29+
client->setInsecure(); //
30+
31+
HTTPClient https;
32+
if (!https.begin(*client, URL )) {
33+
Serial.println("HTTPS setup failed");
34+
return;
35+
};
36+
37+
https.setTimeout(5000);
38+
39+
int httpCode = https.GET();
40+
if (httpCode != 200) {
41+
Serial.print("Connect failed: ");
42+
Serial.println(https.errorToString(httpCode));
43+
return;
44+
}
45+
46+
const mbedtls_x509_crt* peer = client->getPeerCertificate();
47+
48+
// Show general output / certificate information
49+
//
50+
char buf[1024];
51+
int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer);
52+
if (l <= 0) {
53+
Serial.println("Peer conversion to printable buffer failed");
54+
return;
55+
};
56+
Serial.println();
57+
Serial.println(buf);
58+
59+
uint8_t fingerprint_remote[32];
60+
if (!client->getFingerprintSHA256(fingerprint_remote)) {
61+
Serial.println("Failed to get the fingerprint");
62+
return;
63+
}
64+
// Fingerprint late 2021
65+
Serial.println("Expecting Fingerprint (SHA256): 70 CF A4 B7 5D 09 E9 2A 52 A8 B6 85 B5 0B D6 BE 83 47 83 5B 3A 4D 3C 3E 32 30 EC 1D 61 98 D7 0F");
66+
Serial.print( " Received Fingerprint (SHA256): ");
67+
68+
for (int i = 0; i < 32; i++) {
69+
Serial.print(fingerprint_remote[i], HEX);
70+
Serial.print(" ");
71+
};
72+
Serial.println("");
73+
};
74+
75+
void setup() {
76+
Serial.begin(115200);
77+
Serial.println("Started " __FILE__ " build " __DATE__ " " __TIME__);
78+
79+
WiFi.mode(WIFI_STA);
80+
WiFi.begin(WIFI_NETWORK, WIFI_PASSWD);
81+
82+
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
83+
Serial.println("Wifi fail - rebooting");
84+
delay(5000);
85+
ESP.restart();
86+
}
87+
}
88+
89+
void loop() {
90+
bool already_tried = false;
91+
if ((millis() < 1000) || already_tried)
92+
return;
93+
already_tried = true;
94+
95+
// Run the test just once.
96+
demo();
97+
}

Diff for: libraries/WiFiClientSecure/src/WiFiClientSecure.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class WiFiClientSecure : public WiFiClient
3131
sslclient_context *sslclient;
3232

3333
int _lastError = 0;
34-
int _peek = -1;
34+
int _peek = -1;
3535
int _timeout = 0;
3636
bool _use_insecure;
3737
const char *_CA_cert;
@@ -53,7 +53,7 @@ class WiFiClientSecure : public WiFiClient
5353
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
5454
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
5555
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
56-
int peek();
56+
int peek();
5757
size_t write(uint8_t data);
5858
size_t write(const uint8_t *buf, size_t size);
5959
int available();
@@ -73,7 +73,8 @@ class WiFiClientSecure : public WiFiClient
7373
bool loadPrivateKey(Stream& stream, size_t size);
7474
bool verify(const char* fingerprint, const char* domain_name);
7575
void setHandshakeTimeout(unsigned long handshake_timeout);
76-
76+
const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); };
77+
bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); };
7778
int setTimeout(uint32_t seconds){ return 0; }
7879

7980
operator bool()

Diff for: libraries/WiFiClientSecure/src/ssl_client.cpp

+24-14
Original file line numberDiff line numberDiff line change
@@ -418,22 +418,10 @@ bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const
418418
fingerprint_local[i] = low | (high << 4);
419419
}
420420

421-
// Get certificate provided by the peer
422-
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
423-
424-
if (!crt)
425-
{
426-
log_d("could not fetch peer certificate");
427-
return false;
428-
}
429-
430421
// Calculate certificate's SHA256 fingerprint
431422
uint8_t fingerprint_remote[32];
432-
mbedtls_sha256_context sha256_ctx;
433-
mbedtls_sha256_init(&sha256_ctx);
434-
mbedtls_sha256_starts(&sha256_ctx, false);
435-
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
436-
mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote);
423+
if(!get_peer_fingerprint(ssl_client, fingerprint_remote))
424+
return false;
437425

438426
// Check if fingerprints match
439427
if (memcmp(fingerprint_local, fingerprint_remote, 32))
@@ -449,6 +437,28 @@ bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const
449437
return true;
450438
}
451439

440+
bool get_peer_fingerprint(sslclient_context *ssl_client, uint8_t sha256[32])
441+
{
442+
if (!ssl_client) {
443+
log_d("Invalid ssl_client pointer");
444+
return false;
445+
};
446+
447+
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
448+
if (!crt) {
449+
log_d("Failed to get peer cert.");
450+
return false;
451+
};
452+
453+
mbedtls_sha256_context sha256_ctx;
454+
mbedtls_sha256_init(&sha256_ctx);
455+
mbedtls_sha256_starts(&sha256_ctx, false);
456+
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
457+
mbedtls_sha256_finish(&sha256_ctx, sha256);
458+
459+
return true;
460+
}
461+
452462
// Checks if peer certificate has specified domain in CN or SANs
453463
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name)
454464
{

Diff for: libraries/WiFiClientSecure/src/ssl_client.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, size_t len
3636
int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length);
3737
bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const char* domain_name);
3838
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name);
39-
39+
bool get_peer_fingerprint(sslclient_context *ssl_client, uint8_t sha256[32]);
4040
#endif

0 commit comments

Comments
 (0)