Spresense SDK の lte_tls サンプルを動かすのにハマったので備忘録

今回、Spresense + LTE拡張ボードを使って https でデータをサーバーにアップするシステムを構築する機会がありました。
ただ単純なところで結構躓いてしまったので備忘録としてこの記事を書きます。

ハードウェア構成
Spresense Main Board [CXD5602PWBMAIN1]
SPRESENSE LTE拡張ボード [CXD5602PWBLM1JUL]
SDカード 8G

通信環境
SIM MEEQ
通信キャリア Softbank

ソフトウェア
Spresese SDK
lte_tls (exampl 内のプログラム)

今回のテストはSpresese SDKに付属している **lte_tls** をベースにしています(ほぼそのまま)
lte_tlsに関しては
[Spresense SDK での開発 8.2. LTE TLS サンプルアプリケーション](https://developer.sony.com/develop/spresense/docs/sdk_tutorials_ja.html#_lte_tls_%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)
ここで解説されています。
configの設定はここで解説されているそのままで問題ありません。

**今回私がはまったところです。**

<h2>1. 証明書のSDカード内の場所</h2>
チュートリアルのページで
HTTPSで使用するサーバのルート証明書を格納するディレクトリを指定します。

「デフォルトの設定では **/mnt/sd0/CERTS** となっています。」
とあるので windows10で
**/sd0/CERTS**というフォルダーを作って そこに証明書ファイルを置いたが
Spresenseで認識してくれない
Spresense で SDカードのファイルを確認するプログラムを走らせたら
**/mt/sd0/sd0/CERTS**
と sd0 の階層が2つになっていた。
　
MFKyoto2023 でソニーの方に聞いたら、 /mt/sd0/  は自動で付くので
Windowsで作る場合は **/CERTS/証明書ファイル** にしてくださいと。
これはlinuxでは常識なのかな? 無知すぎて恥ずかしい。

<h2>2. 証明書のダウンロード</h2>

解説されているダウンロード方式と実際のダウンロード方法が違っていた
cromeでURLの左の鍵をクリックして表示されるページが解説されているのと全然違っていました。

実際のページ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/9c6f5e08-3f69-1e72-8354-20ad951131f1.png)
このページでのダウンロード方法
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/5d153750-b6ad-6db7-eec9-067f14787bee.png)
拡張子が .crt のファイルがダウンロードされます
　
解説ページで対応している形式は以下のようになっており、 .crt ファイルがありません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/8dab13be-8183-0250-81c7-9b16fe5c871b.png)
　
証明書の拡張子を調べると以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/e97ca3bd-3dbc-2445-66e6-f173d3de3161.png)
**.crt** は、「バイナリ DER 形式または Base64 エンコードされた PEM 形式」
となっていますので、中身を見たらアスキーコードでかつヘッダとフッタで囲まれていたので、試しに拡張子を **.pem** に変更してみました。
　
結果、拡張子を .pem に変更した証明書ファイルは問題なく使えました。
(もしかしたら**.crt**のままでも使える? 確認していません)

<h2>3. プログラムが途中で止まっている(最後まで実行していない)</h2>

チュートリアルページの正常な実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/a2a283e0-a8e2-8bdd-35d2-42876189a782.png)  
これに対して自分の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/ad3dcc49-2ceb-4327-8c3f-f6d97e98ede2.png)
**HTTP status code** = 200 が出力されていないうえに
何のメッセージも無くプロンプトに戻っています。
　
該当箇所にあたりをつけてプログラムの **531行目と538行目にprint文** を追加しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/ceec0dac-654a-0155-0b2f-2a66c96eefc2.png)
(ここより上の方でpostデータとかを変更しているので行数はオリジナルとはずれています)
　
**プログラムを修正した後のログ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/15850f79-f1f8-5c43-89f9-ea07ffe68771.png)
**Peer's disconnection**が表示されているので
「**MBEDTLS_ERR_SSL_PEAR_CLOSE_NOTIFY**」エラーが発生しているのが判明しました。
発生している場所はプログラム上エラー側の処理で、コメントやエラー内容からエラーが発生して**接続断**されていると推測しました。
ただ、**mbed TLSライブラリ**内での発生なのでどうしたものかと思い、ChatGPTにこのエラーの内容を質問してみました。
　
**ChatGPTの回答**です。
　
このエラーは、相手側が接続を終了したことを示す通知（Close Notify）を受信した場合に発生します。
TLS/SSLプロトコルでは、通信の終了を示すためにClose Notifyメッセージが送信されます。
一方、MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY エラーは、このClose Notifyメッセージを受け取ったことを示しています。
　
このエラーは通常、次のような場合に発生します：
サーバー側がクライアントに対して通信を終了する旨のメッセージを送信し、クライアントがそれを受け取った場合。
クライアントがサーバーに対して通信を終了する旨のメッセージを送信し、サーバーがそれを受け取った場合。
　
**このエラーは一般的には問題ではありませんが、プログラムのデバッグやログ分析の際に通知として表示されることがあります。**
　
エラー自体はTLS/SSLの正常な動作の一部であるため、対処が必要な場合は特にありません。
ただし、MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY エラーが発生する状況によっては、通信の問題が存在する可能性もあります。
　
その場合は、通信の他のエラーコードやログメッセージとともに詳細を調査する必要があります。
　
と言うことで、プログラム上はエラー側の処理となっていますが、問題なさそうなので、サーバー側のログを確認しました。

<h2>4. サーバー側の受信プログラム</h2>

```index.php
<?php
// ヘッダー情報とPOSTデータを取得
$headers = getallheaders();
$post_data = file_get_contents("php://input");

// ヘッダー情報とPOSTデータを結合
$log_content = "Headers:\n";
foreach ($headers as $key => $value) {
    $log_content .= "{$key}: {$value}\n";
}
$log_content .= "\nPOST Data:\n{$post_data}\n";

// ファイルに記録
$log_file = 'log.txt';
file_put_contents($log_file, $log_content, FILE_APPEND);

// 必要に応じて、レスポンスを返す
header("Content-Type: text/plain");
echo "POST data and headers have been logged.";
echo "<br>";
echo "<br>POST Data: {$post_data}<br>";

?>
```
IoT装置からデータをgetやpostで送る場合、サーバーに届いているかとかエラーを確認するためにいつもログを取るようにしています。
今回は正しく接続されているかと、ヘッダー情報とPOSTデータの確認なのでこれだけとなっています。
通常はデータをDBに格納したりとかの処理も入ります。

**実際のログ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/f68af72d-580e-0228-1ee9-861514cacede.png)

以上今回些細なところで嵌まってしまいました。
でもなんとか動いたので次のステップに進めそうです。

色々と対応していただいた **SONY Spreseseチーム** の方々、ありがとうございました。