Grpc Java SSL相互认证

Bel*_*via 3 java ssl mutual-authentication grpc

我想知道需要在GrpcSslContext中设置什么才能让Grpc Client与服务器进行SSL身份验证?

目前,以下代码适用于从服务器到客户端的常规单向SSL身份验证.

在服务器上,

SslContext sslContext = GrpcSslContexts.forServer(new File(pathToOwnCertPemFile), new File(pathToOwnPrivateKeyPemFile)).trustManager(new File(pathToClientCertPemFile)).build();

ServerImpl server = NettyServerBuilder
        .forPort(port)
        .sslContext(sslContext)
        .addService(MyGrpc.bindService(new MyGrpcService()))
        .build().start();
Run Code Online (Sandbox Code Playgroud)

在客户端,

SslContext sslContext = GrpcSslContexts.forClient().trustManager(new File(pathToServerCertPemFile)).keyManager(new File(pathToOwnCertPemFile), new File(pathToOwnPrivateKeyPemFile)).build();

ChannelImpl channel = NettyChannelBuilder.forAddress(host, port)
                .negotiationType(NegotiationType.TLS)
                .sslContext(sslContext).build();

blockingStub = MyGrpc.newBlockingStub(channel);
asyncStub = MyGrpc.newStub(channel);
Run Code Online (Sandbox Code Playgroud)

根据gRPC在https://github.com/grpc/grpc-java/blob/master/SECURITY.md,

如果需要相互认证,也可以通过创建适当的SslContext来支持.

我想知道我是否正确初始化了GrpcSslContexts?

任何建议/意见表示赞赏.

[更新]

在进一步故障排除后,我注意到CertificateRequest消息(如https://en.wikipedia.org/wiki/Transport_Layer_Security#Client-authenticated_TLS_handshake中所述)从未发送到客户端以启动客户端身份验证.

我的服务器日志摘录如下:......

*** ECDH ServerKeyExchange
Signature Algorithm SHA512withRSA
Server key: Sun EC public key, 256 bits
public x coord: 81392923578261760187813715443713168545877454618233337093852615933913992434989
public y coord: 26389586381130695169212775668808794166799180199461581135201001980310825571555
parameters: secp256r1 NIST P-256, X9.62 prime256v1
*** ServerHelloDone
[write] MD5 and SHA1 hashes: len = 1617
0000: 02 00 00 56 03 03 55 DF 34 10 9C 73 B5 00 C2 70 ...V..U.4..s...p
0010: FD B8 CC 36 5B 83 87 70 5B 74 A3 D2 AD B7 75 3B ...6[..p[t....u;
....
Run Code Online (Sandbox Code Playgroud)

我开始怀疑它可能是gRPC中的固有错误.

Eri*_*son 6

编辑:添加了支持.制作您自己的SslContext并传递给它NettyChannelBuilder.sslContext(),确保通过请求客户端证书SslContextBuilder.clientAuth().然后为每个RPC检查ClientCall.getAttributes()并获取SSLSession通道Grpc.TRANSPORT_ATTR_SSL_SESSION.


看起来gRPC-Java 似乎没有办法在服务器上请求客户端证书,这是相互身份验证所必需的.鉴于SSL/TLS如何工作,这不是一个错误,而是更多的功能请求.

Java实现尚未支持客户端证书作为完整功能.这将包括能够在服务器上检索客户端证书.请求客户端身份验证的能力必须是该功能的一部分.

客户端身份验证是grpc-Java打算支持的东西,但尚未开发.


Dan*_*ldt 5

要更新现有答案,现在可以访问SSLSession服务器端的 ,它提供客户端证书的句柄。github 项目的Security.md文件 ( https://github.com/grpc/grpc-java/blob/master/SECURITY.md ) 在Mutual TLS部分给出了一个明确的例子,如何这样做。

在撰写本文时,该文件指的是一个不存在的常量 ( ServerCall.SSL_SESSION_KEY),但我为此创建了一个问题,其中包含一个 workaraound ( https://github.com/grpc/grpc-java/issues/3013。) . 现在,问题得到解决后,我的解决方法不再是必需的,您可以按照 Security.md 的描述进行操作:

您必须做什么才能获得客户端证书:

实施一个ServerInterceptor

public class MyInterceptor implements ServerInterceptor {
    @Override
    public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        String subjectDn = null;
        try {
            SSLSession sslSession = (SSLSession) call.attributes().get(Grpc.TRANSPORT_ATTR_SSL_SESSION););
            // sslSession.getPeerCertificates(); contains the certificates, you are looking for 
            }
        } catch (SSLPeerUnverifiedException e) {
            // do what you need to do
        }
        return next.startCall(call, headers);
    }
}
Run Code Online (Sandbox Code Playgroud)

并将其插入您的Server

server = NettyServerBuilder.forPort(port)
                    .sslContext(context)
                    .addService(ServerInterceptors.intercept(new MyServiceImpl(), new MyInterceptor()))
                    .build();
Run Code Online (Sandbox Code Playgroud)

Security.md还告诉你,你怎么可以注入SSLSession到上下文,有一些关于它的手柄在你的MyServiceImpl方法,实施被称为远程程序。

请注意,这是实验性 API,因此请谨慎使用。