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:
parent
5cfff190e9
commit
af7e489f01
67
libraries/WiFiClientSecure/README.md
Normal file
67
libraries/WiFiClientSecure/README.md
Normal 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.
|
@ -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
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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!");
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user