gRPC on Android using TLS

Abdullah Jan Khan
7 min readJan 5, 2023

--

gRPC is a relatively new concept for the interaction between client and server, but not anymore.

gRPC client-server

What is Remote Procedure Call (RPC)?

A remote procedure call is a software communication protocol. One process can call a procedure (also known as a function or a sub-routine) on a remote system (e.g. local system) without prior knowledge of the remote system’s network.
RPC uses the traditional Client-Server model. A server defines the procedures, and a client requests the remote servers to apply business logic and return the result.

Rest vs gRPC:

Representational state transfer (REST) is a communication protocol of interaction between a client and server built over HTTP 1.1/HTTPS 1.1 with verbs like (PUT, POST, GET, etc.).

gRPC is a Remote Protocol Call from Google, an open-source, contract-based, cross-platform communication protocol to simplify inter-service communication.

gRPC leverages the bi-directional feature of HTTP/2.0 and TLS. gRPC also enables more efficient communication through serialized binary payloads. It uses a protocol buffer as its mechanism for payload management and serialization, similar to JSON of Rest.

Unlike JSON protocol buffers also include three major parts:

  1. A contract definition language, i.e. proto3 (latest protocol buffer language specification).
  2. Generated Function Code
  3. Language-specific runtime libraries

In contrast to HTTP/1.1, which sequentially loads resources, HTTP/2.0 uses a TCP connection and a single connection to carry many data streams, so there is no resource blocking. In HTTP/1.1 we can face an issue if a resource fails to load the sequence will be blocked.

What is proto3?

syntax = 'proto3';

package bookStorePackage;

// Book service definition. defines all the rpc methods here
service Book {
rpc createBook (BookItemReq) returns (BookItem);
rpc readBook (BookRequest) returns (BookItem);
rpc readBooks (Empty) returns (BooksList);
}

// inputs / request , e.g. BookItemReq
// outputs / response BookItem must be defined
// they are defined with a keyword of struct

message BookItemReq {
string name = 1;
string description = 2;
string author = 3;
}

message BookItem {
int32 id = 1;
string name = 2;
string description = 3;
string author = 4;
}

message BookRequest {
int32 id = 1;
}

message BooksList {
repeated BookItem books = 1;
}

message Empty {}

A `.proto` contains the structure of our gRPC and defines the procedures and the request/responses for each call. The .proto files are translated into proto buffer files using the protoc plugin.

protoc --proto_path=src --java_out=build/gen PROTO_PATH

Let’s look more into the code now. You can find many resources for a gRPC without TLS implementation. We will look into making a gRPC client in android with TLS.
Some requirements before we start are a secure gRPC server written in any language.

“How To” for Android Studio:

First, we need to set up a new project in android. In this project, we will use an empty activity. Next, set up the dependencies and add the following in build.gradle.


plugins {
id 'com.google.protobuf' version '0.8.18'
}

// this is used to generate proto buf file when you build the project
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.21.7"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.51.0'
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java { option 'lite' }
}
task.plugins {
grpc { option 'lite' }
}
}
}
}

dependencies {
// for TLS based connection we need okhttp
implementation 'io.grpc:grpc-okhttp:1.51.1'
implementation 'io.grpc:grpc-protobuf-lite:1.51.1'
implementation 'io.grpc:grpc-stub:1.51.1'
// these are used to set up a secure client
implementation 'com.squareup.okhttp3:okhttp-tls:4.10.0'
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
// for implementation of auto generated code
implementation 'javax.annotation:javax.annotation-api:1.3.2'
}

Now we need to add the proto file to our project.

File Structure Section in Android Studio

We need to change the structure from android to project. Now we can add our proto file in app/src/main/proto. We need to create a directory by the name of proto and place our .proto file in there.

Proto file destination app/src/main/proto

Now we need to build our project from the toolbar > build > rebuild project.

rebuild project to get generated code.

These are the generated file you will get.

auto-generated protocol buffer files.
BookGrpc auto-generated class

One important thing, as we know, in java, we have a specific package name in our project. How do we specify that in our proto file?
It is easy. We need to add an option for java to guide the compiler (protoc) in generating the proto buffer file.

proto file with java options

