Internet of Aquarium : ESP-WROOM-02 と ThingSpeak を利用して
目次
目的
アクアリウムを始めたので、表題の方法で適正水温監視を行う。
監視方法
- Arduino で水温(温度)測定
- ThingSpeak*1 に水温データを送信しアーカイブ・グラフ化
- 水温監視を ThingSpeak のアプリ "React"*2 で実行
- 適正温度を外れた場合は Twitter へツイート
- ThingSpeak からのツイートを IFTTT*3 で監視し警告メールを送信
作成
Arduino で水温(温度)測定
まず母体として、"ESP-WROOM-02" を "無線付Arduino" として使いました。回路図・ソースコードはまとめて後述しています。
また、本エントリで作成したものについては "Aquarino" と名付けましたので、以後そのように記載します。
温度センサ
手元に MCP9700*4 がありましたので、下記を参考に利用しました。
Arduinoで遊ぼう -温度センサIC、MCP9700を使って温度を測る - なんでも作っちゃう、かも。
注意としては、5V電源とGNDを繋ぎ間違えると一発で壊れてしますので、ご用心を。(壊れてからは、TOUT の許可されている電圧上限 1V の値を受け取っていたので、TOUT に負荷がかかっていたかもしれません^^; 自分は2つダメにしてしまいました…)
温度・通信状態表示のため LCD
こちらも手元に "LCDキャラクタディスプレイモジュール(16×2行バックライト無)"*5 がありましたので、下記を参考に利用しました。
【Arduino】SC1602B(16桁LCDディスプレイ)を使ってみる。 : Blazing Azuki's Blog
また、LCD のために GPIO2 をデジタル出力として利用しました。( GPIO0,2,15 をデジタル出力として使う場合は、実行モードを妨げないようにしないといけません。*6 )
省電力のための "Deep-Sleepモード" 利用
下記がとても参考になりました。
ESP8266の真骨頂Deep-Sleepモードの使い方 - Qiita
今回の場合ですと ESP-WROOM-02 が待機状態でも LCD は通電したままですので、LCD の最後に表示した状態が持続します。
ThingSpeak への接続に HTTPS を利用
"WiFiClientSecure class" を利用して対応し、証明書(フィンガープリント)確認なども行うようにしました。
GETリクエストは https://api.thingspeak.com/update?api_key=XXXXXXXXXX&field2=20
という感じです。
デバッグ用のシリアルモニタへの出力
リリースバージョンではコメントアウトしてますが、参考に載せておきます。
Connecting to XXXXmask-networkXXXX ....... WiFi connected IP address : 192.168.1.123 Connecting HOST : api.thingspeak.com TOUT from MCP9700 : 796 mV (count 0) TOUT from MCP9700 : 778 mV (count 1) TOUT from MCP9700 : 779 mV (count 2) TOUT from MCP9700 : 779 mV (count 3) TOUT from MCP9700 : 780 mV (count 4) TOUT Average : 782.40 mV ( 24.84 degress C) Requesting URL : /update?api_key=XXXXXXXXXXXXXXX&field1=24.84 closing connection.
ソースコード
下記で公開しております。
ハードウェア作成のための準備
EAGLE*7 で回路図を作成し、パーツ郡は下記を利用しました。
始めての EAGLE 利用なので間違った使い方などあるかと思いますが、下記が本エントリで作成した ESP-WROOM-02 を含む回路図です。
Pic.1 回路図
ユニバーサル基板でのパーツ配置を自動でできないかと思い、途中まで検討してみました。
Pic.2 パターン図の検討
しかしながら回路も小規模でユニバーサル基板利用なので、結局自分で考えた方が早そうということで配置を検討したメモが下記です。
Pic.3 ユニバーサル基板実装案
ハードウェア作成
Pic.3 のメモを参考に実際に実装した基板が下記です。(裏側は試行錯誤があったためクオリティが酷いですが…)
Pic.4 ユニバーサル基板実装(表)
Pic.5 ユニバーサル基板実装(裏)
動作確認
Movie.1 "Aquarino" の動作(字幕に動作説明あり)
ThingSpeak に水温データを送信しアーカイブ・グラフ化
グラフ設定として、Y軸の最小・最大値を設定しました。
Graph.1 水槽の温度監視
水温監視を ThingSpeak のアプリ "React" で実行 & 適正温度を外れた場合は Twitter へツイート
適正水温の監視は、Arduino 側で行うのではなく、ThingSpeak のアプリ "react" が目的に合っていそうだったのでこちらを利用しました。
別解として "ThingTweet"*13 を利用する方法も考えたのですが、適性水温の変更やツイート内容を変更するためには "Arduino" のコードを編集した後に再書き込みが必要であり、これが面倒だと考えて採用を見送りました。
ThingSpeak からのツイートを IFTTT で監視し警告メールを送信
IFTTT で監視を行い、自分の Twitter アカウントでハッシュタグ "#thingspeak" が付加されてツイートされたら、指定アドレスへ警告メールを送信する設定を行いました。
TODO
水温低下時は別途ヒーターの設備があるのですが、水温上昇時に対処する設備がありません。
"Aquarino" ではシリアル通信のための RX・DX が空いているので、それで "水槽用冷却ファン" を制御しようと考えています。
付録
秋月さんで販売している "ESP-WROOM-02"*14 の "EAGLEライブラリ"
"ikesato" さんの "ESP-WROOM-02" の "EAGLEライブラリ"*15 を基に、見出しのパーツを作成しました。
下記にパーツイメージ画像と共に公開してますので、ご確認のうえご利用いただけましたらと。
制作費用
table.1 "Aquarino" 制作費用(はんだ等の消耗品は除く)
製品 | 単価 | 数 | 値段 |
---|---|---|---|
ESP-WROOM-02(DIP、秋月電子通商) | 650 | 1 | 650 |
LCDキャラクタディスプレイモジュール(16×2行バックライト無) | 500 | 1 | 500 |
温度センサーIC MCP9700 | 38 | 1 | 38 |
片面ユニバーサル基板(D) | 30 | 1 | 30 |
低損失三端子レギュレータ TA48M033F(SQ) | 100 | 1 | 100 |
カーボン抵抗1/4W 10kΩ | 1 | 3 | 3 |
カーボン抵抗1/4W 20kΩ | 1 | 1 | 1 |
半固定ボリューム(20kΩ) | 40 | 1 | 40 |
DCジャック(DIP) | 100 | 1 | 100 |
ブレッドボード・ジャンパーコード(オス-メス)(10本入) | 220 | 1 | 220 |
電線50cm | 20 | 2 | 40 |
ACアダプター5V2A GF12-US0520 | 650 | 1 | 650 |
ABS樹脂ケース(蝶番式・中) 112-TS | 120 | 1 | 120 |
絶縁ラジアルリード型積層セラミックコンデンサ 0.1μ | 10 | 1 | 10 |
丸ピンICソケット | 3 | 11 | 33 |
丸ピンIC連結ソケット | 5 | 3 | 15 |
合計 | - | - | 2550 |
ケースを入れ替えました(2017/1/17追記)
エステーさんの冷蔵庫の脱臭炭のケースがちょうど良いサイズだったので、ケースを工作のうえケーブルも収まるように新たに細工しました。旧ケースよりもスリムで良いですね。
所感
タイトルからすると竜頭蛇尾な内容かもですが、自分としてはボリュームのあるプロジェクトで、初めの構想から1ヶ月半が経とうとしています。
EAGLE も勉強することができましたし、ESP-WROOM-02 の扱いにも慣れてきました。また YouTube で動画に字幕をいれられることが分かったり、ThingSpeak 利用も IFTTT のレシピ公開なども初めてでした。
センサのハードウェア・ソフトウェアの構想・実装、加えてWEBサービスのマッシュアップまで、"IoT" の要素技術を一通り学ぶことができた実り多いプロジェクトでした。
しかしながらこれで終わりではなく、これからは運用・改修・機能追加など持続して取り組んでいきたいと思います。
以上
*2:https://thingspeak.com/apps
*4:http://akizukidenshi.com/catalog/g/gI-03286/
*5:http://akizukidenshi.com/catalog/g/gP-00040/
*6:http://deviceplus.jp/hobby/entry0032/
*8:http://lowreal.net/2013/11/02/2
*9:http://d.hatena.ne.jp/seinzumtode/20130531/1369988380
*10:http://threesons-technicalmemo.blogspot.jp/2014/03/eagle.html
*11:https://code.google.com/archive/p/phi-prompt-user-interface-library/downloads#makechanges
*12:https://github.com/maijou2501/EAGLE/tree/master/esp-wroom-02
*13:http://qiita.com/minori24/items/38c06d206ceae1330e0c
Gmail などのWEBメールで適切に処理されない S/MIME受信メールに対して、Linux 上で改ざん・デジタル署名の確認などを行う
目次
- 背景
- smime.p7s とは
- smime.p7s を openssl コマンドで処理してみる(下調べ)
- 所感
- 追記 ( 2016/04/04 )
背景
表題の件に関係し、銀行から送られてくるメールの添付ファイル smime.p7s
にいつも疑問を感じていたため、何であるか調査を行い、Linux 上にて適切な処理ができるか確認しました。
smime.p7s とは
S/MIMEを扱えない電子メールソフトもあるため、"smime.p7m"という名の本文や、"smime.p7s"という名の添付ファイルに困惑する人が多い。
S/MIME - Wikipedia
S/MIME に対応していないメーラでは、S/MIME証明書を添付ファイルとして表示しているのですね。
S/MIME(エスマイム、Secure / Multipurpose Internet Mail Extensions)とは、MIMEでカプセル化した電子メールの公開鍵方式による暗号化とデジタル署名に関する標準規格である。
S/MIME - Wikipedia
"公開鍵方式による" ということで、openssl*1コマンドでどうにかできそうです。
smime.p7s を openssl コマンドで処理してみる(下調べ)
自分は Gmail を使っているので、まずはsmime.p7s
をダウンロードして処理してみます。
環境
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=12.04 DISTRIB_CODENAME=precise DISTRIB_DESCRIPTION="Ubuntu 12.04.5 LTS" $ openssl version OpenSSL 1.0.1 14 Mar 2012
証明書の有効性確認
openssl の smime コマンドで Verify も簡単にできるかと思ったのですが、一度デコードしないといけないようです。
How to handle OpenSSL and not get hurt using the CLI - GridWiki
RSA鍵、証明書のファイルフォーマットについて - Qiita
$ openssl pkcs7 -in smime.p7s -inform DER -print_certs -out /tmp/output.crt
そして、メールに添付された当該証明書の信頼性を、インストール済みのルート証明書から確認します。
・/usr/share/ca-certificates/ 以下にバラバラのファイルとして CA cert が置かれる
・/etc/ssl/certs/ にはそのファイルへのシンボリックリンクと c_rehash で生成されたファイルが置かれる
・上記のファイルを一つにまとめたもの(だと思う)が /etc/ssl/certs/ca-certificates.crt に置かれる
Debian の SSL 認証局証明書 (CA cert.) を最新に保つ - Lazy Diary @ はてな
openssl コマンドでのSSL証明書の検証 - なんだそのカオは -_-
openssl でSSL/TLSと戯れてみる - いますぐ実践! Linuxシステム管理 / Vol.252
$ openssl verify -verbose -x509_strict -CAfile /etc/ssl/certs/ca-certificates.crt /tmp/output.crt /tmp/output.crt: OK
返り値チェックで、成功・失敗が分かるかと思ったのですが、error 出力でも返り値0だったりしたので、"OK" という文字列出現を確認することとしました。
加えて、ここでエラーが発生する場合、後述するのですが一部の証明書で「サーバー証明書・中間証明書などを逆順に記載しているもの」があったため、その可能性を疑ってみると良いと思います。
補足 : SSL 認証局証明書の更新
自分の場合は、NG の場合のエラーメッセージで "ルート証明書が古い" 的なことを言われたので(内容はコピーし忘れました…)、下記を参考に証明書を更新しました。
Debian の SSL 認証局証明書 (CA cert.) を最新に保つ - Lazy Diary @ はてな
sudo update-ca-certificates --fresh
サーバー証明書の失効を確認する
下記にも記載があるが、サーバー証明書失効リスト (CRL) を使うのは現実的ではないので、OCSP(Online Certificate Status Protocol)*2を使います。
OCSPリクエストによるサーバー証明書の失効確認
ここではメール送信者の証明書だけを、その上位の認証局に失効していないか確認します。(手順が複雑になるため、証明書パス全体の確認は行わない。)
私が愛した openssl (PKI 編 その 2) - してみむとて
$ OCSP_URI=`openssl x509 -in /tmp/output.crt -noout -text | egrep ocsp | sed -e "s/.*\(http.*\)$/\1/"` $ awk 'BEGIN{RS="";FS="\n"};{a[NR]=$0}END{print a[2]}' /tmp/outpu.crt > /tmp/intermediate.crt $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/output.crt -url $OCSP_URI -resp_text -respout resp.der -no_nonce -CAfile /tmp/intermediate.crt 〜略〜 -----END CERTIFICATE----- Response verify OK /tmp/output_reverse.crt: good This Update: Mar 30 09:02:34 2016 GMT Next Update: Apr 6 09:02:34 2016 GMT
OCSP 記載が無い場合は失効確認は行わない。OCSP記載が無く、CRL記載だけのものは少ないと思いますし、運用してみて改修するか考えようと思います。
【別解】サーバー証明書失効リスト (CRL) を取得する
証明書に記載のある CRL をダウンロードし、それとの突き合わせを行います。
opensslによるサーバー証明書失効リスト (CRL) 確認 - IKB: 雑記帖
自堕落な技術者の日記 : OpenSSL 1.0.0 beta1 タイムスタンプ検証機能 - livedoor Blog(ブログ)
メール送信者の証明書中から CRL 配布ポイントを確認・ダウンロードを行い、デジタル署名の確認に合わせて CRL との突き合わせを行なっています。
mkdir ~/.crl pushd ~/.crl curl -O ` openssl x509 -noout -text -in output.crt | grep .crl | sed -e "s/.*\(http.*\.crl\)$/\1/" ` c_rehash . popd openssl verify -verbose -crl_check -x509_strict -CApath ~/.crl -CAfile /etc/ssl/certs/ca-certificates.crt /tmp/output.crt
送信元の確認
メールに記載された送信元と、下記コマンド実行で出力される証明書に記載された署名者アドレスを突き合わせます。
grep emailAddress /tmp/output.crt | sed -e "s/.*emailAddress=\(.*\)/\1/"
メール内容の改ざん有無の確認
これは下記の記事を参考にしましたが、簡単なようです。
openssl - smime.p7sからメッセージダイジェストを求めたい - スタック・オーバーフロー
Gmail で "メッセージのソースを表示" を行い、ここではgmail.eml
と名前をつけて保存し、下記コマンドを実施します。
openssl smime -verify -in gmail.eml
改ざん無し
- 返り値 : 0
- 標準出力 : Verification successful
※メッセージボディの sha1 ハッシュを確認し改ざんを確認しているので、試しにメッセージヘッダを改ざんしても上記結果となります。
メッセージボディの改ざん有り
- 返り値 : 4
- 標準出力 : Verification failure
- エラー出力 : 140465929549472:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:1158:
- エラー出力 : 140465929549472:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:410:
電子証明書の期限切れ
- 返り値 : 4
- 標準出力 : Verification failure
- エラー出力 : 139838430467744:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:342:Verify error:certificate has expired
S/MIME証明書チェックスクリプトを作成
これまでの内容では、smime.p7s
を操作していましたが、Gmail の "メッセージのソースを表示" からメールを保存したものは eml 形式なので、そこからS/MIME電子証明書をパースします。
Gmail からメールをローカルに保存する
いちいちメールを閲覧時に "メッセージのソースを表示" するのは面倒なので、メッセージのダウンロードを簡単にできるようにします。
メールを普通に表示している時のURLを下に記します。("YYYYYYYYYYYYYYYY" はおそらくユーザ毎のメールID)
https://mail.google.com/mail/u/0/?zx=XXXXXXXXXXXX#inbox/YYYYYYYYYYYYYYYY
メッセージのソースを表示した時のURLを下に記します。("ZZZZZZZZZZ" はおそらくユーザID)
https://mail.google.com/mail/u/0/?ui=2&ik=ZZZZZZZZZZ&view=om&th=YYYYYYYYYYYYYYYY
上記URL構造から、メール閲覧時に下記 JavaScript にてダウンロードを実施します。("ZZZZZZZZZZ" は適宜置き換えてください。)
javascript:(function(){var str=""+document.location;num=str.slice(-16); url="https://mail.google.com/mail/u/0/?ui=2&ik=ZZZZZZZZZZ&view=om&th="+num; var a = document.createElement('a'); a.href = url; a.setAttribute('download', "gmail.eml"); a.dispatchEvent(new CustomEvent('click')); })()
JavaScript でのダウンロードについては、下記サイトを参考にさせていただきました。
javascriptからファイル保存ダイアログを出す - Qiita
改ざん確認
先程のテストしたコマンドの返り値から、改ざん判定を行います。
if `openssl smime -verify -in gmail.eml 1>/dev/null` ; then echo 'NoPolute: OK' else exit 1 fi
emlファイルよりS/MIME証明書部分を切り出す
- ダウンロードした eml ファイルの改行が
0d0a
なので、awk利用のため0a
に変換する - 最終行(付近)の multi-part 終了を示すハイフンを含む部分を削除する
- awk で、S/MIME証明書部分を切り取る
- BASE64デコードを実行する
perl -pe 's/\r\n/\n/' gmail.eml | sed 's/------.*--//g' | awk 'BEGIN{RS="";FS="\n"};{a[NR]=$0}END{print a[NR]}' | base64 -d > /tmp/smime.p7s
上記手順により添付ファイルとして見えていたsmime.p7s
と同じものが取り出せました。
切り出した S/MIME証明書の確認
ここでは確認として、取り出した/tmp
配下のファイルと、ダウンロードしたsmime.p7s
ファイルのハッシュ値を比較します。
$ md5sum /tmp/smime.p7s ca49c2ed84c7d108f23bc66157766e88 /tmp/smime.p7s $ md5sum smime.p7s ca49c2ed84c7d108f23bc66157766e88 smime.p7s
ダウンロードしたsmime.p7s
ファイルと同じものを、うまく eml ファイルから切り出せたようです。
証明書の Verify を行う (一部のS/MIME証明書など、サーバー証明書・中間証明書などを逆順に記載しているものにも対応)
いざサーバー証明書の Verify を行う段になりまして、一部 S/MIME証明書で Verify が失敗する事象に出くわしました。
これは "三菱東京UFJダイレクト" さんの S/MIME証明書の場合で生じた事象だったのですが、どうも連結されているサーバー証明書の順番が一般的なものと逆だったようです。
(ELB に)中間証明書とクロスルート証明書の連結する順番に注意 - tkuchikiの日記
SSLサーバー証明書に中間証明書を結合する [(全部俺)何でも Advent Calendar 2013 8日目] | maruTA(Bis5)'s Weblog – Side D:iary
したがいまして、連結されたサーバー証明書の中の証明書を逆順にしてチェックを行うのがopenssl
的に正しいのかなと思うのですが、正順(?)の場合も合わせてチェックすることにします。
openssl pkcs7 -in /tmp/smime.p7s -inform DER -print_certs -out /tmp/output.crt openssl verify -verbose -x509_strict -CAfile /etc/ssl/certs/ca-certificates.crt /tmp/output.crt cat /tmp/output.crt | awk 'BEGIN{RS="";FS="\n"};{a[NR]=$0}END{for(i=NR;i>0;i--)print a[i]"\n"}' > /tmp/output_reverse.crt openssl verify -verbose -x509_strict -CAfile /etc/ssl/certs/ca-certificates.crt /tmp/output_reverse.crt
補足 : 証明書の中身を見てみる
openssl x509 -text -noout -in /tmp/output.crt
OCSPリクエストによるサーバー証明書の失効確認
OCSP記載があるか確認し、OCSPリクエスト結果の返り値で失効しているか判定しています。
OCSP_URI=`openssl x509 -in /tmp/output_reverse.crt -noout -text | egrep ocsp | sed -e "s/.*\(http.*\)$/\1/"` if [ -n "$OCSP_URI" ]; then awk 'BEGIN{RS="";FS="\n"};{a[NR]=$0}END{print a[2]}' /tmp/output_reverse.crt > /tmp/intermediate.crt if `openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/output_reverse.crt -url $OCSP_URI -resp_text -no_nonce -CAfile /tmp/intermediate.crt 1>/dev/null` ; then echo 'Status : OK' else echo 'Status : NG' fi rm /tmp/intermediate.crt else echo 'OCSP_URI: None' fi
送信元の確認
証明書に記載されたアドレスと、メールに記載された送信元を突き合わせます。
FROM_ADDRESS=`egrep "^From:" $TARGET_MAIL | perl -pe 's/.*?([a-zA-Z0-9!$&\*\.=^\`|~#%\+\/?_{}\-]+@[a-zA-Z0-9_\-\.]+).*/$1/' | perl -pe 's/\r\n/\n/'` CERT_ADDRESS=`grep emailAddress /tmp/output.crt | sed -e "s/.*emailAddress=\(.*\)/\1/" | perl -pe 's/\r\n/\n/'` if [ $FROM_ADDRESS = $CERT_ADDRESS ] ; then echo 'Address : OK' else echo 'Address : NG' echo "FROM_ADDRESS = $FROM_ADDRESS" echo "CERT_ADDRESS = $CERT_ADDRESS" exit 1 fi
S/MIME証明書チェックスクリプト(完成版)
例:成功
$ smime gmail.eml Verification successful NoPolute: OK Certify : OK Response verify OK Status : OK Address : OK
例:改ざんされている
$ smime gmail_replace.eml Verification failure 139750278760096:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:1158: 139750278760096:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:410:
例:証明書が古い
$ smime gmail.eml Verification failure 139844339644064:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:342:Verify error:certificate has expired
例:送信者が署名者と違う
$ smime gmail_bad_sender.eml Verification successful NoPolute: OK Certify : OK Response verify OK Status : OK Address : NG FROM_ADDRESS = notice@hogemail.ocn.ne.jp CERT_ADDRESS = notice@infomail.ocn.ne.jp
所感
サーバ証明書まわりの知識があまり無かったので勉強になりました。そして、S/MIMEの世間での扱われ方もわかりましたし、在り方として普及が難しいのもわかりました。WEBメール、つまるところブラウザからローカルの資源にアクセスするという意味でもWEBメールとは相性が悪いですし、署名をGmailサーバ上でやる場合の秘密鍵の取り扱いは…などなど。
本件は、MUA(Mail User Agent)を利用すれば簡単に代替できるのですが*3、会社ならまだしも、プライベートではどこからでもアクセスできるWEBメールが楽なんですよね。そして、どうしてもE2E(End-to-End)で暗号化したい場合の手段ではS/MIMEも必要かもしれませんが、それなら今時ならメールでなくとも…という感じはありますね。
なにはともあれ、「smime.p7s
を見て何だろうなと思わなくて良くなり、いざという時はそれを適切に処理できるようになった」というのが、精神安定上的な意味で一番の成果ですね。
追記 ( 2016/04/04 )
Gmail では、タイトルの件とは別に2011年位から "送信ドメイン認証" は行なっているようですね。(だから S/MIME は…)
"Google Apps" で DKIM+SPF 設定を行い、 Gmail は "送信ドメイン認証" チェックもできるわけですし、そもそもメールに限らず Google サービス内 (・間) のやり取りは google プライベートクラウドともいうべき中で行われているわけでして…囲い込まれ感が凄いですね。
社会が Google に取り込まれていくようです。
以上