ESP32でrootCAバンドルを利用してhttps通信をする

概要

ESP32でHTTPS通信をするのは結構たいへんです。証明書を本体に保存する必要があるのですが、通常は中間CAを保存するので、証明書の発行元が変わるとエラーになって使えません。

今回はMozillaのrootCAバンドルを使わせてもらいました。

rootCAを使える形に変換する

wget https://raw.githubusercontent.com/espressif/esp-idf/master/components/mbedtls/esp_crt_bundle/gen_crt_bundle.py
wget https://curl.se/ca/cacert.pem
python gen_crt_bundle.py -i cacert.pem
xxd -i x509_crt_bundle | sed "s/unsigned/const unsigned/g" >x509_crt_bundle.cpp
echo "extern const unsigned char x509_crt_bundle[];" > x509_crt_bundle.h

上記で変換が可能です。gen_crt_bundle.pyが変換をするスクリプトで、cacert.pemがrootCAになります。変換後にxxdを使ってバイナリファイルからC言語の配列に変換しています。

中間CAもルートCAもそれなりに更新されるのですが、今回利用するのは複数のルートCAが入ったバンドルファイルになります。通常中間CAは複数のルートCAに署名されているので、特定のルートCAが期限切れになっても他のルートCAで救われます。単体のCAのみを利用している場合には有効期限切れなどで利用できなくなるので、複数の証明書バンドルを使う方が安全です。

完成物

上記に全ファイルがあります。

const unsigned char x509_crt_bundle[] = {
  0x00, 0x91, 0x00, 0x36, 0x01, 0x26, 0x30, 0x34, 0x31, 0x0b, 0x30, 0x09,
  0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x46, 0x52, 0x31, 0x12, 0x30,
  0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x44, 0x68, 0x69, 0x6d,

上記のように配列になっているrootCAバンドルをプログラムでセットするだけになります。現在の容量は66563バイトでした。それほど大きくはありません。

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include "x509_crt_bundle.h"

プログラムの先頭でx509_crt_bundle.hを読み込んでいます。

extern const unsigned char x509_crt_bundle[];

このファイルには配列の定義が書いてあるだけです。

void loop() {
  static uint8_t urlIndex = 0;
  WiFiClientSecure* client = new WiFiClientSecure;

  if (client) {
    HTTPClient https;
    client->setCACertBundle(x509_crt_bundle);

HTTPSを呼び出す前にWiFiClientSecureクラスに対してsetCACertBundle(x509_crt_bundle)でrootCAバンドルのデータをセットするだけで使えます。

原理

ESP x509 Certificate Bundle - ESP32 - — ESP-IDF Programming Guide latest documentation

上記に書いてありますが、本来はESP-IDFのmenuconfigを利用すると自動的に組み込まれます。Arduinoだと準備されていないので、自分でファイルを作成しました。

https://curl.se/docs/caextract.html

あとはrootCAバンドル自体も定期的に更新されます。本来はOTAでファームウエア全体やrootCAバンドルファイルだけをSPIFFSとかに保存して定期的に更新したほうが好ましいです。

まとめ

中間CAを利用するのは非常に面倒なのでやめたほうがいいです。

https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino

たとえば正規のhttpsのアクセス方法は上記のスケッチ例になりますが、3年前のものですでにスケッチ例にある中間CAだとエラーになってアクセスできません。

rootCAバンドルを使うことで、ほとんどのサイトへのアクセスが可能になると思いますが、それでも定期的に更新しないと有効期限がきて再発行した証明書などがエラーになる可能性があります。

公式ライブラリ化して、includeだけすればいいようにしようとしたのですが、ライセンスと更新が面倒な気がしたので変換方法だけを記載してみました。

コメント