gRPC java client side TLS authentication using root.pem file and using username and password.

1,566 views
Skip to first unread message

Kishore Ganipineni

unread,
Jan 15, 2019, 4:09:39 PM1/15/19
to grpc.io
SSL/TLS Authentication of gRPC using root.pem file and username & password at client side.

To Authenticate the gRPC server using root pem certificate file and credentials in C++ we have a facility to provide both options from client like below.

pem file setup using environment variable option (C++):

setenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", fileBuff1, true);
sprintf(setSecBuff, "chmod 777 %s", fileBuff1);
system(setSecBuff);
Creating Channel Using ssl options(keyPassword if any):

SslCredentialsOptions ssl_opts;
TelemAsyncClient telemAsyncClient(grpc::CreateChannel(std::string(hostIpStr), grpc::SslCredentials(ssl_opts), ChannelArguments()));
Passing credentials using ClientContext(C++):

ClientContext context;
CompletionQueue cq;
Status status;

context.AddMetadata("username", userid);
context.AddMetadata("password", password);


// Print Populated GetRequest
printGetRequest(&getReq);
std::unique_ptr<ClientAsyncResponseReader<GetResponse> > rpc(stub_->AsyncGet(&context, getReq, &cq));
In java we have facility to pass the pem file but how to pass the credentials? Java code to pass pem file: ============================

ManagedChannel channel = NettyChannelBuilder.forAddress(ip, port)
.useTransportSecurity()
.negotiationType(NegotiationType.TLS)
.sslContext(GrpcSslContexts.forClient()
.trustManager(new File("<path>/test.pem"))
.clientAuth(ClientAuth.REQUIRE)
.build())
.overrideAuthority("test")
.build();
Tried to set the credentials using CallCredentials and ClientInterceptor options but none of the worked. Server side Username is not receiving. Hence getting io.grpc.StatusRuntimeException: UNAUTHENTICATED exception.

CallCredentials Tried:

OpenConfigGrpc.OpenConfigBlockingStub blockingStub = OpenConfigGrpc.newBlockingStub(channel).withCallCredentials(credentials);

public void applyRequestMetadata(MethodDescriptor<?, ?> methodDescriptor, Attributes attributes, Executor executor, final MetadataApplier metadataApplier) {
String authority = attributes.get(ATTR_AUTHORITY);
Attributes.Key<String> usernameKey = Attributes.Key.of("userId");
Attributes.Key<String> passwordKey = Attributes.Key.of("password");
attributes.newBuilder().set(usernameKey, username).build();
attributes.newBuilder().set(passwordKey, pasfhocal).build();
System.out.println(authority);
executor.execute(new Runnable() {
public void run() {
try {
Metadata headers = new Metadata();
Metadata.Key<String> usernameKey = Metadata.Key.of("userId", Metadata.ASCII_STRING_MARSHALLER);
Metadata.Key<String> passwordKey = Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);
headers.put(usernameKey, username);
headers.put(passwordKey, pasfhocal);
metadataApplier.apply(headers);
} catch (Exception e) {
metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
e.printStackTrace();
}finally{
logger.info("Inside CienaCallCredentials finally.");
}
}
});
}
Interceptors Tried:

OpenConfigGrpc.OpenConfigBlockingStub blockingStub = OpenConfigGrpc.newBlockingStub(channel).withInterceptors(interceptors);

public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel channel) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(channel.newCall(methodDescriptor, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
callOptions.withCallCredentials(credentials);
Metadata.Key<String> usernameKey = Metadata.Key.of("usernId", Metadata.ASCII_STRING_MARSHALLER);
headers.put(usernameKey, username);
Metadata.Key<String> passwordKey = Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);
headers.put(passwordKey, pasfhocal);
super.start(responseListener, headers);
}
};
}
Much appreciated your help if some can help on this how to authenticate gRPC using root.pem file and username and password.

Thanks in Advance, Kishore

Carl Mastrangelo

unread,
Jan 17, 2019, 9:11:40 PM1/17/19
to grpc.io
You are going to need to clarify some more, I can't tell what's going on in your setup.   Where do the username and password come from?  Why aren't you using an authentication token?    Have you read our Mutual TLS guide here https://github.com/grpc/grpc-java/blob/master/SECURITY.md#mutual-tls

sanjay...@google.com

unread,
Jan 18, 2019, 2:00:24 PM1/18/19
to grpc.io
Agree with Carl - we need some more clarification and eliminate confusion. 

