Arduino公式ページのブログを見ていたら、BearSSLをArduino用に移植した「ArduinoBearSSL」というSSL通信を行うためのライブラリがあることを知りました。
ESPマイコンならともかく、標準的なArduinoボードではメモリや処理能力的な問題でSSL通信はできないものと思っていたので、興味本位で「ArduinoBearSSL」ライブラリを使ってSSL通信できるかどうか少し試してみることにしました。
●結果
先に実験結果を言ってしまいますと、
Arduino MKR ZERO + Ethernetシールド(有線LAN接続)
の組み合わせでもSSL通信は可能っぽいです。
(自分の実験環境では厳密にSSL通信ができている事を確認する方法が無いため、通信ログなどを見る限りではおそらくSSL通信できているだろう、と言う感触です)
ArduinoBearSSLの内部に使用されている「ArduinoECCX08」という暗号化チップを制御するライブラリがMKRシリーズなどのARM系のCPU向けに作られているようで、AVR系のArduino UNOやMEGAなどではエラーが出てしまいコンパイルすることができませんでした。
ArduinoBearSSLライブラリの説明文にも「このライブラリはArduinoECCC08に依存します」のようなことが記載されています。
ただ、Arduino MKR ZEROでは暗号化チップは未実装になっているので、暗号化チップなしでもSSL通信自体はできるようです。
(日本では技適が取得されていないため、国内販売がされていないArduino NANO 33 IOTなどのIoT系ボードには暗号化チップが実装されています)
推測ですが、暗号化チップは主にArduino IoT Cloudに接続する時のセキュリティ用に使用されているのではと思います。
(Arduino IoT Cloudに接続できるボードが限られているのはこのためなのかもしれません)
●SSL通信実験
ArduinoBearSSLライブラリをArduino IDEにインストールするとサンプルスケッチも付属しているため、クライアントとして動作するサンプルスケッチ(MKRGSMSSLClient.ino)を有線LAN用に変更してみることにしました。
<MKRGSM.h>に関連する処理を <Ethernet.h>の処理に置き換える
元々、Arduino MKR GSM 1400ボード用のサンプルスケッチとなっているので、<MKRGSM.h>に関連する部分を Ethernetシールド用のライブラリ<Ethernet.h>で動作する処理に置き換えます。
- <MKRGSM.h> → <Ethernet.h>
- GSMClient client; → EthernetClient client;
- MACアドレスやIPアドレスといったEthernet接続するために必要な内容、初期化処理を追加 などなど。
現在時刻が合っていないとSSL通信できない
あまりSSL通信の内容には詳しく無いのですが、サーバへ接続時に現在時刻が合っていないとSSL通信でのサーバ接続自体ができないようです。
サンプルスケッチでも、まず始めに3G通信で現在時刻を取得して、その結果をArduinoBearSSLに渡す処理を行なっています。
なので、今回のArduino MKR ZERO + Ethernetシールドでもどうにかして現在時刻を取得する必要があります。
NTPで現在時刻を取得
Ethernetライブラリでは現在時刻を取得するAPIは用意されていないので、NTPで現在時刻を取得する処理を追加しました。
NTPに関するサンプルスケッチ(UdpNtpClient.ino)はEthernetライブラリに付属しているので、これを参考にできます。
具体的には
- NTPで取得した時刻データをunsigned long型の戻り値として返す関数を作成
- 下記のようにArduinoBearSSLに渡す
ArduinoBearSSL.onGetTime(関数名); //"関数名"には1で作成した関数名を入れる
変更したスケッチを実際に動かしてみる
上記の変更を行った上で、ミスがなければコンパイルして書き込みができるはずです。
Arduino MKR ZERO用のEthernetシールドは持っていなかったので、Arduino UNO用のEthernetシールドをArduino MKR ZEROに接続して動かしてみました。
サンプルスケッチでは「www.google.com」へGETリクエストを行う内容(GoogleのサーバとHTTPS通信を行う内容)になっています。
接続した有線LANがインターネットへ接続できる環境であれば、通信内容がシリアルで表示されます。
シリアルで表示された通信ログを見る限りはSSL通信が行えているように見えます。
iw回路設計のwebサイトにも接続してみる
このiw回路設計のウェブサイト(iw-circuitdesign.net)も一応SSL化しているので、接続できるかやってみました。
- 接続先サーバの情報を「iw-circuitdesign.net」に変更します。
char server[] = "iw-circuitdesign.net";
- サーバ接続後にGETリクエストを下記のように変更します。
sslClient.println("GET / HTTP/1.1");
sslClient.println("Host: iw-circuitdesign.net");
sslClient.println("Connection: close");
sslClient.println("");
それっぽい応答が返ってきていますので、多分SSL通信できているものと思います。
●自己証明書を使ったサーバにはそのままでは接続できない
外部のサーバに接続できるならローカルで使用しているwebサーバ にも接続できるだろう、
と思って自己証明書(いわゆるオレオレ証明書)を使用してSSL通信を有効化した実験用のローカルサーバで試してみたところ、どうにも接続することができませんでした。
最初はサーバの設定がうまくないのかなと思って、色々いじっていたのですがどうやっても接続できず。。
結局のところ、ArduinoBearSSLの「BearSSLTrustAnchors.h」に登録されていない証明書では接続できないようになっているみたいでした。
デフォルトではいくつかの主要な認証局の証明書が登録されているようです。
ArduinoBearSSLライブラリのファイルが保存されているフォルダ内の「extras/TrustAnchors」フォルダに保存されている証明書が「BearSSLTrustAnchors.h」に登録されています。
ただ、証明書ファイルを直接読み込んでいるわけではなく、「BearSSLTrustAnhors.h」というヘッダファイルに記載できる形に変換してファームウェア(Arduinoスケッチ)に埋め込む形になっています。
googleやiw回路設計のウェブサイトに接続できたのは、デフォルトで登録されている証明書によって正しい証明書であることが確認できたためと考えられます。
BearSSLTrustAnchors.h に自己証明書を登録する
自己証明書を使ったローカルなサーバにも接続できるようにするためには、BearSSLTrustAnchors.hに自己証明書を登録する必要があります。
自己証明書ファイルの内容はそのままでは内容を見ることはできないため、「brssl」というコマンドラインで動作する変換ツールを使用して「BearSSLTrustAnchors.h」に記載できる形式に変換します。
ただ、「brssl」は実行ファイルのような形で提供されておらず、ソースコードをビルドする必要があります。
結構面倒な手順なのですが、大まかに下記のような方法で行いました。
ローカルサーバ: raspberry pi2上に構築したウェブサーバ
作業環境: 下記1〜6のbrsslツール関係のビルドや変換作業はraspberry pi2上で行いました(SSHでPCから操作)
- BearSSLのソースコードをダウンロード(Gitでリポジトリを取得)
Git clone https://bearssl.org/git/BearSSL
- ソースコードを保存したフォルダに移動(保存フォルダの場所は任意)
- あらかじめmakefileは作成されているので、そのままmakeを実行すればOKです
- ビルドが完了すれば、「build」フォルダ内に「brssl」という実行可能ファイルができます
- brsslツールを使って変換するには下記のように行います
./brssl ta 変換したい証明書ファイル
- 変換結果が表示されるので、この内容を「BearSSLTrustAnchors.h」に追記する(下記画像は一部モザイク処理をしています)
追記する際に「TA0」の数字部分を「TA16」のように変更する必要があったり、
#define TAs_NUM 15 → #define TAs_NUM 16
と変更する必要があります
- 自己証明書が追加された「BearSSLTrustAnchors.h」を保存したら、スケッチをコンパイルしてArduino MKR ZEROに書き込む
これで、自己証明書を使ったサーバにも接続できるようになりました。
●手間はかかりましたが、
「ArduinoBearSSL」ライブラリを使ってSSL通信ができるらしいことは確認することができました。
今のところ、SSL通信がしっかりできている事を確認できる方法がないので、今回の実験はここまでとします。
SSL通信の確認方法があれば、もう少し深堀りしてみたいなあと思っています。