diff --git a/libraries/WiFiClientSecure/README.md b/libraries/WiFiClientSecure/README.md new file mode 100644 index 0000000000000000000000000000000000000000..10ac26ccaa50fff84686659436c4141cda025e3c --- /dev/null +++ b/libraries/WiFiClientSecure/README.md @@ -0,0 +1,67 @@ +WiFiClientSecure +================ + +The WiFiClientSecure class implements support for secure connections using TLS (SSL). +It inherits from WiFiClient and thus implements a superset of that class' interface. +There are three ways to establish a secure connection using the WiFiClientSecure class: +using a root certificate authority (CA) cert, using a root CA cert plus a client cert and key, +and using a pre-shared key (PSK). + +Using a root certificate authority cert +--------------------------------------- +This method authenticates the server and negotiates an encrypted connection. +It is the same functionality as implemented in your web browser when you connect to HTTPS sites. + +If you are accessing your own server: +- Generate a root certificate for your own certificate authority +- Generate a cert & private key using your root certificate ("self-signed cert") for your server +If you are accessing a public server: +- Obtain the cert of the public CA that signed that server's cert +Then: +- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your + CA or of the public CA +- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate + presented by the server, and then negotiates encryption for the connection + +Please see the WiFiClientSecure example. + +Using a root CA cert and client cert/keys +----------------------------------------- +This method authenticates the server and additionally also authenticates +the client to the server, then negotiates an encrypted connection. + +- Follow steps above +- Using your root CA generate cert/key for your client +- Register the keys with the server you will be accessing so the server can authenticate your client +- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your + CA or of the public CA, this is used to authenticate the server +- In WiFiClientSecure use setCertificate, and setPrivateKey (or the appropriate connect method) to + set your client's cert & key, this will be used to authenticate your client to the server +- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate + presented by the server, it will use the cert/key to authenticate your client to the server, and + it will then negotiate encryption for the connection + +Using Pre-Shared Keys (PSK) +--------------------------- + +TLS supports authentication and encryption using a pre-shared key (i.e. a key that both client and +server know) as an alternative to the public key cryptography commonly used on the web for HTTPS. +PSK is starting to be used for MQTT, e.g. in mosquitto, to simplify the set-up and avoid having to +go through the whole CA, cert, and private key process. + +A pre-shared key is a binary string of up to 32 bytes and is commonly represented in hex form. In +addition to the key, clients can also present an id and typically the server allows a different key +to be associated with each client id. In effect this is very similar to username and password pairs, +except that unlike a password the key is not directly transmitted to the server, thus a connection to a +malicious server does not divulge the password. Plus the server is also authenticated to the client. + +To use PSK: +- Generate a random hex string (generating an MD5 or SHA for some file is one way to do this) +- Come up with a string id for your client and configure your server to accept the id/key pair +- In WiFiClientSecure use setPreSharedKey (or the appropriate connect method) to + set the id/key combo +- When WiFiClientSecure connects to the target server it uses the id/key combo to authenticate the + server (it must prove that it has the key too), authenticate the client and then negotiate + encryption for the connection + +Please see the WiFiClientPSK example. diff --git a/libraries/WiFiClientSecure/examples/WiFiClientPSK/WiFiClientPSK.ino b/libraries/WiFiClientSecure/examples/WiFiClientPSK/WiFiClientPSK.ino new file mode 100644 index 0000000000000000000000000000000000000000..bd3ffbeda05d1f92eee155e472aa0ba7a43ba957 --- /dev/null +++ b/libraries/WiFiClientSecure/examples/WiFiClientPSK/WiFiClientPSK.ino @@ -0,0 +1,85 @@ +/* + Wifi secure connection example for ESP32 using a pre-shared key (PSK) + This is useful with MQTT servers instead of using a self-signed cert, tested with mosquitto. + Running on TLS 1.2 using mbedTLS + + To test run a test server using: openssl s_server -accept 8443 -psk 1a2b3c4d -nocert + It will show the http request made, but there's no easy way to send a reply back... + + 2017 - Evandro Copercini - Apache 2.0 License. + 2018 - Adapted for PSK by Thorsten von Eicken +*/ + +#include + +#if 0 +const char* ssid = "your-ssid"; // your network SSID (name of wifi network) +const char* password = "your-password"; // your network password +#else +const char* ssid = "test"; // your network SSID (name of wifi network) +const char* password = "securetest"; // your network password +#endif + +//const char* server = "server.local"; // Server hostname +const IPAddress server = IPAddress(192, 168, 0, 14); // Server IP address +const int port = 8443; // server's port (8883 for MQTT) + +const char* pskIdent = "Client_identity"; // PSK identity (sometimes called key hint) +const char* psKey = "1a2b3c4d"; // PSK Key (must be hex string without 0x) + +WiFiClientSecure client; + +void setup() { + //Initialize serial and wait for port to open: + Serial.begin(115200); + delay(100); + + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + WiFi.begin(ssid, password); + + // attempt to connect to Wifi network: + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + // wait 1 second for re-trying + delay(1000); + } + + Serial.print("Connected to "); + Serial.println(ssid); + + client.setPreSharedKey(pskIdent, psKey); + + Serial.println("\nStarting connection to server..."); + if (!client.connect(server, port)) + Serial.println("Connection failed!"); + else { + Serial.println("Connected to server!"); + // Make a HTTP request: + client.println("GET /a/check HTTP/1.0"); + client.print("Host: "); + client.println(server); + client.println("Connection: close"); + client.println(); + + while (client.connected()) { + String line = client.readStringUntil('\n'); + if (line == "\r") { + Serial.println("headers received"); + break; + } + } + // if there are incoming bytes available + // from the server, read them and print them: + while (client.available()) { + char c = client.read(); + Serial.write(c); + } + + client.stop(); + } +} + +void loop() { + // do nothing +} diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp index 5d6986b565d6e81f54a47c3d6bff4ef714803bab..5f1ab6a0e9d44a70db8ded5a76253b4b8fbbc7fd 100644 --- a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp @@ -39,6 +39,8 @@ WiFiClientSecure::WiFiClientSecure() _CA_cert = NULL; _cert = NULL; _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; next = NULL; } @@ -59,6 +61,8 @@ WiFiClientSecure::WiFiClientSecure(int sock) _CA_cert = NULL; _cert = NULL; _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; next = NULL; } @@ -89,11 +93,15 @@ void WiFiClientSecure::stop() int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { + if (_pskIdent && _psKey) + return connect(ip, port, _pskIdent, _psKey); return connect(ip, port, _CA_cert, _cert, _private_key); } int WiFiClientSecure::connect(const char *host, uint16_t port) { + if (_pskIdent && _psKey) + return connect(host, port, _pskIdent, _psKey); return connect(host, port, _CA_cert, _cert, _private_key); } @@ -104,7 +112,24 @@ int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *_CA_cert, int WiFiClientSecure::connect(const char *host, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key) { - int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key); + int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key, NULL, NULL); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { + return connect(ip.toString().c_str(), port,_pskIdent, _psKey); +} + +int WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { + log_v("start_ssl_client with PSK"); + int ret = start_ssl_client(sslclient, host, port, NULL, NULL, NULL, _pskIdent, _psKey); _lastError = ret; if (ret < 0) { log_e("start_ssl_client: %d", ret); @@ -223,6 +248,11 @@ void WiFiClientSecure::setPrivateKey (const char *private_key) _private_key = private_key; } +void WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) { + _pskIdent = pskIdent; + _psKey = psKey; +} + bool WiFiClientSecure::verify(const char* fp, const char* domain_name) { if (!sslclient) diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.h b/libraries/WiFiClientSecure/src/WiFiClientSecure.h index a9ba37617a34948b1980f36bbcca585bb385f1fe..17294b0d3510d82edc62e3e4592ef887bcc20a93 100644 --- a/libraries/WiFiClientSecure/src/WiFiClientSecure.h +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.h @@ -35,6 +35,8 @@ protected: const char *_CA_cert; const char *_cert; const char *_private_key; + const char *_pskIdent; // identity for PSK cipher suites + const char *_psKey; // key in hex for PSK cipher suites public: WiFiClientSecure *next; @@ -45,6 +47,8 @@ public: int connect(const char *host, uint16_t port); int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); 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(); size_t write(uint8_t data); size_t write(const uint8_t *buf, size_t size); @@ -55,6 +59,7 @@ public: void stop(); uint8_t connected(); int lastError(char *buf, const size_t size); + void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex void setCACert(const char *rootCA); void setCertificate(const char *client_ca); void setPrivateKey (const char *private_key); diff --git a/libraries/WiFiClientSecure/src/ssl_client.cpp b/libraries/WiFiClientSecure/src/ssl_client.cpp index f8736bd5dfd9afad7d8072fcd9b8e652ec3f0b8c..25bca6773657641cb6aeb3d34a7f2a8cb913d6c4 100644 --- a/libraries/WiFiClientSecure/src/ssl_client.cpp +++ b/libraries/WiFiClientSecure/src/ssl_client.cpp @@ -45,7 +45,7 @@ void ssl_init(sslclient_context *ssl_client) } -int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) +int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey) { char buf[512]; int ret, flags, timeout; @@ -116,6 +116,36 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p if (ret < 0) { return handle_error(ret); } + } else if (pskIdent != NULL && psKey != NULL) { + log_v("Setting up PSK"); + // convert PSK from hex to binary + if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) { + log_e("pre-shared key not valid hex or too long"); + return -1; + } + unsigned char psk[MBEDTLS_PSK_MAX_LEN]; + size_t psk_len = strlen(psKey)/2; + for (int j=0; j= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] = c<<4; + c = psKey[j+1]; + if (c >= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] |= c; + } + // set mbedtls config + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, + (const unsigned char *)pskIdent, strlen(pskIdent)); + if (ret != 0) { + log_e("mbedtls_ssl_conf_psk returned %d", ret); + return handle_error(ret); + } } else { mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); log_i("WARNING: Use certificates for a more secure communication!"); diff --git a/libraries/WiFiClientSecure/src/ssl_client.h b/libraries/WiFiClientSecure/src/ssl_client.h index 6575313d78e012405ecf3f58ee77bdae04f1793d..15b391fc2176bbec868e43d12b67ef93846011bf 100644 --- a/libraries/WiFiClientSecure/src/ssl_client.h +++ b/libraries/WiFiClientSecure/src/ssl_client.h @@ -29,7 +29,7 @@ typedef struct sslclient_context { void ssl_init(sslclient_context *ssl_client); -int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); +int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey); void stop_ssl_socket(sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); int data_to_read(sslclient_context *ssl_client); int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);