From your subject line "client side TLS authentication" I get the impression you might be interested in mTLS (https://github.com/grpc/grpc-java/issues/4004  ) . Pls clarify if that's the case.

I saw your Java snippets on the client side but it is not clear how the username+password authentication is supposed to work on the server side. Can you paste the exception stack when you get the "io.grpc.StatusRuntimeException: UNAUTHENTICATED" exception? 

Kishore Ganipineni

unread,
Jan 21, 2019, 11:27:45 AM1/21/19
to grpc.io
Hi Carl, and Sanjay

We are trying to connect to a Vendor Router devices using gRPC where the SSL is enabled. We don't have any control on the server setup. We have been instructed to use certificates as well as credentials. They have given a c++ client to verify the data flow and If you observe the c++ code, first certificates are being set as Environment variable and then credentials are being set to ClientContext and passed to stub through get method. 

 setenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", fileBuff1, true);

ClientContext context;
CompletionQueue cq;
Status status;

context.AddMetadata("username", userid);     
context.AddMetadata("password", password);      

printGetRequest(&getReq); 
std::unique_ptr<ClientAsyncResponseReader<GetResponse> > rpc(stub_->AsyncGet(&context, getReq, &cq));


Even my impression was to just set ssl certificates or ssl certificates using keyStorePassword,.but here we are directed to use credentials too, that's why I have tried to pass the credentials using stub.withCallCredentials but credentials are not going through the request and getting UNAUTHENTICATED exception.


Thanks,
Kishore.

kishore.g...@verizon.com

unread,
Jan 22, 2019, 11:47:26 AM1/22/19
to grpc.io
Hi Sanjay,

More specific details are needed here and you should look them up in the Vendor Router documentation to answer the following questions:

- are certificates needed only for establishing (one-way) SSL or mTLS? I am assuming it is not mTLS but it is good to confirm. Note that mTLS is used to authenticate a client by the server.
 
  My understanding is for encryption might be. I don't have the documentation right now in hand, will get it and check the documentation.

- the credentials are just passed as "username" and "password" headers just like your C++ example shows? That should be relatively straightforward as shown in the Java auth examples here (https://github.com/grpc/grpc-java/blob/master/examples/AUTHENTICATION_EXAMPLE.md). I suggest you use that approach - of using ClientInterceptor and adding headers - instead of stub.withCallCredentials().

- can you provide the stack trace of UNAUTHENTICATED exception you are getting? 

I have tried the ClientInterceptor option , still getting the UNAUTHENTICATED exception. Below is the stacktrace.

io.grpc.StatusRuntimeException: UNAUTHENTICATED
        at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:233)
        at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:214)
        at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:139)
        at telemetry.OpenConfigGrpc$OpenConfigBlockingStub.get(OpenConfigGrpc.java:373)
        at OpenConfigTelemetryClient.get(OpenConfigTelemetryClient.java:208)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
[2019-01-22 16:33:27,534 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error Code:: UNAUTHENTICATED
[2019-01-22 16:33:27,534 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error description:: null
[2019-01-22 16:33:27,534 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error Cause:: null

Channel Creation code:

channel = NettyChannelBuilder.forAddress(ip, port)
.useTransportSecurity()
.negotiationType(NegotiationType.TLS)
    .sslContext(sslContext)
    .intercept(interceptor)
    .build();


ClientInterceptor Code:

public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel channel) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(channel.newCall(methodDescriptor, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
//callOptions.withCallCredentials(credentials);
Metadata.Key<String> usernameKey = Metadata.Key.of("userid", Metadata.ASCII_STRING_MARSHALLER);
headers.put(usernameKey, user);
Metadata.Key<String> passwordKey = Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);
headers.put(passwordKey, pass);
super.start(responseListener, headers);
}
};
}

Sanjay Pujare

unread,
Jan 22, 2019, 12:36:50 PM1/22/19
to grp...@googlegroups.com
Hi Kishore,

For encryption TLS (SSL) also works so mTLS is not needed for encryption.

In any case the info you have provided is useful although we still don't have the root cause. It seems the error occurred on the server side (was an ExecutionException) and we can rule out mTLS related issues.

In your C++ snippet you had "printGetRequest(&getReq);". Can you insert a similar print/log statement in the Java code and just compare the 2 requests going out? 

BTW I noticed that 
Your C++ code sets "username":

