grpc / grpc-dart

The Dart language implementation of gRPC.
https://pub.dev/packages/grpc
Apache License 2.0
861 stars 271 forks source link

Hostname mismatch when using subjectAltName with ipv4 adress in ssl certificate. #545

Open mathisfouques opened 2 years ago

mathisfouques commented 2 years ago

I have the following error :

[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: gRPC Error (code: 14, codeName: UNAVAILABLE, message: Error connecting: 
HandshakeException: Handshake error in client (OS Error:
        CERTIFICATE_VERIFY_FAILED: Hostname mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393)), details: null, rawResponse: null, trailers: {})

when using an ipv4 adress to connect to a local server (written in dart) from a flutter windows app. I use a subject alternative name, to be able to pass this raw ipv4 adress.

I use the latest 3.0.2 version of grpc.

Repro steps

I have configured a server certificate and key, as well as client ones, with the following command : openssl req -new -nodes -x509 -days 365 -keyout server.key -out server.crt -config config_ssl

(and also :) openssl req -new -nodes -x509 -days 365 -keyout client.key -out client.crt -config config_ssl

Here is the config_ssl file :

[req]

default_bits = 4096

default_md = sha256

distinguished_name = req_distinguished_name

x509_extensions = v3_req

prompt = no

[req_distinguished_name]

CN = <my ipv4adress>

[v3_req]

keyUsage = keyEncipherment, dataEncipherment, digitalSignature

extendedKeyUsage = serverAuth, clientAuth

subjectAltName = @alt_names

[alt_names]

IP.1 = <my-ipv4adress>

--> The error is above.

Details

Client

I use the following class that herits from ChannelCredentials for the Client :

class ClientCertificateChannelCredentials extends ChannelCredentials {
  final List<int> _serverCert;
  final List<int> _cert;
  final List<int> _key;
  final String ipAddress;

  const ClientCertificateChannelCredentials(
    this._serverCert,
    this._cert,
    this._key, {
    required this.ipAddress,
  }) : super.secure();

  @override
  SecurityContext get securityContext {
    return SecurityContext()
      ..useCertificateChainBytes(_cert)
      ..usePrivateKeyBytes(_key)
      ..setTrustedCertificatesBytes(_serverCert)
      ..setAlpnProtocols(['grpc-exp', 'h2'], true);
  }

  @override
  String? get authority {
    print("getter returns $ipAddress");
    return ipAddress;
  }
}

This is used right there on the client :

channel = ClientChannel(
      sslCertificates.ipAddress!,
      port: 50051,
      options: ChannelOptions(
          credentials: ClientCertificateChannelCredentials(
            sslCertificates.serverCertificateBytes!,
            sslCertificates.clientCertificateBytes!,
            sslCertificates.clientKeyBytes!,
            ipAddress: sslCertificates.ipAddress!,
          ),
          codecRegistry:
              CodecRegistry(codecs: const [GzipCodec(), IdentityCodec()])),
    );
    client = SimpleClient(channel);

Server

class ServerTlsCredentialsWithClientCheck extends ServerTlsCredentials {
  final List<int> certificateBytes;
  final List<int> keyBytes;
  final List<int> clientCertificatesBytes;

  ServerTlsCredentialsWithClientCheck({
    required this.clientCertificatesBytes,
    required this.certificateBytes,
    required this.keyBytes,
  });

  @override
  SecurityContext get securityContext {
    return SecurityContext()
      ..useCertificateChainBytes(certificateBytes)
      ..usePrivateKeyBytes(keyBytes)
      ..setTrustedCertificatesBytes(clientCertificatesBytes)
      ..setAlpnProtocols(['grpc-exp', 'h2'], true);
  }

  @override
  bool validateClient(Socket socket) {
    print(socket.address);
    return super.validateClient(socket);
  }   

Btw, i don't know where this validateClient is called currently

Used here :

await server
        .serve(
          address: ipAddress,
          //address: "localhost",
          port: 50051,
          security: ServerTlsCredentialsWithClientCheck(
            clientCertificatesBytes: clientCertificates ?? [],
            keyBytes: serverKeyBytes ?? [],
            certificateBytes: serverCertificateBytes ?? [],
          ),
          requestClientCertificate: true,
          requireClientCertificate: true,
        )
        .onError((error, stackTrace) => print(error));

Any help would be much appreciated !