While it certainly seems like the TLS client certificate is a better way to 
identify the peer on the other end, if for any reason you need to 
authenticate the payload of the RPC, I recommend directly signing the 
payload and sending the signature alongside it (instead of metadata). You 
can do this by wrapping the request_serializer/response_deserializer 
functions on the client side and request_deserializer/response_serializer 
objects on the server side. Here's probably the easiest way to do it:

On the client side, create your own object implementing the grpc.Channel 
interface, wrapping the gRPC channel object, and wrapping the serializers 
in unary_unary, unary_stream, stream_unary, stream_stream:


def _signing_serializer(identity, serializer):
def sign_and_serialize(message):
binary_message = message if serializer is None else serializer(message)
# _sign implements your signing logic and returns a bytestring with message 
and signature
return _sign(identity, binary_message)
return sign_and_serialize

def _verifying_deserializer(identity, deserializer):
def verify_and_deserialize(message):
# _verify_and_unwrap implements your custom verification logic and returns 
the original binary message and a boolean to indicate validity
binary_message, valid = _verify_and_unwrap(message)
if not valid:
return None
return binary_message if deserializer is None else deserializer(message)
return verify_and_deserialize

class PayloadSigningChannel(grpc.Channel)

def __init__(channel, identity):
self._channel = channel
self._identity = identity

def unary_unary(self,
method,
request_serializer=None,
response_deserializer=None):
return self._wrapped_channel.unary_unary(method, 
request_serializer=_signing_request_serializer(self._identity, 
request_serializer),
response_deserializer=_verifying_deserializer(self._identity, 
response_deserializer))


and on the server, you can register a simple interceptor to accomplish the 
same thing:

class PayloadSigningServerInterceptor(grpc.ServerInterceptor):
def __init__(identity):
self._identity = identity
def intercept_service(self, continuation, handler_call_details):
handler = continuation(handler_call_details)
if handler is None:
return None
if handler.request_streaming and handler.response_streaming:
return grpc.stream_stream_rpc_method_handler(handler.stream_stream,
request_deserializer=_verifying_deserializer(self._identity, handler.
request_deserializer),
response_serializer=_signing_serializer(self._identity, handler.
_response_serializer))
elif handler.request_streaming and not handler.response_streaming:
return grpc.stream_unary_rpc_method_handler(handler.stream_unary,
request_deserializer=_verifying_deserializer(self._identity, handler.
request_deserializer),
response_serializer=_signing_serializer(self._identity, handler.
_response_serializer))
elif not handler.request_streaming and handler.response_streaming:
return grpc.unary_stream_rpc_method_handler(handler.unary_stream,
request_deserializer=_verifying_deserializer(self._identity, handler.
request_deserializer),
response_serializer=_signing_serializer(self._identity, handler.
_response_serializer))
else:
return grpc.unary_unary_rpc_method_handler(handler.unary_unary,
request_deserializer=_verifying_deserializer(self._identity, handler.
request_deserializer),
response_serializer=_signing_serializer(self._identity, handler.
_response_serializer))


On Thursday, February 8, 2018 at 10:34:37 PM UTC-8, Carl Mastrangelo wrote:
>
> One thing to realize is that Protobuf is not always going to serialize the 
> message the same way, so you'll need to use raw bytes to wrap the message 
> anyways.   
>
> I'm surprised that the interceptor is a lot of boiler plate; what language 
> are you using gRPC with?
>
> Lastly: gRPC is protobuf agnostic.   You can use it without using proto at 
> all, so you should always be able to get at the raw message bytes, and not 
> just through the call credentials api.
>
>
> On Thu, Feb 8, 2018 at 10:19 PM Haiwei Zhou <high...@gmail.com 
> <javascript:>> wrote:
>
>> Thanks for replying.
>>
>> TLS had been adopted, otherwise call credentials cannot be used. 
>>
>> A RPC service is designed to handle core logic. A Web service provides UI 
>> to proxy user request to the RPC service. Then a request signature should 
>> be introduced to verify the real authorization.
>>
>> Using customized interceptor means a lot of boilerplate code, which I try 
>> to avoid. The best way I guess is that call credentials API provides raw 
>> request buffer.
>>
>>
>> On 9 February 2018 at 11:34, 'Carl Mastrangelo' via grpc.io <
>> grp...@googlegroups.com <javascript:>> wrote:
>>
>>> To do this, you'll need to wrap the serialized proto.   Actually, you 
>>> don't even need to put the signature in the headers.   For example:
>>>
>>>
>>> message Wrapper {
>>>   bytes signature = 1;
>>>   bytes message = 2;
>>> }
>>>
>>>
>>> From the client:
>>>
>>> 1.   Serialize your messsage
>>> 2.   Put this in field 2
>>> 3.   Sign the message and put this in field 1.
>>>
>>> Send the wrapper as your message type.   
>>>
>>> From the server:
>>>
>>> 1.  Receive the wrapper proto
>>> 2.   Verify the signature on the data
>>> 3.  Deserialize the data.
>>>
>>>
>>> You can also do this from an interceptor, which would make the process 
>>> transparent from the application point of view.
>>>
>>> That said, you're probably better off just using TLS with a client side 
>>> certificate, which the server can verify and then you can trust all the 
>>> data that comes over the wire. 
>>>
>>>
>>> On Thursday, February 8, 2018 at 1:27:04 AM UTC-8, high...@gmail.com 
>>> wrote:
>>>>
>>>> Hi,
>>>>
>>>>   I try to use call credentials to verify the request sanity:
>>>>
>>>>   In the client, 
>>>>          serialize the request to string,
>>>>          sign the string,
>>>>          and send the signature as metadata.
>>>>  
>>>>   In the server, 
>>>>         serialize the request to string,
>>>>         sign the string,
>>>>         sign the request,
>>>>         and compare the signature to the one in metadata.
>>>>
>>>>   It works perfect until I met a message with a map. Protobuf 
>>>>  serialization doesn't guarantee the order of map items. 
>>>>
>>>>   How could I get original serialization string of the request in the 
>>>> server side using python API?
>>>>
>>>> Thanks,
>>>> Haiwei
>>>>
>>> -- 
>>> 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/SPAv92gUypA/unsubscribe.
>>> To unsubscribe from this group and all its topics, send an email to 
>>> grpc-io+u...@googlegroups.com <javascript:>.
>>> To post to this group, send email to grp...@googlegroups.com 
>>> <javascript:>.
>>> 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/e1cfcfca-9da1-4439-b4e3-d9a37d533648%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/grpc-io/e1cfcfca-9da1-4439-b4e3-d9a37d533648%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to grpc-io+unsubscr...@googlegroups.com.
To post to this group, send email to grpc-io@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/904b37d6-262e-4b67-9a80-3fa3772b291d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to