Grpc Java SSL 相互认证
Grpc Java SSL mutual authentication
我想知道需要在 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();
在客户端,
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);
根据 https://github.com/grpc/grpc-java/blob/master/SECURITY.md 上的 gRPC,
If mutual authentication is desired this can also be supported by creating the appropriate SslContext.
我想知道我是否正确初始化了 GrpcSslContexts?
感谢任何advice/comments。
[更新]
进一步排除故障后,我注意到 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;
....
我开始怀疑它可能是 gRPC 中的固有错误。
编辑:添加了支持。制作您自己的 SslContext
并将其传递给 NettyChannelBuilder.sslContext()
, making sure to request the client certificate via SslContextBuilder.clientAuth()
. Then for each RPC check the ClientCall.getAttributes()
and get the SSLSession
via Grpc.TRANSPORT_ATTR_SSL_SESSION
。
似乎 gRPC-Java does not have a way 请求服务器上的客户端证书,这是相互身份验证所必需的。考虑到 SSL/TLS 的工作原理,这并不是一个错误,而是更多的功能请求。
Java 实现尚未支持客户端证书作为完整功能。这将包括能够在服务器上检索客户端证书。请求客户端身份验证的能力必须是该功能的一部分。
Client auth 是 grpc-Java 完全有意支持的东西,但还没有开始工作。
要更新现有答案,现在可以访问服务器端的 SSLSession
,它提供客户端证书的句柄。 Security.md 项目的 github 文件 (https://github.com/grpc/grpc-java/blob/master/SECURITY.md) 在 Mutual TLS[=37= 部分给出了一个明确的例子],怎么做。
在撰写本文时,该文件引用了一个不存在的常量 (ServerCall.SSL_SESSION_KEY
),但我为此创建了一个问题,其中包含一个解决方法 (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);
}
}
并将其插入您的 Server
:
server = NettyServerBuilder.forPort(port)
.sslContext(context)
.addService(ServerInterceptors.intercept(new MyServiceImpl(), new MyInterceptor()))
.build();
Security.md 还告诉您如何将 SSLSession
注入上下文,以便在您的 MyServiceImpl
方法,实施
要调用的远程过程。
请注意,这是实验性的 API,因此请谨慎使用。
我想知道需要在 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();
在客户端,
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);
根据 https://github.com/grpc/grpc-java/blob/master/SECURITY.md 上的 gRPC,
If mutual authentication is desired this can also be supported by creating the appropriate SslContext.
我想知道我是否正确初始化了 GrpcSslContexts?
感谢任何advice/comments。
[更新]
进一步排除故障后,我注意到 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;
....
我开始怀疑它可能是 gRPC 中的固有错误。
编辑:添加了支持。制作您自己的 SslContext
并将其传递给 NettyChannelBuilder.sslContext()
, making sure to request the client certificate via SslContextBuilder.clientAuth()
. Then for each RPC check the ClientCall.getAttributes()
and get the SSLSession
via Grpc.TRANSPORT_ATTR_SSL_SESSION
。
似乎 gRPC-Java does not have a way 请求服务器上的客户端证书,这是相互身份验证所必需的。考虑到 SSL/TLS 的工作原理,这并不是一个错误,而是更多的功能请求。
Java 实现尚未支持客户端证书作为完整功能。这将包括能够在服务器上检索客户端证书。请求客户端身份验证的能力必须是该功能的一部分。
Client auth 是 grpc-Java 完全有意支持的东西,但还没有开始工作。
要更新现有答案,现在可以访问服务器端的 SSLSession
,它提供客户端证书的句柄。 Security.md 项目的 github 文件 (https://github.com/grpc/grpc-java/blob/master/SECURITY.md) 在 Mutual TLS[=37= 部分给出了一个明确的例子],怎么做。
在撰写本文时,该文件引用了一个不存在的常量 (ServerCall.SSL_SESSION_KEY
),但我为此创建了一个问题,其中包含一个解决方法 (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);
}
}
并将其插入您的 Server
:
server = NettyServerBuilder.forPort(port)
.sslContext(context)
.addService(ServerInterceptors.intercept(new MyServiceImpl(), new MyInterceptor()))
.build();
Security.md 还告诉您如何将 SSLSession
注入上下文,以便在您的 MyServiceImpl
方法,实施
要调用的远程过程。
请注意,这是实验性的 API,因此请谨慎使用。