context.AddMetadata("username", userid);  

But your Java code has typos:

                Metadata.Key<String> usernameKey = Metadata.Key.of("usernId", Metadata.ASCII_STRING_MARSHALLER);
                headers.put(usernameKey, username);

in one place and

                                Metadata.Key<String> usernameKey = Metadata.Key.of("userid", Metadata.ASCII_STRING_MARSHALLER);
headers.put(usernameKey, user);
  
in a different place. Why are you not using "username" here as well?


--
You received this message because you are subscribed to a topic in the Google Groups "grpc.io" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/grpc-io/ZB2bwPCxOHI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to grpc-io+u...@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/79f3ee80-8a44-400e-a3cf-ce10f7312fbe%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

kishore.g...@verizon.com

unread,
Jan 22, 2019, 3:54:02 PM1/22/19
to grpc.io
Hi Sanjay,

I tried first with "username" to copy c++ but got INVALID_ARGUMENT exception. When I was searching for some solutions some where I got "userid" after using userid instead of username INVALID_ARGUMENT gone and getting UNAUTHENTICATED exception.

Yes I am printing request in Java too, below is the result
[2019-01-22 20:28:18,574 UTC] [INFO ] pool-1-thread-1 com.verizon.eclipse.client.OpenConfigTelemetryClient - Path List:: [element: "/statistics/otm"]

C++ result:
 Prefix :  --
 AsyncGet(GetRquest) =>: 
 Path: "statistics" "otm"

sanjay...@google.com

unread,
Jan 22, 2019, 4:35:15 PM1/22/19
to grpc.io
The real problem seems to be INVALID_ARGUMENT you are getting on the Java client side. If the server is expecting "username" header that needs to be sent and not something else. Let me see if I can find something about INVALID_ARGUMENT on the grpc java code side

sanjay...@google.com

unread,
Jan 22, 2019, 7:21:51 PM1/22/19
to grpc.io
Kishore: I think you should go back to using "username" instead of "userId" or "usernId" and then troubleshooting the code. I also noticed this catch clause in your Java code 

            } catch (Exception e) {
                    metadataApplier.fail(Status.UNAUTHENTICATED.withCause(e));
                    e.printStackTrace();
            }

where your code is generating the UNAUTHENTICATED error and not gRPC layer or the server. That could also be a source of confusion.

Looking at https://github.com/grpc/grpc-java/blob/master/core/src/main/java/io/grpc/Metadata.java there are multiple places where it calls checkArgument (which throws the INVALID_ARGUMENT exception) for different reasons. Generate a stack trace again and see where it is coming from.

kishore.g...@verizon.com

unread,
Jan 23, 2019, 4:19:24 PM1/23/19
to grpc.io
Hi Sanjay,

StackTrace for INVALID_ARGUMENT exception.

[2019-01-23 21:16:50,523 UTC] [INFO ] pool-1-thread-1 com.verizon.eclipse.client.OpenConfigTelemetryClient - connectivityState in connect:: IDLE

io.grpc.StatusRuntimeException: INVALID_ARGUMENT
        at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:233)
        at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:214)
        at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:139)
        at .proto_compiled.telemetry.OpenConfigGrpc$OpenConfigBlockingStub.get(OpenConfigGrpc.java:373)
        at OpenConfigTelemetryClient.get(OpenConfigTelemetryClient.java:187)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
[2019-01-23 21:16:51,252 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - sre.getTrailers():: Metadata()
[2019-01-23 21:16:51,252 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error Code:: INVALID_ARGUMENT
[2019-01-23 21:16:51,252 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error description:: null
[2019-01-23 21:16:51,252 UTC] [ERROR] pool-1-thread-1 OpenConfigTelemetryClient - Error Cause:: null

Sanjay Pujare

unread,
Jan 23, 2019, 5:14:15 PM1/23/19
to kishore.g...@verizon.com, grpc.io
Looking further it looks like an error coming from the server. You can see for yourself by looking at https://github.com/grpc/grpc-java/blob/master/stub/src/main/java/io/grpc/stub/ClientCalls.java and tracing the line numbers. At this stage it is best for you to troubleshoot this yourself. Couple of suggestions I have:

- log/print the request going out on the wire in each case (C++ and Java) and see what is different. 
- see if you can get the logs on the server (some kind of a Vendor router you mentioned?) and see if those logs reveal anything for the error case


Reply all
Reply to author
Forward
0 new messages