Dart gRPC TLS certificates with PEMs

782

I ended up receiving somewhat of a proper answer on the grpc-dart issues page. The solution looks something like this:

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',
);
Share:
782
pilotguy
Author by

pilotguy

Updated on December 01, 2022

Comments

  • pilotguy
    pilotguy over 1 year

    I'm having a bit of trouble sorting out how to adapt my Dart gRPC client to use the same TLS settings that are working with my Go client. I've already validated that I can interface with the server suppling the correct CA cert, client cert and client key. In Go I'm using:

        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,
        }
    

    Just supplying that in case it helps demonstrate what's working. In Dart I'm doing this:

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

    grpcCertificate contains the contents of client-key.pem. I suspect this is not correct. I'm not very skilled with certificates like this so I'm a bit at a loss. What value should I be supplying to certificates to achieve a successful handshake with the server?

    From the above it seems like I need to parse my PEMs into X.509. In Go that's super easy, not sure how to handle this in Dart.

    Edit: I've made a bit of progress:

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

    The server seems to hate this less and spits out:

    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)
    

    Thanks.

    • Guy Luz
      Guy Luz almost 3 years
      How funny, I have just created question that is similar in some way. If you think my questio will help you too please up vote it. stackoverflow.com/questions/68330923/…
    • Guy Luz
      Guy Luz almost 3 years
      I add answer to my question. If you find how to do this for .pem file please post here answer since I also need a solution for .pem file and not .crt and .key files.