By adding the option in our proto file, our compiler will understand that it needs to specify the package name the one we have enforced. By default, it will use the package keyword as our package name.
Now let’s dig into how to set up our client to communicate with our gRPC server over a secure connection.
We need to place our signing certificate in the raw category in the res folder of our application.

location to add the signing certificate

After we have placed our certificate we need to just use the following piece of code and viola we can easily communicate with our secure server.

        try{
Resources res = getResources();
InputStream instream = res.openRawResource(R.raw.ca_cert);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(instream);
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(kStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sslSocketFactory = context.getSocketFactory();

final ManagedChannel channel = OkHttpChannelBuilder
.forAddress("IP Address",PORT)
.useTransportSecurity()
.overrideAuthority("add same DNS / IP:PORT entry here")
.sslSocketFactory(sslSocketFactory)
.build();
// BookGrpc is an auto generated class from the proto we defined.
BookGrpc.BookBlockingStub stub = BookGrpc.newBlockingStub(channel);
// create a request object
BookItemReq requestData = BookItemReq
.newBuilder()
.setName("AnyName")
.setAuthor("AnyAuthor")
.setDescription("Any Description")
.build();
BookItem response = stub.createBook(requestData);
// handle the response
System.out.println(response);
channel.shutdown();
} catch (Exception e){
e.printStackTrace();
}

Using the provided sample code you can communicate with your secure gRPC server.

What does the code do?

As we know to establish a secure connection, we have a set of certificates that ensure that our communication is encrypted. We need to add a signing certificate to our android application.

Next, we read the certificate using the following lines.

Resources res = getResources();
InputStream instream = res.openRawResource(R.raw.ca_cert);

First, we get the resource instance then we use the function openRawResource to read our certificate. Next, we generate a certificate from our file and add it as a trusted certificate. Using the trusted certificate, we initialize an SSL context instance to initialize an SSL socket factor. SSL socket factory acts as a factor of secure sockets.

An encrypted channel handles requests and responses from the server. We have used an OkHttpChannelBuilder, which supports the use of the SSLSocketFactor to provide a secure communication medium between client and server.

How to obtain certificates yourself?

To generate certificates you can use the following script:

rm *.pem
rm *.srl
rm *.cnf

# 1. Generate CA's private key and self-signed certificate
openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout ca-key.pem -out ca-cert.pem -subj "/C=FR/ST=Occitanie/L=Toulouse/O=Test Org/OU=Test/CN=*.test/emailAddress=test@gmail.com"

echo "CA's self-signed certificate"
openssl x509 -in ca-cert.pem -noout -text

# 2. Generate web server's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=FR/ST=Ile de France/L=Paris/O=Server TLS/OU=Server/CN=*.tls/emailAddress=tls@gmail.com"

# Remember that when we develop on localhost, It’s important to add the IP:0.0.0.0 as an Subject Alternative Name (SAN) extension to the certificate.
echo "subjectAltName=DNS:*.tls,DNS:example.com,IP:0.0.0.0" > server-ext.cnf
# Or you can use localhost DNS and grpc.ssl_target_name_override variable
# echo "subjectAltName=DNS:localhost" > server-ext.cnf

# 3. Use CA's private key to sign web server's CSR and get back the signed certificate
openssl x509 -req -in server-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.cnf

echo "Server's signed certificate"
openssl x509 -in server-cert.pem -noout -text

# 4. Generate client's private key and certificate signing request (CSR)
openssl req -newkey rsa:4096 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=FR/ST=Alsace/L=Strasbourg/O=PC Client/OU=Computer/CN=*.client.com/emailAddress=client@gmail.com"

# Remember that when we develop on localhost, It’s important to add the IP:0.0.0.0 as an Subject Alternative Name (SAN) extension to the certificate.
echo "subjectAltName=DNS:eaple.com,IP:0.0.0.0" > client-ext.cnf

# 5. Use CA's private key to sign client's CSR and get back the signed certificate
openssl x509 -req -in client-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile client-ext.cnf

echo "Client's signed certificate"
openssl x509 -in client-cert.pem -noout -text

Note: You can find the complete code at https://github.com/AbdullahJanKhan/android-grpc-tls

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Abdullah Jan Khan
Abdullah Jan Khan

Written by Abdullah Jan Khan

Backend geek | Blockchain | Go Lang | Node Contact me at: abdullah.jankhan445@gmail.com

Responses (3)

Write a response