Add the ability to get the peer certificate of an SSL connection; useful for...

Add the ability to get the peer certificate of an SSL connection; useful for IoT when the root/cert trust chain has a shorter lifecylce than the device itself. Includes example
parent bd54ee44
// 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();
}
......@@ -31,7 +31,7 @@ protected:
sslclient_context *sslclient;
int _lastError = 0;
int _peek = -1;
int _peek = -1;
int _timeout = 0;
bool _use_insecure;
const char *_CA_cert;
......@@ -53,7 +53,7 @@ public:
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();
......@@ -73,7 +73,8 @@ public:
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()
......
......@@ -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))
......@@ -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)
{
......
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment