Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GRPC ssl with self-signed cert

I'm trying to use a self-signed certificate with GRPC. I generated the certificate / key with:

openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

This gave me two files: cert.pem and key.pem.

I have a Kotlin GRPC server that I setup like follows:

val ca = classLoader.getResourceAsStream("cert.pem")
val key = classLoader.getResourceAsStream("key.pem")
ServerBuilder
    .forPort(8443)
    .useTransportSecurity(ca, key)
    .addService(...)
    .build()
    .start()

This appears to start successfully. I have a flutter client that I setup the following way:

final cert = await rootBundle.load('cert.pem')
final certAsList = cert.buffer
        .asUint8List(
          cert.offsetInBytes,
          cert.lengthInBytes,
        )
        .map((uint8) => uint8.toInt())
        .toList()
final channel = new ClientChannel(
      'localhost',
      port: 8443,
      options: ChannelOptions(
        credentials: ChannelCredentials.secure(certificates: certAsList),
      ),
    )

However, using this channel to connect to my service gives the following error:

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

What is wrong with this setup?

like image 303
Max Avatar asked Jul 21 '19 18:07

Max


2 Answers

By default, the certificate presented (in either direction) will be validated in several ways:

  1. Hostname must match the subject Common Name in the certificate presented.
  2. the certificate must not be revoked, by using CRL or OCSP to validate
  3. the root CA certificate must be trusted, and no intermediaries can be revoked

It's likely that you are running into #3, since it's a self-signed certificate (the root is itself and not trusted) and you're already using localhost to connect to it. You could either add this certificate to your trusted CA certificate store or you can programmatically create an insecure certificate validation for your SSL Context. For more details on the Kotlin (Java) side, you can consult the SO here: Disabling certificate check in gRPC TLS

like image 121
Matt Kerr Avatar answered Nov 11 '22 06:11

Matt Kerr


As mentioned in Matt's answer, your CA certificate is not trusted by the device running your Flutter app since it's self-signed.

Now you have 2 options:

  1. Get a valid certificate from a certificate authority like Verisign, or
  2. Disable the certificate verification in your Flutter app itself

Here's how to implement option 2. You simply add a BadCertificateHandler to the ChannelCredentials instance like so:

    final cert = await rootBundle.load('cert.pem')
    final certAsList = cert.buffer
        .asUint8List(
      cert.offsetInBytes,
      cert.lengthInBytes,
    )
        .map((uint8) => uint8.toInt())
        .toList()
    final channel = new ClientChannel(
      'localhost',
      port: 8443,
      options: ChannelOptions(
        credentials: ChannelCredentials.secure(
          certificates: certAsList,
          onBadCertificate: (cert, host) => true, // <--- **** The missing part **** 
        ),
      ),
    )

By having a handler that always returns true, you're basically disabling the certificate verification completely. Now whether you really want to do that or not is up to you ;)

like image 43
Nimrod Dayan Avatar answered Nov 11 '22 06:11

Nimrod Dayan