🍣

FlutterでWindowsのHTTPS通信で急に証明書エラーが出るようになった場合の対処法

2024/10/31に公開

問題

今まで正常に通信できていたのに、ある時から急に下記のようなエラーが表示されて、通信に失敗するようになった。

flutter: HandshakeException: Handshake error in client (OS Error: 
flutter: 	CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate chain(../../flutter/third_party/boringssl/src/ssl/handshake.cc:393))

原因

AWS Certificate Manager などを利用してる場合、証明書更新のタイミングでルート証明書が変わってしまい、新しい証明書が、Windowsの証明書ストアに存在していないないことが原因。
Windowsアプリではなく、ブラウザなどは独自に信頼リストを保持しているので、Windowsのルート証明書がなくてもブラウザの信頼リストの証明書にある場合は問題ない。

直近だと、ACM will no longer cross sign certificates with Starfield Class 2 starting August 2024 の対応によって、アプリの通信が失敗する問題が発生した。
2024年8月以降の証明書には、「Starfield Class 2」との相互署名を停止するというもので、従来は「Starfield Class 2 Certification Authority」または「Starfield Services Root Certificate Authority - G2」のルート証明書が存在していればよかったが、対応後「Starfield Services Root Certificate Authority - G2」のルート証明書を見るようになる。
Windows端末に「Starfield Class 2 Certification Authority」のルート証明書のみ入っている場合は、2024年8月以降に通信エラーになる。

AWSの本件の対応で、同様の問題の質問がAWSに投稿されている
ACM Amazon-issued certs: new trusted chain missing on some computers (Starfield Services Root ... G2)

Windwosに入っているルート証明書について

今回の問題は、「Starfield Services Root Certificate Authority - G2」のルート証明書がWindowsに入っていないことが原因であるが、Windowsに入っているルート証明書の管理などはどうなっているのか?

Windowsに入っているルート証明書は、「ファイルを指定して実行」にcertmgr.mscを入力することで、証明書マネージャーツールを開いて確認できる。

証明書マネージャーツール

この問題は、Flutterアプリだけではなく、Windowsのネイティブアプリにも影響するのではないかと考えられるが、どうやらそうではないみたいです。

Windows は、互換性のあるWindowsライブラリがTLSセッションを開始すると、ほとんどのCAを実行時にオンザフライまたはオンデマンドでダウンロードします。
TLSハンドシェイク中に、証明書チェーンが現在のローカルの信頼されたルートに基づいて信頼されていない場合、Windows は Windows Update サーバー上の CTL(ctldl.windowsupdate.com) から一致するルート証明書をダウンロードします。
この場合、CTL は実際にはMicrosoft 信頼されたルート プログラム(更新されたリスト) からのものです。

引用

この内容から、WindowsのAPIであるWinINetやWindows10から標準で入っているcurlコマンドなどでは、通信時にルート証明書がない場合でも、WindowsのCTLを確認して信頼されている証明書なら自動でダウンロードしてくれるということである。

実際にcurlコマンドを実行すると不足するとルート証明書が自動でダウンロードされるのか検証してみた。
「Starfield Services Root Certificate Authority - G2」のルート証明書を必要とする、Amazonのデモサイトhttps://valid.sfg2.demo.amazontrust.com/にアクセスしてみる。

curlコマンド実行前
「Starfield Services Root Certificate Authority - G2」のルート証明書がWindowsに入っていない状態

証明書マネージャーツール

curlコマンド実行後
「Starfield Services Root Certificate Authority - G2」のルート証明書がWindowsに入っている

証明書マネージャーツール

Flutterのhttpパッケージの問題

前述の通り、WindowsライブラリがTLSセッションを開始すると、ほとんどのCAを実行時にオンザフライまたはオンデマンドでダウンロードします。
しかし、Flutterのhttpパッケージは、この機能をサポートしていないため、Windowsの証明書ストアに入っていない証明書は通信できない。

解決方法

まず、本件に対してスマートな解決策はなさそうであるが、下記の方法が考えられる。

  • 事前にルート証明書をなんとかしてインストールする
  • アプリに証明書を同梱してHttpClient生成時に手動で読み込ませる

自分の場合は、起動の遅さを犠牲にして、アプリ起動時にWinINetを使って、ルート証明書をダウンロードする方法を採用した。

検証アプリは下記にあります。
https://github.com/ynug/flutter_windows_wininet

他に良い方法をご存知の方は教えていただけると幸いです。

参考サイト

Amazon関連

Windows関連

Discussion