We're Hiring!

How to Pin Mobile gRPC Channels

Last-mile Security for gRPC-connected mobile APIs

Large blue safety art installation - New Orleans Museum of Art

In Consider gRPC for Mobile APIs, we evaluated gRPC for use in mobile applications. We took a look at common operations such as:

  • A basic request-response API call
  • Token-based authentication
  • A single request, streaming response API call

I concluded that the RPC function call paradigm felt more natural to me than designing and implementing a fully-RESTful API implementation, and I said I wouldn’t hesitate to use gRPC with mobile clients when the API is static and well understood.

For mobile apps, certificate pinning is an important capability to strengthen API security, and in this sequel, we’ll examine certificate pinning for gRPC on Android. Spoiler alert - in the end, it’s quite similar to pinning a restful connection.

Certificate Pinning

Transport Layer Security (TLS) is a well accepted and evolving standard to strengthen privacy and message integrity. When establishing a connection, a server endpoint sends its public key certificate to the requesting client. The client follows the certificate chain of trust until it reaches a root certificate it implicitly trusts.

Chain of Trust

Source: Wikipedia — chain of trust: image originally via Gary Stevens of HostingCanada.org

Android and iOS devices maintain an installed set of certificates which they implicitly trust. Unfortunately, it is too easy to trick mobile devices into trusting certificates signed by unexpected certificate authorities. Diagnostic tools, such as mitmproxy, use this same technique to intercept and potentially interfere with encrypted HTTPS streams.

On mobile devices, certificate pinning should be used to limit trust to website leaf certificates or only those intermediate or root authorities trusted by the app itself. You can pin against the certificates, their public keys, or hashes of their public keys. Options for storing these certificate or key pins include:

  • Preloading: Embedding the pins inside the distributed app package is usually the easiest to implement. Upgrading the pin set, though, requires an app field upgrade, so it is recommended to install multiple leaf and intermediate pins with each release.
  • Trust on First Use (TOFU): endpoints’ public keys are determined on first use after installation or upgrade. Security depends on the security of the environment at first use.
  • Pin Server: Request pins from a trusted server. How to you trust the pin server? Perhaps you pin the pin server! If the pin server is stable, this allows you to manage a dynamic set of endpoints without requiring application upgrades for each change of served pins. Upgrades are only required if the pin server’s certificates change. It’s an interesting trade off and the subject of a future post.

When verifying the pinning certificates, the client verifies both the certificate’s signature and the requested hostname. Because the same IP address may share multiple hostnames, Server Name Indication is a TLS extension which enables a client to request a specific certificate by virtual hostname. In addition to its use in virtual hosting, this technique simplifies debugging self-signed certificates served from localhost.

Pinning gRPC Managed Channels

With gRPC, a client makes an rpc call to a stub interface which, through a channel, sends one or more proto request messages to and receives one or more response messages from the server. In Consider gRPC for Mobile APIs, we used a plain managed channel for our transport. To pin the channel, we will enable TLS (SSL) and create our own set of trusted certificates, separate from the certificates already installed on the device. We’ll use Android for our examples.

First, we will build our own keystore within our demonstration app. For convenience, we store a set of public key certificates as raw resources, and we identify those resources in a certs.xml resource file:

The keystore is created when the ShapesActivity is started, and the keystore and a server name override are passed to our pinned managed channel, PinnedChannelBuilder:

We override the server name so that the server will respond with an end-entity certificate whose common name (CN) matches what we have pinned.

The PinnedChannelBuilder we are building will use a customized SSLSocketFactory to make its connections. Java’s security and networking stack make this a bit tedious, requiring 1) building a javax.net.ssl.TrustManagerFactory containing our java.security.KeyStore, 2) creating a javax.net.ssl.SSLContext containing this TrustManagerFactory, and 3) exposing the javax.net.ssl.SSLSocketFactory from within the SSLContext:

Though cumbersome, this is not much different than building a custom socket factory for a restful HTTPS connection, although many networking stacks and Android N have convenience methods to hide this complexity.

Trying It Out

To try it out, we use the same shapes demo application we used previously. We are running a gRPC server on localhost serving a shapes.proto API to a shapes app running in a local Android emulator.

 

Approov shapes app screenshot
 

First, we’ll generate a self-signed private key, public-key certificate pair for localhost using openssl:

The localhost.crt certificate file should be copied into the shape app’s raw resources directory (app/src/main/res/raw/). Similarly, we generate an otherhost.crt certificate file and also copy it to the raw resource directory.

We’ll install both the localhost.key private key and the localhost.crt certificate files into our gRPC server. gRPC servers are commonly configured for mutual-SSL. Our client is pinning the gRPC server and not the other way around, so ensure mutual SSL is disabled.

Now we fire up the app. The managed channel requests the localhost certificate from the server, and the channel is successfully pinned upon connection. Hitting the Stream button, we see the expected response:

gRPCStreamSeq

To test the pinning, we delete the localhost certificate from the app and restart. This time the server delivers the localhost certificate, but the trust manager finds no matching pinned certificate, so the channel connection fails:

gRPCStreamErr

To keep it simple, we have only shown self-signed leaf certificates. A better practice would be to pin on intermediate certificates. Square’s certstrap tool is an excellent resource for generating your own test certificate authority and longer key chains if you would like to explore these scenarios.

Wrapping Up

On Android, we were able to demonstrate a pinned gRPC channel, and it really wasn’t any more difficult than pinning a restful HTTPS connection. Including Consider gRPC for Mobile APIs, we have demonstrated:

  • A basic request-response API call
  • Token-based authentication
  • A single request, streaming response API call
  • Pinned TLS connections to the server

gRPC’s function call paradigm, along with gRPC’s ability to generate both client and server API interfaces for many target languages from a single proto file and our demonstration of basic and secure API functionality, makes gRPC a reasonable approach for mobile API development.

Skip Hovsmith

- Senior Consultant at Approov
Developer and Evangelist - Software Performance and API Security - Linux and Android Client and Microservice Platforms