I cannot seem to make MQTT+TLS+self signed certs work on my ESP32.
I am using this "setup" in my home network without major hickups with paho-python and mosquitto_pub/sub. The latter works with the following bash command (note the insecure there):
mosquitto_pub -h 192.168.1.X -p 8883 --cafile /etc/mosquitto/ca_certificates/mosq_ca.crt --cert /etc/mosquitto/certs/mosq_client.crt --key /etc/mosquitto/certs/mosq_client.key --debug --insecure --topic "test/message" -m "off"
My broker's mosquitto config file contains these lines apart from cert paths and general settings:
tls_version tlsv1.3
require_certificate true
When I send the sketch to my ESP32, the result is that it is stuck in the reconnect() loop (see sketch below).
When I do not specify a client cert and key ( // for client verification rows are commented in setup()), I get the following error (I guess it is attributable to the config which requires certificate):
1594936874: New connection from 192.168.1.162 on port 8883.
1594936874: OpenSSL Error: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
1594936874: Socket error on client <unknown>, disconnecting.
When I do specify a client certificate, I get this:
1594936887: New connection from 192.168.1.162 on port 8883.
1594936888: Socket error on client <unknown>, disconnecting.
Not sure if it is a host name mismatch on the certificate or what. This would be my main tip, but I am uncertain. I also do not know if a similar option to --insecure can be specified for WiFiClientSecure class, so far I have not found anything like that. But I am a beginner, too.
My sketch if pretty unpolished yet, but it should be still readable:
#include <WiFiClientSecure.h>
#include <time.h>
#include <PubSubClient.h>
const char* ssid = "mySSID";
const char* password = "myPasswd";
const char* server = "192.168.1.X"; // Server URL
const char* MQTT_subscribe_topic = "test/esp32";
int timediff_hr = 2;
const char* root_ca = \
"-----BEGIN CERTIFICATE-----\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"-----END CERTIFICATE-----\n" ;
// You can use x.509 client certificates if you want
const char* test_client_key = \
"-----BEGIN CERTIFICATE-----\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"-----END CERTIFICATE-----\n" ;
const char* test_client_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"pem format broken into lines\n" \
"-----END CERTIFICATE-----\n" ;
WiFiClientSecure wifiClient;
time_t now;
void msgReceived(char* topic, byte* payload, unsigned int len);
PubSubClient pubSubClient(server, 8883, msgReceived, wifiClient);
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);
getTime();
wifiClient.setCACert(root_ca);
wifiClient.setCertificate(test_client_key); // for client verification
wifiClient.setPrivateKey(test_client_cert); // for client verification
}
unsigned long lastPublish;
int msgCount;
void loop() {
if (!pubSubClient.connected()) {
reconnect();
}
pubSubClient.loop();
if (millis() - lastPublish > 10000) {
String msg = String("Hello from ESP32: ") + ++msgCount;
boolean rc = pubSubClient.publish("outTopic", msg.c_str());
Serial.print("Published, rc="); Serial.print( (rc ? "OK: " : "FAILED: ") );
Serial.println(msg);
lastPublish = millis();
}
}
void msgReceived(char* topic, byte* payload, unsigned int length) {
Serial.print("Message received on "); Serial.print(topic); Serial.print(": ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void reconnect() {
Serial.print(F("PubSubClient attempting connection to: ")); Serial.println(server);
uint8_t i = 0;
while ( ! pubSubClient.connected()) {
// Attempt to connect
if (pubSubClient.connect("ESP32Client")) { //or "ESP8266Client"
Serial.println("connected");
// Subscribe
pubSubClient.subscribe(MQTT_subscribe_topic);
} else { //unsuccessful connect
Serial.print("failed, rc=");
Serial.print(pubSubClient.state());
Serial.print("\nNext try in 5 seconds: connection to "); Serial.println(server);
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void getTime(){
// Synchronize time useing SNTP. This is necessary to verify that
// the TLS certificates offered by the server are currently valid.
Serial.print("Setting time using SNTP");
configTime(timediff_hr * 3600, 0, "de.pool.ntp.org");
time_t now = time(nullptr);
while (now < 1000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
P.S.: The base of this sketch is a test sketch of mine for MQTT:1883 unsecured, which also works fine. So the issue should definitely be in the TLS part.
Your test_client_key contains a certificate.
It would be helpful if you post the sample code with keys and certificates. Obviously not the ones you will be using later, but as you are self signing, generate some test key material that you can post here.
The configuration of your broker would also be useful.
Related
i am trying to write a piece of code in which the ESP32 connect securely to my api-server and post some sensor data. For this i use the WifiClientSecure library.
Through trial and error i have found out that client.connect(www.myserver.com, 443); works.
But client.connect(www.myserver.com/api, 443); will throw an error.
I have searched the internet for a solution but can't manage to find one.
Full code:
#include <arduino.h>
#include <WiFiClientSecure.h>
const char* ssid = "filled out ssid"; // your network SSID
const char* password = "filled out password"; // your network password
const char* server = "www.server.com/api"; // Server URL
/* use
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
to get this certificate */
const char* ca_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n" \
"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \
"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \
"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n" \
"MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n" \
"BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n" \
"aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n" \
"dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" \
"AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n" \
"3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n" \
"tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n" \
"Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n" \
"VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n" \
"79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n" \
"c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n" \
"Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n" \
"c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n" \
"UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n" \
"Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n" \
"BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n" \
"A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n" \
"Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n" \
"VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n" \
"ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n" \
"8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n" \
"iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n" \
"Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n" \
"XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n" \
"qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n" \
"VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n" \
"L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n" \
"jjxDah2nGN59PRbxYvnKkKj9\n" \
"-----END CERTIFICATE-----\n" ;
/* create an instance of WiFiClientSecure */
WiFiClientSecure client;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
/* waiting for WiFi connect */
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}
Serial.print("Connected to ");
Serial.println(ssid);
/* set SSL/TLS certificate */
client.setCACert(ca_cert);
Serial.println("Connect to server via port 443");
if (!client.connect(server, 443)){
Serial.println("Connection failed!");
} else {
Serial.println("Connected to server!");
/* create HTTP request */
Serial.print("Waiting for response ");
while (!client.available()){
delay(50); //
Serial.print(".");
}
/* if data is available then receive and print to Terminal */
while (client.available()) {
char c = client.read();
Serial.write(c);
}
/* if the server disconnected, stop the client */
if (!client.connected()) {
Serial.println();
Serial.println("Server disconnected");
client.stop();
}
}
}
void loop() {
}
www.server.com/api is a partial URL. The complete URL will be https://www.server.com/api,
where:
https - This part is called scheme or protocol. HTTPS implies a default TCP port 443 (http mean port 80 by default)
www.server.com - The domain part, can be called also server address.
api- This is a path on the server
WiFiClient, or WiFiClientSecure, is the TCP client, it doesn't know anything about URLs. TCP client recognizes just address, given in form of IP address (192.168.1.20) or FQDN (which is then translated into IP address).
It is easier to use HTTPClient for requesting HTTP resources.
edit: FNDQ->FQDN
I tried modifying the basic Arduino code from here in order to send HTTP Requests to AWS API Gateway. While the example code from the link worked, I was not able to get a successful connection with AWS API Gateway.
I have tried a combination of things such as removing the https:// from server[], changing the port to 443 instead of 80, removing the /beta from server[], using client.connectSSL instead of client.connect, but none of these have worked so far.
The line:
int err = client.connect(server, 80);
returns me a value of 0.
There are no certificates set up with the AWS API Gateway, so I don't think it's a problem with that. Wifi works perfectly.
Any help would be greatly appreciated!
#include <SPI.h>
#include <WiFi101.h>
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or
use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
char server[] = "https://**********.execute-api.us-west-2.amazonaws.com/beta"; // name address for Google (using DNS)
WiFiClient client;
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWiFiStatus();
Serial.println("\nStarting connection to server...");
// if you get a connection, report back via serial:
int err = client.connect(server, 80);
Serial.println(err);
if (err) {
Serial.println("connected to server");
// Make a HTTP request:
client.println("GET /beta HTTP/1.1");
client.println("Host: https://**********.execute-api.us-west-2.amazonaws.com");
client.println("Connection: close");
client.println();
}
}
void loop() {
// if there are incoming bytes available
// from the server, read them and print them:
while (client.available()) {
char c = client.read();
Serial.write(c);
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting from server.");
client.stop();
// do nothing forevermore:
while (true);
}
}
void printWiFiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Tips:
(1) client.connect(fqdn, port) is expecting an FQDN for the first parameter
The example in the docs is client.connect("Arduino.cc", 80) this seems to work well in my tests. The docs say "URL" but they mean FQDN.
(2) If you need SSL then you MUST load up your certs using the firmware updater
first. If you are using non-standard pins for the WiFi101 board then you MUST use wifi.setPins() to set the pins or the firmware updater will fail. Adafruit Feather M0 1500 owners will know what I am talking about here.
Reference: https://www.arduino.cc/en/Reference/WiFi101ClientConnect
I hope this helps.
I originally asked a related question on security.stackexchange.com. Here's the MCVE.
Short version: when I use GnuTLS to verify an HTTPS connection to googleapis.com, it fails verification. For other sites (e.g. github.com), it succeeds.
I'm loading the /etc/ssl/certs/ca-certificates.crt file explicitly (in the real program, we cache it, rather than hit the filesystem every time).
The CA store was updated recently by Ubuntu. Prior to that update, the following code works. Since the update, it fails.
Ubuntu 14.04, compile with g++ -o gnutls-client gnutls-client.cpp -lgnutls
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <gnutls/x509.h>
#include <assert.h>
#define CURL_CA_BUNDLE "/etc/ssl/certs/ca-certificates.crt" // FAILS
//#define CURL_CA_BUNDLE "old-ca-certificates.crt" // WORKS
#define CHECK(x) assert((x) >= 0);
// Fails with sheets.googleapis.com
// Succeeds with (e.g.) github.com
int main(int argc, char *argv[])
{
if (argc < 2) {
exit(1);
}
const char *server_name = argv[1];
gnutls_global_init();
printf("gnutls-client (GnuTLS/%s)\n", gnutls_check_version(NULL));
gnutls_certificate_credentials_t creds = NULL;
CHECK(gnutls_certificate_allocate_credentials(&creds));
gnutls_certificate_set_verify_flags(creds,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
int certificateCount = gnutls_certificate_set_x509_trust_file(creds,
CURL_CA_BUNDLE, GNUTLS_X509_FMT_PEM);
if (certificateCount >= 0) {
printf("%d certificate(s) processed\n", certificateCount);
}
else {
printf("Failed to set trust file: %d\n", certificateCount);
exit(1);
}
gnutls_session_t session = NULL;
CHECK(gnutls_init(&session, GNUTLS_CLIENT));
CHECK(gnutls_server_name_set(session, GNUTLS_NAME_DNS,
server_name, strlen(server_name)));
CHECK(gnutls_set_default_priority(session));
CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, creds));
struct addrinfo hint, *addr;
memset(&hint, 0, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
getaddrinfo(server_name, "https", &hint, &addr);
int sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
CHECK(connect(sockfd, addr->ai_addr, addr->ai_addrlen));
gnutls_transport_set_int(session, sockfd);
int ret;
do {
ret = gnutls_handshake(session);
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
if (ret < 0) {
fprintf(stderr, "ret: %d\n", ret);
exit(1);
}
printf("Connected: %s\n", gnutls_session_get_desc(session));
unsigned int peercerts_size;
const gnutls_datum_t *peercerts = gnutls_certificate_get_peers(session,
&peercerts_size);
printf("Server presented %d certs\n", peercerts_size);
unsigned int verify_status;
CHECK(gnutls_certificate_verify_peers2(session, &verify_status));
printf("%d 0x%x\n", ret, verify_status);
assert(verify_status == 0x0);
return 0;
}
With the current CA bundle...
$ ./gnutls-client github.com
gnutls-client (GnuTLS/3.2.11)
148 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-GCM)
Server presented 2 certs
0 0x0
$ ./gnutls-client googleapis.com
gnutls-client (GnuTLS/3.2.11)
148 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-ECDSA-SECP256R1)-(AES-128-GCM)
Server presented 3 certs
0 0x42
gnutls-client: gnutls-client.cpp:82: int main(int, char**): Assertion `verify_status == 0x0' failed.
Aborted (core dumped)
With the previous CA bundle...
$ ./gnutls-client github.com
gnutls-client (GnuTLS/3.2.11)
173 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-GCM)
Server presented 2 certs
0 0x0
$ ./gnutls-client googleapis.com
gnutls-client (GnuTLS/3.2.11)
173 certificate(s) processed
Connected: (TLS1.2)-(ECDHE-ECDSA-SECP256R1)-(AES-128-GCM)
Server presented 3 certs
0 0x0
gnutls-cli, on the same machine, works fine:
$ gnutls-cli googleapis.com --x509cafile /etc/ssl/certs/ca-certificates.crt
Processed 148 CA certificate(s).
Resolving 'googleapis.com'...
Connecting to '108.177.119.105:443'...
- Certificate type: X.509
- Got a certificate list of 3 certificates.
- Certificate[0] info:
- subject `C=US,ST=California,L=Mountain View,O=Google Inc,CN=*.googleapis.com', issuer `C=US,O=Google Inc,CN=Google Internet Authority G2', RSA key 2048 bits, signed using RSA-SHA256, activated `2017-10-17 10:22:56 UTC', expires `2017-12-29 00:00:00 UTC', SHA-1 fingerprint `34e45ef97aadd3e73978790c2f16ce275a28cd1c'
- Certificate[1] info:
- subject `C=US,O=Google Inc,CN=Google Internet Authority G2', issuer `C=US,O=GeoTrust Inc.,CN=GeoTrust Global CA', RSA key 2048 bits, signed using RSA-SHA256, activated `2017-05-22 11:32:37 UTC', expires `2018-12-31 23:59:59 UTC', SHA-1 fingerprint `a6120fc0b4664fad0b3b6ffd5f7a33e561ddb87d'
- Certificate[2] info:
- subject `C=US,O=GeoTrust Inc.,CN=GeoTrust Global CA', issuer `C=US,O=Equifax,OU=Equifax Secure Certificate Authority', RSA key 2048 bits, signed using RSA-SHA1, activated `2002-05-21 04:00:00 UTC', expires `2018-08-21 04:00:00 UTC', SHA-1 fingerprint `7359755c6df9a0abc3060bce369564c8ec4542a3'
- The hostname in the certificate matches 'googleapis.com'.
- Peer's certificate is trusted
- Version: TLS1.2
- Key Exchange: RSA
- Cipher: AES-128-CBC
- MAC: SHA1
- Compression: NULL
- Handshake was completed
- Simple Client Mode:
^C
(Note the "Peer's certificate is trusted")
The "Equifax" certificate was removed in the update, but according to the security.stackexchange.com question, GnuTLS should see the intermediate "GeoTrust" certificate and treat that as a valid root.
What am I doing wrong?
... apt-get install libgnutls28-dev, which is the only relevant difference between my machine and a vanilla 14.04 box
There is a known problem with this version in exactly the situation you describe. For more details and a patch see gnutls28 in trusty no longer validates many valid certificate chains, such as google.com.
$ gnutls-cli googleapis.com --x509cafile /etc/ssl/certs/ca-certificates.crt
...
- Peer's certificate is trusted
gnutls-cli is still using the original gnutls version 2.12.23 which comes by default with Ubuntu 14.04 (see output of gnutls-cli -v). This version does not seem to be affected by the problem.
I have a raspberry pi 3 with raspbian stretch as its operating system. I have installed and fully configured a MQTT broker on the raspberry pi following this tutorial: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-the-mosquitto-mqtt-messaging-broker-on-ubuntu-16-04
Everything works fine and well on the broker's side. The certificates get renewed after 60 days and you can only connect to port 1883 via the localhost and the other ports (8883 and 8083) are open but can only be accessed using TLS version 1.2 and for the latter also using websockets. Below you can find the code of my configuration of mosquitto (/etc/mosquitto/conf.d/default.conf).
allow_anonymous false
password_file /etc/mosquitto/passwd
listener 1883 localhost
listener 8883
certfile /etc/letsencrypt/live/home.kamidesigns.be/cert.pem
cafile /etc/letsencrypt/live/home.kamidesigns.be/chain.pem
keyfile /etc/letsencrypt/live/home.kamidesigns.be/privkey.pem
tls_version tlsv1.2
listener 8083
protocol websockets
certfile /etc/letsencrypt/live/home.kamidesigns.be/cert.pem
cafile /etc/letsencrypt/live/home.kamidesigns.be/chain.pem
keyfile /etc/letsencrypt/live/home.kamidesigns.be/privkey.pem
tls_version tlsv1.2
I also bought a ESP8266 Wemos D1 Mini to connect to this broker in a secure way. I used the pubsubclient library from this link: https: //github.com/knolleary/pubsubclient for my MQTT client.
I use the master branch of this link: https://github.com/esp8266/Arduino for my secure SSL connection. Below you see the code I used for programming my Wemos D1 Mini
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
const char* ssid = "ssid";
const char* password = "wifipassword";
const char* host = "home.kamidesigns.be";
const int port = 8883;
WiFiClientSecure espClient;
PubSubClient client(host, port, callback, espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.print("connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Synchronize time useing SNTP. This is necessary to verify that
// the TLS certificates offered by the server are currently valid.
Serial.print("Setting time using SNTP");
configTime(8 * 3600, 0, "pool.ntp.org", "time.nist.gov");
time_t now = time(nullptr);
while (now < 1000) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266LightController","username","password")) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "hello world");
// ... and resubscribe
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
When I start my Wemos D1, the serial monitor says:
connecting to ssid
..
WiFi connected
IP address:
192.168.0.213
Setting time using SNTP.
Current time: Sat Oct 14 02:26:25 2017
Attempting MQTT connection...connected
This is good and it is exactly what I wanted but I'm confused by how my Wemos D1 is able to connect to port 8883 without it verifying the certificate chain of the server? Remember that I never uploaded a certificate to the Wemos D1 or implemented a certificate into the code, and still it can connect.
One of 2 options
The WiFiClientSecure includes a list of public CA certs and is verifying your certificate against this list
The WiFiClientSecure defaults to not verifying remote certs by default.
Looking at this issue it looks like option 2 is most likely as it implies you have to verify the cert yourself after the connection.
I am developing my custom browser in Qt using QWebView and
I am trying to make my own root cert store of trusted certificates which are taken from mozilla project.
I have used QSslSocket::setDefaultCaCertificates() to override the default certificates.
But I am not able to load https://www.gmail.com , where as in mozilla it works.
I have set all required root certs for gmail to my store.
can anyone guide me ?
The reason you can't connect is because the SSL certificate (with serial 2F:DF:BC:F6:AE:91:52:6D:0F:9A:A3:DF:40:34:3E:9A) presented to you when you connect to www.gmail.com is issued for a different domain - www.google.com. This has nothing to do with root CA certificate store because no root CA certificate is needed to compare cert's Subject CN field with the host you are trying to connect to. You can ignore this and other SSL errors by calling
void QNetworkReply::ignoreSslErrors () [virtual slot]
To avoid this error you can connect directly to https://mail.google.com which is the domain you are being redirected to when you try to connect to https://www.gmail.com
Below is a working example which will show you the exact SSL errors and QNAM level errors. Either line B1 or line B2 must be active at the same time. You can comment line A if you want to see what happens with the default (system) root CA certificate store. There are two certs used by this code; CA's cert with serial 30:00:00:02 should be placed in a file called ThawteSGCCA.crt and CA's cert with serial 70:BA:E4:1D:10:D9:29:34:B6:38:CA:7B:03:CC:BA:BF should be placed in a file called BuiltinObjectToken-VerisignClass3PublicPrimaryCertificationAuthority.crt.
#include <QtGui/QApplication>
#include <QtCore/QDebug>
#include <QtCore/QList>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslSocket>
#include <QtNetwork/QSslError>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebPage>
class Handler : public QObject{
Q_OBJECT
public slots:
void slotLoadFinished(bool ok) {
if (ok) {
qDebug() << "Page size: " << static_cast<QWebPage*>(sender())->mainFrame()->toHtml().size();
}
}
void slotFinished(QNetworkReply * reply) {
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "connected to " << reply->url();
qDebug() << "HTTP status: " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
} else {
qDebug() << "error while connecting to " << reply->url();
qDebug() << "error code: " << reply->error();
qDebug() << "error string: " << reply->errorString();
}
}
void slotSslErrors(QNetworkReply * reply, QList<QSslError> const & errors) {
qDebug() << "SSL errors: " << errors;
qDebug() << "peer's certificate: "
<< reply->sslConfiguration().peerCertificate();
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Handler handler;
// CA certs for:
// 1. cert with Subject.CN == mail.google.com cert with serial 1f:19:f6:de:35:dd:63:a1:42:91:8a:d5:2c:c0:ab:12
// 2. cert with Subject.CN == www.google.com cert with serial 2F:DF:BC:F6:AE:91:52:6D:0F:9A:A3:DF:40:34:3E:9A
QList<QSslCertificate> CAcerts =
// serial 30:00:00:02
QSslCertificate::fromPath("ThawteSGCCA.crt") +
// serial 70:BA:E4:1D:10:D9:29:34:B6:38:CA:7B:03:CC:BA:BF
QSslCertificate::fromPath("BuiltinObjectToken-VerisignClass3PublicPrimaryCertificationAuthority.crt");
qDebug() << "root CA certificates:\n"
<< CAcerts
<< "\n";
QSslSocket::setDefaultCaCertificates(CAcerts); // line A
QWebPage page;
// OK because cert with serial 1f:19:f6:de:35:dd:63:a1:42:91:8a:d5:2c:c0:ab:12 is for host mail.google.com
// page.mainFrame()->load(QUrl("https://mail.google.com")); // line B1
// SSL ERROR "The host name did not match any of the valid hosts for this certificate"
// because cert with serial 1f:19:f6:de:35:dd:63:a1:42:91:8a:d5:2c:c0:ab:12 is NOT for www.gmail.com
page.mainFrame()->load(QUrl("https://www.gmail.com")); // line B2
QObject::connect(page.networkAccessManager(), SIGNAL(finished(QNetworkReply*)), &handler, SLOT(slotFinished(QNetworkReply*)));
QObject::connect(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), &handler, SLOT(slotSslErrors(QNetworkReply*,QList<QSslError>)));
QObject::connect(&page, SIGNAL(loadFinished(bool)), &handler, SLOT(slotLoadFinished(bool)));
return app.exec();
}
#include "main.moc"