Dart gRPC TLS 证书与 PEM

问题描述

我在整理如何调整 Dart gRPC 客户端以使用与 Go 客户端相同的 TLS 设置时遇到了一些麻烦。我已经验证我可以与提供正确 CA 证书、客户端证书和客户端密钥的服务器交互。在 Go 中,我正在使用:

    pemServerCA,err := IoUtil.ReadFile("pems/ca-cert.pem")
    if err != nil {
        return nil,err
    }
    certPool := x509.NewCertPool()
    if !certPool.AppendCertsFromPEM(pemServerCA) {
        return nil,fmt.Errorf("Failed to add server CA's certificate")
    }
    // Load client's certificate and private key
    clientCert,err := tls.LoadX509KeyPair("pems/client-cert.pem","pems/client-key.pem")
    if err != nil {
        return nil,err
    }
    // Create the credentials and return it
    config := &tls.Config{
        Certificates: []tls.Certificate{clientCert},RootCAs:      certPool,}

只是提供它,以防它有助于证明什么是有效的。在 Dart 中,我正在这样做:

  ChannelCredentials credentials = ChannelCredentials.secure(
    certificates: utf8.encode(grpcCertificate),onBadCertificate: (certificate,host) {
      return host == apiURL + ':' + apiPort.toString();
    },);

grpcCertificate 包含 client-key.pem 的内容。我怀疑这是不正确的。我对这样的证书不是很熟练,所以我有点不知所措。我应该为证书提供什么值才能与服务器成功握手?

从上面看来,我似乎需要将 PEM 解析为 X.509。在 Go 中这非常简单,不知道如何在 Dart 中处理。

编辑:我取得了一些进展:

    List<int> list = grpcCertificate.codeUnits;
    Uint8List cert = Uint8List.fromList(list);
    ChannelCredentials credentials = ChannelCredentials.secure(
      certificates: cert,authority: 'localhost',host) {
        return host == apiURL + ':' + apiPort.toString();
      },);

服务器似乎不太讨厌这个并吐槽:

Flutter: gRPC Error (code: 14,codeName: UNAVAILABLE,message: Error connecting: TlsException: Failure trusting builtin roots (OS Error:
    BAD_PKCS12_DATA(pkcs8_x509.c:645),errno = 0),details: null,rawResponse: null)

谢谢。

解决方法

我最终在 grpc-dart 问题页面上收到了一些正确的答案。解决方案如下所示:

class MyChannelCredentials extends ChannelCredentials {
  final Uint8List? certificateChain;
  final Uint8List? privateKey;

  MyChannelCredentials({
    Uint8List? trustedRoots,this.certificateChain,this.privateKey,String? authority,BadCertificateHandler? onBadCertificate,}) : super.secure(
            certificates: trustedRoots,authority: authority,onBadCertificate: onBadCertificate);

  @override
  SecurityContext get securityContext {
    final ctx = super.securityContext;
    if (certificateChain != null) {
      ctx.useCertificateChainBytes(certificateChain);
    }
    if (privateKey != null) {
      ctx.usePrivateKeyBytes(privateKey);
    }
    return ctx;
  }
}

final cred = MyChannelCredentials(
  trustedRoots: File('pems/ca-cert.pem').readAsBytesSync(),certificateChain: File('pems/client-cert.pem').readAsBytesSync(),privateKey: File('pems/client-key.pem').readAsBytesSync(),authority: 'localhost',);