UnityのHttpWebRequestで自己署名証明書のローカルHTTPSサーバに通信したらいろいろハマった時の記録(結局ローカルやめてFaaSにした)
あるUnityアプリのテストのためにローカルにHTTPのAPIサーバを立てていたのだけど、 ある日うまく通信ができなくなってしまい、原因を探った時の記録。
事象1
エラー
E/Unity(20792): java.io.IOException: Cleartext HTTP traffic to xxx.xxx.xxx.xxx not permitted
HTTPなんか信頼できないから繋ぎたくないよ!と仰っています。
Android 9 (pie)になって、基本的にHTTPSへの接続しか許可されなくなったことによるエラーらしい。
Android 9(Pie)でHTTP通信を有効にする - Qiita
Android側でHTTP接続を許可する道もあるけど、本番APIはHTTPSだしセキュリティ的にもイマイチだなーということで、ローカルAPIサーバをHTTPS化する方針で。
対処:mkcertで自己署名証明書を作る
mkcertで簡単にオレオレ証明書を発行する – RE:ENGINES
mkcert
このGitHubのReadMeに従って作業したら、難なく作れてしまった。。
FastAPIでHTTPSサーバを起動する
通常起動はuvicorn main:app
だが、HTTPSサーバとして起動する場合はuvicorn main:app --ssl-keyfile=./key.pem --ssl-certfile=./cert.pem
となる。
で、ビルドしたUnityアプリから接続してみる。
事象2
エラー
E/Unity(14330): javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
自己証明書は信頼できないので繋ぎたくないよ!と駄々をこねています。
対処:UnityWebRequestのcertificateHandlerに自己証明書に対応した独自ロジックを追加
※最終的に自分ではうまくいかなかった方法になりますので参考までに。
こちらの内容を参考に。
How to accept self signed certificate - Unity Answers
自前CertificateHandlerのコードは以下。
PUB_KEYに指定するのは、certificateファイル中のpublic keyのDER形式のバイナリを文字列で16進表記したもの。
using UnityEngine.Networking; using System.Security.Cryptography.X509Certificates; using UnityEngine; public class MyCertificateHandler : CertificateHandler { private static string PUB_KEY = @"my pub key"; protected override bool ValidateCertificate(byte[] certificateData) { X509Certificate2 cert = new X509Certificate2(certificateData); string pk = cert.GetPublicKeyString(); Debug.Log(cert.ToString()); if (pk.Equals(PUB_KEY)) return true; return false; } }
ValidateCertificateがtrueを返すと信頼、というロジックを組めるようです。
※常にtrueを返す実装は絶対やっちゃダメ!
UnityWebRequestを使う部分ではこのように指定。
UnityWebRequest www = UnityWebRequest.Post(url, formData); // 自己証明書に対応するため、自作の証明書確認ロジックを追加 www.certificateHandler = new MyCertificateHandler(); yield return www.SendWebRequest();
とやったんだけど、最終的に自分の証明書のpublic keyと、Unityで取得した証明書のpublic keyの値がどうしても一致せず、この実装は断念・・・(原因不明)
根本対処:FaaSを使う
今回自分の目的としては、Unityアプリのデバッグのために決まりきった内容のレスポンスを返すHTTPSのAPIがほしかっただけ。
なので、代わりにGCP Cloud Functionsを使うことにした。
で、30分くらいでできた。
共同開発者にも使ってもらえるしこれでええやん。クラウド最高。