今、調べながら試しているけどchatgptに存在しないメソッド言われたりと騙されるっぽいのでメモを残す。終了
ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。
環境
- Windows11
- Arfuino IDE 2.3.1
- esp8266 by ESP8266 Community 3.1.2(レポジトリ)
動きそうなやつ(これから確認、動いたら記事修正する)
WiFiClientSecureBearSSL#setInsecure
基本こちらのまんまで動いた。(リンク先はESP8266でなくESP32なので他を参考にするときはクラス仕様違うので注意)
最初は戻り値にバイト数とか入ってたけどサーバ側でcontents-length指定していないのが原因だった。
BearSSL_Validation.ino
Example->Esp8266Wifiの中にいるサンプル。
単独のCAをツールでヘッダーファイルに変えて読み込むやつだと思う。
includeで気になった奴
#include <StackThunk.h>というのがある。(実装のcpp)
StackThunk.c - Allow use second stack for BearSSL calls BearSSL uses a significant amount of stack space, much larger than the default Arduino core stack. These routines handle swapping between a secondary, user-allocated stack on the heap and the real stack.
BearSSLはスタックすごい使うからうんぬんと。通常のスタックだと足りないから、ヒープを使ってごにょごにょしてくれるらしい。
サンプルソースだと割り当て関連の機能は使っておらず使用量確認のみに見えるけど中で読んでいるかも。一見、足りてそうに見えるときも内部でこいつが使い切ってるとかはあるかもしれないので心に留めておく。
事前準備
cert.pyを使用して証明書のデータを更新。(長くなったのでこちら)
フィンガープリントだけで接続
フィンガープリント設定すればとりあえずつながる。
const char fingerprint_www_example_org [] PROGMEM = "4d:a2:5a:6d:5e:f6:2c:5f:95:c7:bd:0a:73:ea:3c:17:7b:36:99:9d";
#define FINGERPRINT fingerprint_www_example_org
BearSSL::WiFiClientSecure client;
client.setFingerprint(FINGERPRINT);
fetchURL(&client, SSL_host, SSL_port, path);
何もないよりましだといっている
The SHA-1 fingerprint of an X.509 certificate can be used to validate it instead of the while certificate. This is not nearly as secure as real X.509 validation, but is better than nothing.
自己署名証明書で接続
allowSelfSignedCertすればOKとのこと。
BearSSL::WiFiClientSecure client;
client.allowSelfSignedCerts();
fetchURL(&client, "self-signed.badssl.com", 443, "/");
こいつは自己署名は誰でも作れるから注意を(略
It is also possible to accept *any* self-signed certificate. This is absolutely insecure as anyone can make a self-signed certificate.
ハードコード公開鍵で接続
const char pubkey_www_example_org [] PROGMEM = R"PUBKEY(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz(略)
#define PUBKEY pubkey_www_example_org
BearSSL::WiFiClientSecure client;
BearSSL::PublicKey key(PUBKEY);
client.setKnownKey(&key);
fetchURL(&client, SSL_host, SSL_port, path);
公開鍵をハードコードしておいてそいつを使って(通常のSSLは認証プロセスを飛ばして)通信できる。一般公開しないサービスならこれでもよさそうな気がする。
The server certificate can be completely ignored and its public key hardcoded in your application. This should be secure as the public key needs to be paired with the private key of the site, which is obviously private and not shared. A MITM without the private key would not be able to establish communications.
certをハードコード方式(多分一番固い)
const char pubkey_www_example_org [] PROGMEM = R"PUBKEY(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz
#define CERT cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1
BearSSL::X509List cert(CERT);
client.setTrustAnchors(&cert);
setClock();// 有効期間確認用に時刻設定(詳細はソース確認)
fetchURL(&client, SSL_host, SSL_port, path);
certをハードコードして検証する方式。
有効期限チェックはするけれどもCRLはサポートしていないらしい。発行された証明書が有効期限内に無効化されていてもチェックされない。自己署名証明書をルートにしたチェーンも作れるらしい。
A specific certification authority can be passed in and used to validate a chain of certificates from a given server. These will be validated using BearSSL's rules, which do NOT include certificate revocation lists. A specific server's certificate, or your own self-signed root certificate can also be used. ESP8266 time needs to be valid for checks to pass as BearSSL does verify the notValidBefore/After fields.
//#define CERT cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1
#define CERT cert_DigiCert_Global_Root_G2
上のようにcertをルート証明書にしても動くはずだがレスポンスとりそびれるときがあるのに気づいた。ルートじゃなくてサーバのcertを使っているときも取りそびれ発生しているかもしれない。原因調査未。とり急ぎ調査はおいておいて他の方式の確認進める。と、思ったら方式別でも発生している。=> 解決方法
暗号化方式の選択
早さ優先したいときとか
client.setInsecure();
ノーチェック
client.setCiphersLessSecure();
以下のやつら指定(と思う)
// For apps which want to use less secure but faster ciphers, only static const uint16_t faster_suites_P[] PROGMEM = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_RSA_WITH_AES_128_CBC_SHA256, BR_TLS_RSA_WITH_AES_256_CBC_SHA, BR_TLS_RSA_WITH_AES_128_CBC_SHA };
BR_TLS_RSA_WITH_AES_256_CBC_SHA256
: RSA鍵交換、AES-256-CBC暗号、SHA-256ハッシュBR_TLS_RSA_WITH_AES_128_CBC_SHA256
: RSA鍵交換、AES-128-CBC暗号、SHA-256ハッシュBR_TLS_RSA_WITH_AES_256_CBC_SHA
: RSA鍵交換、AES-256-CBC暗号、SHA-1ハッシュBR_TLS_RSA_WITH_AES_128_CBC_SHA
: RSA鍵交換、AES-128-CBC暗号、SHA-1ハッシュ
自分で指定
std::vector<uint16_t> myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA };
client.setInsecure();
client.setCiphers(myCustomList);
使えるリストはおそらくこちら。(さらに制限かかっているかもしれないが細かくは追っていない)
ちなみにサンプルソースのwww.example.comはsha1のBR_TLS_RSA_WITH_AES_256_CBC_SHAとBR_TLS_RSA_WITH_AES_128_CBC_SHAは通じたがsha256では通じなかったので上の指定だと接続エラーになった。
つなげるサーバの証明書を取得
こちらを参照
変数名サンプルソースと違うので適宜修正
証明書のアップロード
初めにアップロード用ツールのインストール。(手順はこちら)
IDEから[Ctrl] + [Shift] + [P]から ‘Upload Little FS to Pico/ESP8266‘ を選択し、(リセットボタン押したりするプログラム書き込みモードで)アップロード実行。
手順の方にも書いたけどシリアルモニター開いていると競合して書きこめないので注意
動作確認
サイトのドメイン入れて実行。大体の場合、htmlソースの表示がレスポンス待ち処理が足りなくてされない。 解決方法
課題
Upload LittleFSだとファイルシステムがすべて上書きされるらしい。一部差し換え的なことがやりたい。ファイルシステムルート直下のcerts.idxとcerts.arを差し替えたらよさそうだがcerts.idxがどこでできているのかまだ理解していない。CertStore::initCertStoreの中でcerts.idx作成しているのでcerts.arだけさしかえればよさそう。失敗したら接続できなくなりそうだけど、ネット越しにファイル放り込んで再起動とかもできそうかな。
ESP8266HTTPClientへの連携
以下の感じでcertを設定済みのWiFiClientSecureをHTTPClient のbeginメソッドに渡してやると動いた
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <CertStoreBearSSL.h>
(略)
BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure();
// Integrate the cert store with this connection
bear->setCertStore(&certStore);
HTTPClient https;
if(https.begin(*bear,"https://mail.epea.co.jp/api/reserve_count")){
以下蛇足
Chatgptに言われたけどなさそうな奴
無いっぽい奴1
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
include "x509_crt_bundle.h" // して
WiFiClientSecure client;
client.setCACertBundle(x509_crt_bundle);
とchatgptにいわれた。が、
C:\Users\kitam\Desktop\https\https.ino: In function 'void setup()': https:32:10: error: 'class BearSSL::WiFiClientSecure' has no member named 'setCACertBundle' 32 | client.setCACertBundle(x509_crt_bundle); | ^~~~~~~~~~~~~~~ exit status 1 'class BearSSL::WiFiClientSecure' has no member named 'setCACertBundle'
ないよと。ソース見ると確かにいない。なお、ESP32ならこれで動く模様。(参考)
無いっぽい奴2
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include "x509_crt_bundle.h" // して
WiFiClientSecure client;
client.addServerKey(x509_crt_bundle);
といわれたが、同じように
C:\Users\kitam\Desktop\https\https.ino: In function 'void setup()': C:\Users\kitam\Desktop\https\https.ino:32:9: error: 'class BearSSL::WiFiClientSecure' has no member named 'addServerKey' 32 | client.addServerKey(x509_crt_bundle); | ^~~~~~~~~~~~ exit status 1 Compilation error: 'class BearSSL::WiFiClientSecure' has no member named 'addServerKey'
addServerKeyはぱっとみESP32のライブラリにもなさそうなのでchatgptがどこから拾ってきたかよくわからない。