Garage Door Opener – Connecting the ESP8266 with TLS
While I want to do full CA verification, I’m waiting on some of the bugs to get ironed out of the ESP8266 Arduino library, so I’ll take a shortcut for the moment, and use fingerprinting to verify the server certificate (It should be pretty easy to move to CA verification down the track).
To do this, we need three pieces of information:
- Client certificate
- Client secret key
- Server fingerprint
We have already generated the certificate and the secret key, so let”s generate a fingerprint for the server certificate. We can use OpenSSL to do this:
openssl x509 -in mosquitto/etc/certs/mqtt.local.crt.pem -sha1 -noout -fingerprint
This will generate a 160bit sha1 string, that may look a little like this:
SHA1 Fingerprint=DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09
Convert PEM to DER
I was reading through the docs, and while the axTLS says it supports PEM, I found that I needed to convert the PEM format certificate (which is ASCII based) to DER which is binary.
Again, OpenSSL to the rescue:
openssl x509 -outform der -in garage.local.csr.pem -out garage.local.csr.der
openssl x509 -outform der -in garage.local.key.pem -out garage.local.key.der
Loading the certificates
Down the track, I’ll be able to upload the certificate and key via the administration console (certificates expire, and need to be updated), which means storing them in the code makes little sense. Instead I’m going to store them on the flash as a file.
Thankfully there is a tool that makes uploading files to the SPIFFS filesystem super simple. Gratuitously stolen from the Filesystem Read Me:
ESP8266FS is a tool which integrates into the Arduino IDE. It adds a menu item to Tools menu for uploading the contents of sketch data directory into ESP8266 flash file system.
- Download the tool: https://github.com/esp8266/arduino-esp8266fs-plugin/releases/download/0.2.0/ESP8266FS-0.2.0.zip.
- In your Arduino sketchbook directory, create tools directory if it doesn’t exist yet
- Unpack the tool into tools directory (the path will look like_<home_dir>/Arduino/tools/ESP8266FS/tool/esp8266fs.jar_)
- Restart Arduino IDE
- Open a sketch (or create a new one and save it)
- Go to sketch directory (choose Sketch > Show Sketch Folder)
- Create a directory named data and any files you want in the file system there
- Make sure you have selected a board, port, and closed Serial Monitor
- Select Tools > ESP8266 Sketch Data Upload. This should start uploading the files into ESP8266 flash file system. When done, IDE status bar will display SPIFFS Image Uploaded message.
Copy the two der files in to the data directory, and run the data upload function (I renamed them client.key.der and client.crt.der because that kind of makes more sense in this context). To load the files, the code looks a little like this:
// We need to use WiFiCLientSecure to the encryption works
WiFiClientSecure wifiClient
SPIFFS.begin();
File ca = SPIFFS.open("/client.crt.der", "r");
if(!ca) {
Serial.println("Couldn't load cert");
return;
}
if(wifiClient.loadCertificate(ca)) {
Serial.println("Loaded Cert");
} else {
Serial.println("Didn't load cert");
return;
}
File key = SPIFFS.open("/client.key.der", "r");
if(!key) {
Serial.println("Couldn't load key");
return;
}
if(wifiClient.loadPrivateKey(key)) {
Serial.println("Loaded Key");
} else {
Serial.println("Didn't load Key");
}
The device will connect at this point, and the mosquitto server will be happy. It will use the name on the certificate as the client name, and the connection will be encrypted.
However, I not verifing the server yet. I will do that using the verify function
PubSubClient client("mqtt.local", 8883, callback, wifiClient); //set MQTT port number to 8883 as per //standard
wifiClient.connect("mqtt.local", 8883);
if(wifiClient.verify("DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09", "mqtt.local")) {
Serial.println("connection checks out");
wifiClient.stop();
if(client.connect("garage")) {
Serial.println("Connected");
client.subscribe("test");
client.publish("test", "hello world");
} else {
Serial.println("Not connected");
}
} else {
wifiClient.stop();
Serial.println("connection doesn't check out");
}
Notice that all of the colons have been replace with spaces.
Because of the way the PubSubClient works, I need to make a (pre?)connection to the server to do the verification. It’s pretty simple – connect, verify and disconnect, then continue with the PubSub connection.
There is a problem here – the mDNS name won’t actually resolve at this point. Because the DNS resolver doesn’t support it.
Ok, that isn’t strictly true – every DNS resolver CAN resolve mDNS, but you need to hit a special DNS server and port (224.0.0.251 on port 5353), which I haven’t worked out how to do with the Arduino library yet…