Skip to content

feat: Add the ability to get the peer certificate of an SSL connection #5744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// WiFiClientShowPeerCredentials
//
// Example of a establishing a secure connection and then
// showing the fingerprint of the certificate. This can
// be useful in an IoT setting to know for sure that you
// are connecting to the right server. Especally in
// situations where you cannot hardcode a trusted root
// certificate for long periods of time (as they tend to
// get replaced more often than the lifecycle of IoT
// hardware).
//

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

#ifndef WIFI_NETWORK
#define WIFI_NETWORK "MyWifiNetwork"
#endif

#ifndef WIFI_PASSWD
#define WIFI_PASSWD "MySecretWifiPassword"
#endif

#define URL "https://arduino.cc"

void demo() {
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure(); //

HTTPClient https;
if (!https.begin(*client, URL )) {
Serial.println("HTTPS setup failed");
return;
};

https.setTimeout(5000);

int httpCode = https.GET();
if (httpCode != 200) {
Serial.print("Connect failed: ");
Serial.println(https.errorToString(httpCode));
return;
}

const mbedtls_x509_crt* peer = client->getPeerCertificate();

// Show general output / certificate information
//
char buf[1024];
int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer);
if (l <= 0) {
Serial.println("Peer conversion to printable buffer failed");
return;
};
Serial.println();
Serial.println(buf);

uint8_t fingerprint_remote[32];
if (!client->getFingerprintSHA256(fingerprint_remote)) {
Serial.println("Failed to get the fingerprint");
return;
}
// Fingerprint late 2021
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");
Serial.print( " Received Fingerprint (SHA256): ");

for (int i = 0; i < 32; i++) {
Serial.print(fingerprint_remote[i], HEX);
Serial.print(" ");
};
Serial.println("");
};

void setup() {
Serial.begin(115200);
Serial.println("Started " __FILE__ " build " __DATE__ " " __TIME__);

WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_NETWORK, WIFI_PASSWD);

while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Wifi fail - rebooting");
delay(5000);
ESP.restart();
}
}

void loop() {
bool already_tried = false;
if ((millis() < 1000) || already_tried)
return;
already_tried = true;

// Run the test just once.
demo();
}
7 changes: 4 additions & 3 deletions libraries/WiFiClientSecure/src/WiFiClientSecure.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class WiFiClientSecure : public WiFiClient
sslclient_context *sslclient;

int _lastError = 0;
int _peek = -1;
int _peek = -1;
int _timeout = 0;
bool _use_insecure;
const char *_CA_cert;
Expand All @@ -53,7 +53,7 @@ class WiFiClientSecure : public WiFiClient
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
int peek();
int peek();
size_t write(uint8_t data);
size_t write(const uint8_t *buf, size_t size);
int available();
Expand All @@ -73,7 +73,8 @@ class WiFiClientSecure : public WiFiClient
bool loadPrivateKey(Stream& stream, size_t size);
bool verify(const char* fingerprint, const char* domain_name);
void setHandshakeTimeout(unsigned long handshake_timeout);

const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); };
bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); };
int setTimeout(uint32_t seconds){ return 0; }

operator bool()
Expand Down
38 changes: 24 additions & 14 deletions libraries/WiFiClientSecure/src/ssl_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,22 +418,10 @@ bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const
fingerprint_local[i] = low | (high << 4);
}

// Get certificate provided by the peer
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);

if (!crt)
{
log_d("could not fetch peer certificate");
return false;
}

// Calculate certificate's SHA256 fingerprint
uint8_t fingerprint_remote[32];
mbedtls_sha256_context sha256_ctx;
mbedtls_sha256_init(&sha256_ctx);
mbedtls_sha256_starts(&sha256_ctx, false);
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote);
if(!get_peer_fingerprint(ssl_client, fingerprint_remote))
return false;

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

bool get_peer_fingerprint(sslclient_context *ssl_client, uint8_t sha256[32])
{
if (!ssl_client) {
log_d("Invalid ssl_client pointer");
return false;
};

const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
if (!crt) {
log_d("Failed to get peer cert.");
return false;
};

mbedtls_sha256_context sha256_ctx;
mbedtls_sha256_init(&sha256_ctx);
mbedtls_sha256_starts(&sha256_ctx, false);
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
mbedtls_sha256_finish(&sha256_ctx, sha256);

return true;
}

// Checks if peer certificate has specified domain in CN or SANs
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name)
{
Expand Down
2 changes: 1 addition & 1 deletion libraries/WiFiClientSecure/src/ssl_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, size_t len
int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length);
bool verify_ssl_fingerprint(sslclient_context *ssl_client, const char* fp, const char* domain_name);
bool verify_ssl_dn(sslclient_context *ssl_client, const char* domain_name);

bool get_peer_fingerprint(sslclient_context *ssl_client, uint8_t sha256[32]);
#endif