Flutter ✕ gRPC ✕ オレオレ証明書


2020/5/17 更新
どうやらiOS13以降だと↓の通りの記述を入れても HandshakeException が発生するようです。
https://support.apple.com/ja-jp/HT210176
公式でのアナウンスもあり、クライアントの努力で解決するのは実質不可能と思われます。
正規の証明書発行することをオススメします。

はじめに

gRPCをFlutter(Dart)で使うときにオレオレ証明書で死ぬほど躓いたのでメモ。
オレオレ証明書のための HogeClient の設定までにつまづいたこと、そしてどう修正していったのかを順を追って説明していきます。

目次

1. pbgrpc.dartがない
2. SocketException
3. HandshakeException
4. 黙ってクラッシュ
5. 動いた

1. pbgrpc.dartがない

生成の設定ミスです。設定については割愛します。

2. SocketException

実際のコード

_createClient() {
    final channel = ClientChannel('https://hoge.com');
    return HogeClient();
}

エラー

Unhandled Exception: gRPC Error (14, Error connecting: SocketException: Failed host lookup: 'https://hoge.com' (OS Error: nodename nor servname provided, or not known, errno = 8))

原因

https:// は不要でした。

3. HandshakeException

実際のコード

_createClient() {
    final channel = ClientChannel('hoge.com');
    return HogeClient();
}

エラー

Unhandled Exception: gRPC Error (14, Error connecting: HandshakeException: Handshake error in client (OS Error: 
    CERTIFICATE_VERIFY_FAILED: ok(handshake.cc:354)))

原因

信頼できない証明書(オレオレ証明書)を使っていることによるエラーです。

4. 黙ってクラッシュ

実際のコード

_createClient() {
    final channel = ClientChannel(
        'hoge.com', 
        options: ChannelOptions(
          credentials: ChannelCredentials.insecure(),
        ),
    );
    return HogeClient();
}

エラー

Androidのみで出力されるエラー

Unhandled Exception: gRPC Error (2, HTTP/2 error: Connection error: Connection is being forcefully terminated. (errorCode: 10))

flutter run -vのクラッシュ直前抜粋

[   +5 ms] DevFS: Deleting filesystem on the device
(file:///Users/{ユーザ名とかデバイスIDとか}/)
[ +260 ms] Ignored error while cleaning up DevFS: TimeoutException after 0:00:00.250000: Future not completed

原因

ChannelCredentials.insecure(), は認証をオフにするパラメータです。オレオレ証明書でも認証は必要なためクラッシュします。

5. 動いた

実際のコード

_createClient() async {
    final trustedRoot = await rootBundle.load('assets/key/api.pem');
    final channelCredentials = ChannelCredentials.secure(
      certificates: trustedRoot.buffer.asUint8List(),
    );
    final channel = ClientChannel(
      'hoge.com',
      options: channelOptions,
    );
    client = ClipServiceClient(channel);
}

ポイント

  • 動かすためにオレオレ証明書の .pem が必要なため発行しましょう。
  • assets内にそれを保存し 実際のコード を参考にクライアントを生成しましょう。

感想

4. でずっと黙ってクラッシュしてたので丸一日潰れました。
この記事によって誰かの丸一日が潰れることを阻止できれば本望。