WiFiClientSecure: add support for PSK (pre-shared key) ciphers (#2133)

* WiFiClientSecure: add support for PSK (pre-shared key) ciphers

* add example for WiFiClientSecure PSK

* WiFiClientSecure: added README
This commit is contained in:
Thorsten von Eicken 2018-12-03 07:17:55 -08:00 committed by Me No Dev
parent 5cfff190e9
commit af7e489f01
6 changed files with 220 additions and 3 deletions

View File

@ -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.

View File

@ -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 <WiFiClientSecure.h>
#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
}

View File

@ -39,6 +39,8 @@ WiFiClientSecure::WiFiClientSecure()
_CA_cert = NULL; _CA_cert = NULL;
_cert = NULL; _cert = NULL;
_private_key = NULL; _private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL; next = NULL;
} }
@ -59,6 +61,8 @@ WiFiClientSecure::WiFiClientSecure(int sock)
_CA_cert = NULL; _CA_cert = NULL;
_cert = NULL; _cert = NULL;
_private_key = NULL; _private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL; next = NULL;
} }
@ -89,11 +93,15 @@ void WiFiClientSecure::stop()
int WiFiClientSecure::connect(IPAddress ip, uint16_t port) 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); return connect(ip, port, _CA_cert, _cert, _private_key);
} }
int WiFiClientSecure::connect(const char *host, uint16_t port) 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); 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 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; _lastError = ret;
if (ret < 0) { if (ret < 0) {
log_e("start_ssl_client: %d", ret); log_e("start_ssl_client: %d", ret);
@ -223,6 +248,11 @@ void WiFiClientSecure::setPrivateKey (const char *private_key)
_private_key = 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) bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
{ {
if (!sslclient) if (!sslclient)

View File

@ -35,6 +35,8 @@ protected:
const char *_CA_cert; const char *_CA_cert;
const char *_cert; const char *_cert;
const char *_private_key; const char *_private_key;
const char *_pskIdent; // identity for PSK cipher suites
const char *_psKey; // key in hex for PSK cipher suites
public: public:
WiFiClientSecure *next; WiFiClientSecure *next;
@ -45,6 +47,8 @@ public:
int connect(const char *host, uint16_t port); 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(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(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(uint8_t data);
size_t write(const uint8_t *buf, size_t size); size_t write(const uint8_t *buf, size_t size);
@ -55,6 +59,7 @@ public:
void stop(); void stop();
uint8_t connected(); uint8_t connected();
int lastError(char *buf, const size_t size); 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 setCACert(const char *rootCA);
void setCertificate(const char *client_ca); void setCertificate(const char *client_ca);
void setPrivateKey (const char *private_key); void setPrivateKey (const char *private_key);

View File

@ -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]; char buf[512];
int ret, flags, timeout; 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) { if (ret < 0) {
return handle_error(ret); 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<strlen(psKey); j+= 2) {
char c = psKey[j];
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<<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 { } else {
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE);
log_i("WARNING: Use certificates for a more secure communication!"); log_i("WARNING: Use certificates for a more secure communication!");

View File

@ -29,7 +29,7 @@ typedef struct sslclient_context {
void ssl_init(sslclient_context *ssl_client); 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); 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 data_to_read(sslclient_context *ssl_client);
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len); int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);