资讯详情

资讯详情

grpc-java TLS 加密通信实战:基于 example-tls 实现单向 TLS 与双向 Mutual TLS

grpc-java TLS 加密通信实战基于 example-tls 实现单向 TLS 与双向 Mutual TLS【免费下载链接】grpc-javaThe Java gRPC implementation. HTTP/2 based RPC项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-javagRPC 默认基于 HTTP/2 明文传输生产环境中必须通过 TLS 保证通信的机密性与身份真实性。本指南以 grpc-java 仓库中的 examples/example-tls 示例为核心完整讲解 Hello World 服务在 TLS 场景下的构建、证书准备、单向 TLS 与双向 mTLS 的配置方法并深入剖析TlsServerCredentials、TlsChannelCredentials与overrideAuthority的底层实现原理。读完本文你将掌握 grpc-java 中基于文件证书的 TLS 服务端/客户端完整配置流程并能用三种主流构建工具Gradle、Maven、Bazel运行 TLS 示例。示例概览与目录结构example-tls是 grpc-java 仓库中的官方 TLS 演示项目与基础 Hello World 示例的唯一区别在于服务端与客户端在启动时加载 PEM 格式证书并通过Grpc.newServerBuilderForPort(...)/Grpc.newChannelBuilderForAddress(...)传入 TLS 凭据。其核心源码与配置如下HelloWorldServerTls.javaTLS 服务端入口解析命令行参数并组装TlsServerCredentialsHelloWorldClientTls.javaTLS 客户端入口解析命令行参数并组装TlsChannelCredentialshelloworld.proto仅包含一个Greeter.SayHello一元 RPC与普通 Hello World 完全相同pom.xml、BUILD.bazel、settings.gradle三种构建系统的配置文件。proto 定义中java_multiple_files true与java_package io.grpc.examples.helloworld决定了生成的桩代码GreeterGrpc、HelloRequest、HelloReply所在的包路径服务端与客户端的 import 均依赖这些生成类。前置条件gRPC Java 库与代码生成插件的安装示例的运行依赖 grpc-java 核心库与protoc-gen-grpc-java代码生成插件README 强烈建议先检出 git release 标签因为发布版本已包含可用的预构建产物git checkout vmajor.minor.patch如果坚持使用未发布版本例如 master HEAD则必须先按仓库根目录下的 COMPILING.md 完整构建 gRPC Java 库并将其连同代码生成插件安装到本地通过 Gradle 安装本地 SNAPSHOT在仓库根目录执行./gradlew publishToMavenLocal对应 COMPILING.md构建时如需跳过 C codegen 与 Android 模块可在project-root/gradle.properties中分别添加skipCodegentrue与skipAndroidtrue注意构建需要 JDK 8因为测试用例依赖 TLS。也就是说非 release 版本下运行本文所有构建与运行命令前必须先完成这一步。使用 Gradle 构建并运行推荐路径在examples/example-tls目录内执行$ ../gradlew installDist该命令会创建两个可直接执行的启动脚本位于build/install/example-tls/bin/目录下hello-world-tls-serverhello-world-tls-client与普通 Hello World 一样示例要求先启动服务端再启动客户端。TLS 版本只是额外增加了证书相关的命令行参数。服务端参数说明USAGE: HelloWorldServerTls port certChainFilePath privateKeyFilePath [trustCertCollectionFilePath] Note: You only need to supply trustCertCollectionFilePath if you want to enable Mutual TLS.参数含义如下参数必选说明port是服务监听端口certChainFilePath是PEM 格式的证书链文件路径服务器证书privateKeyFilePath是与证书配对的私钥文件路径trustCertCollectionFilePath否CA 证书集合文件路径仅在启用双向 mTLS 时需要客户端参数说明USAGE: HelloWorldClientTls host port [trustCertCollectionFilePath [clientCertChainFilePath clientPrivateKeyFilePath]] Note: clientCertChainFilePath and clientPrivateKeyFilePath are only needed if mutual auth is desired.参数必选说明host是服务端主机名port是服务端端口trustCertCollectionFilePath否CA 证书集合文件路径若不提供则使用系统默认证书颁发机构system default CAclientCertChainFilePath、clientPrivateKeyFilePath否客户端证书链与私钥仅在双向认证时需要单向 TLS无 mutual auth所谓单向 TLS指仅服务端出示证书、客户端通过 CA 校验服务端身份# 运行服务端 ./build/install/example-tls/bin/hello-world-tls-server 50440 ../../testing/src/main/resources/certs/server1.pem ../../testing/src/main/resources/certs/server1.key # 在另一个终端运行客户端 ./build/install/example-tls/bin/hello-world-tls-client localhost 50440 ../../testing/src/main/resources/certs/ca.pem双向 TLSMutual Auth双向 TLS 要求客户端也持有证书并在握手阶段出示服务端通过 CA 反向校验客户端身份# 运行服务端追加第四个参数 trustCertCollectionFilePath启用 mTLS ./build/install/example-tls/bin/hello-world-tls-server 50440 ../../testing/src/main/resources/certs/server1.pem ../../testing/src/main/resources/certs/server1.key ../../testing/src/main/resources/certs/ca.pem # 在另一个终端运行客户端追加 clientCertChainFilePath 与 clientPrivateKeyFilePath ./build/install/example-tls/bin/hello-world-tls-client localhost 50440 ../../testing/src/main/resources/certs/ca.pem ../../testing/src/main/resources/certs/client.pem ../../testing/src/main/resources/certs/client.key测试证书与 overrideAuthority为什么必须匹配 SANREADME 特别强调使用仓库自带测试证书时客户端必须通过.overrideAuthority(foo.test.google.fr)覆盖ManagedChannelBuilder此处实际为Grpc.newChannelBuilderForAddress返回的 builder的目标授权以匹配测试证书 Subject Alternative Names 中的域名。这是因为 TLS 握手时 gRPC 客户端会用authority默认是host:port去校验服务器证书的 SANSubject Alternative Name。测试证书server1.pem的 SAN 配置在 server1-openssl.cnf 中CN 为*.test.google.comSAN 包含foo.test.google.fr因此连接localhost时只有覆盖 authority 才能通过主机名校验。HelloWorldClientTls.java 中的实现如下ManagedChannel channel Grpc.newChannelBuilderForAddress(host, port, tlsBuilder.build()) /* Only for using provided test certs. */ .overrideAuthority(foo.test.google.fr) .build();源码注释明确说明overrideAuthority仅用于测试证书场景。如果你使用的是由真实 CA 签发的、SAN 与主机名匹配的正式服务器证书则不需要trustCertCollectionFilePath直接走系统默认 CA也不需要 overrideAuthorityNotetrustCertCollectionFilePathis not needed if you are using system default certificate authority.Note you can use system default certificate authority if you are using a real server certificate.仓库中的测试凭据位于 testing/src/main/resources/certs包含ca.pem/ca.key自签 CA、server1.pem/server1.key由 CA 签发、含 SAN、client.pem/client.key客户端证书、以及badclient.*/badserver.*自签错误凭据用于负面测试。用 OpenSSL 生成自己的自签名证书若不想使用仓库测试证书可参照 testing/src/main/resources/certs/README 中记录的生成命令。核心三步流程如下生成自签 CAopenssl req -x509 -new -newkey rsa:2048 -nodes -keyout ca.key -out ca.pem \ -config ca-openssl.cnf -days 3650 -extensions v3_req生成服务器私钥PKCS#8 无加密格式便于 gRPC 直接读取与证书签名请求openssl genrsa -out server.key.rsa 2048 openssl pkcs8 -topk8 -in server.key.rsa -out server.key -nocrypt openssl req -new -key server.key -out server.csr -config server1-openssl.cnf用 CA 签发服务器证书务必带上 SAN 扩展否则客户端主机名校验会失败openssl x509 -req -CA ca.pem -CAkey ca.key -CAcreateserial -in server.csr \ -out server.pem -extensions req_ext -extfile server1-openssl.cnf -days 3650客户端证书的生成方式相同仅需将 CN 设为testclient等标识。注意 gRPC 要求私钥为 PKCS#8 格式openssl pkcs8 -topk8 ... -nocrypt生成这正是仓库中client.key、server1.key的由来。源码剖析服务端如何组装 TlsServerCredentialsHelloWorldServerTls.java 的main方法按参数个数决定是否启用 mTLSif (args.length 3 || args.length 4) { System.out.println( USAGE: HelloWorldServerTls port certChainFilePath privateKeyFilePath [trustCertCollectionFilePath]\n Note: You only need to supply trustCertCollectionFilePath if you want to enable Mutual TLS.); System.exit(0); } // If only providing a private key, you can use TlsServerCredentials.create() instead of // interacting with the Builder. TlsServerCredentials.Builder tlsBuilder TlsServerCredentials.newBuilder() .keyManager(new File(args[1]), new File(args[2])); if (args.length 4) { tlsBuilder.trustManager(new File(args[3])); tlsBuilder.clientAuth(TlsServerCredentials.ClientAuth.REQUIRE); } final HelloWorldServerTls server new HelloWorldServerTls( Integer.parseInt(args[0]), tlsBuilder.build()); server.start(); server.blockUntilShutdown();关键点TlsServerCredentials.newBuilder()是 api/src/main/java/io/grpc/TlsServerCredentials.java 提供的工厂方法keyManager(File certChain, File privateKey)TlsServerCredentials.java加载服务端证书链与私钥trustManager(File rootCerts)TlsServerCredentials.java加载用于校验客户端证书的 CA 集合clientAuth(TlsServerCredentials.ClientAuth.REQUIRE)TlsServerCredentials.java强制要求客户端出示证书——这是单向 TLS 与双向 mTLS 在服务端侧的本质区别若只提供私钥而不需要额外配置也可直接用TlsServerCredentials.create()便捷方法这是源码注释中明确提到的简化路径。服务端随后通过Grpc.newServerBuilderForPort(port, creds)创建带 TLS 凭据的 ServerBuilderHelloWorldServerTls.java这与普通明文服务的ServerBuilder.forPort(port)形成对比——TLS 凭据必须在构建 ServerBuilder 时传入而不是运行时动态附加。源码剖析客户端如何组装 TlsChannelCredentialsHelloWorldClientTls.java 的main方法通过参数个数区分三种场景单向信任自定义 CA、双向认证、系统默认 CAif (args.length 2 || args.length 4 || args.length 5) { System.out.println(USAGE: HelloWorldClientTls host port [trustCertCollectionFilePath [clientCertChainFilePath clientPrivateKeyFilePath]]\n Note: clientCertChainFilePath and clientPrivateKeyFilePath are only needed if mutual auth is desired.); System.exit(0); } // If only defaults are necessary, you can use TlsChannelCredentials.create() instead of // interacting with the Builder. TlsChannelCredentials.Builder tlsBuilder TlsChannelCredentials.newBuilder(); switch (args.length) { case 5: tlsBuilder.keyManager(new File(args[3]), new File(args[4])); // fallthrough case 3: tlsBuilder.trustManager(new File(args[2])); // fallthrough default: } String host args[0]; int port Integer.parseInt(args[1]); ManagedChannel channel Grpc.newChannelBuilderForAddress(host, port, tlsBuilder.build()) /* Only for using provided test certs. */ .overrideAuthority(foo.test.google.fr) .build();要点解析参数组合规则args.length 2表示完全信任系统默认 CA无需任何证书文件args.length 3表示自定义 trustManager信任私有 CAargs.length 5表示双向认证追加客户端证书与私钥args.length 4属于非法组合直接打印 USAGE 退出switch fallthrough 的巧妙复用case 5先加载客户端 keyManager随后 fallthrough 到case 3加载 trustManager一段代码覆盖两种场景客户端最终通过Grpc.newChannelBuilderForAddress(host, port, creds)建立带 TLS 的 Channel并调用blockingStub.sayHello(request)发起 RPC捕获StatusRuntimeException记录失败状态HelloWorldClientTls.java类似地若只需默认配置可使用TlsChannelCredentials.create()便捷方法。从源码结构看TlsServerCredentials与TlsChannelCredentials是对称设计服务端侧重keyManager 可选的trustManagerclientAuth策略客户端侧重可选的trustManager 可选的keyManager。两者最终都转换为 Netty 底层的 SslContext 配置示例的 Bazel 构建中显式依赖了io_netty_netty_tcnative_boringssl_static与io_netty_netty_tcnative_classes见 BUILD.bazel这是 Netty TLS 的 BoringSSL 原生实现。使用 Maven 构建与运行如果偏好 Mavenexample-tls同样提供了完整的 pom.xml。该 POM 通过protobuf-maven-plugin自动完成 protoc 与protoc-gen-grpc-java的代码生成其中grpc.version为1.85.0-SNAPSHOTprotoc.version为3.25.8并依赖grpc-protobuf、grpc-stub、grpc-netty-shaded三个核心 artifact。在examples/example-tls目录下执行$ mvn verify $ # 运行服务端 $ mvn exec:java -Dexec.mainClassio.grpc.examples.helloworldtls.HelloWorldServerTls -Dexec.args50440 ../../testing/src/main/resources/certs/server1.pem ../../testing/src/main/resources/certs/server1.key $ # 在另一个终端运行客户端 $ mvn exec:java -Dexec.mainClassio.grpc.examples.helloworldtls.HelloWorldClientTls -Dexec.argslocalhost 50440 ../../testing/src/main/resources/certs/ca.pemmvn verify会先编译并执行测试确保生成的桩代码正确之后两次mvn exec:java分别以HelloWorldServerTls和HelloWorldClientTls作为主类启动。注意 Maven 路径下证书的相对路径../../testing/...同样以examples/example-tls为当前工作目录计算。使用 Bazel 构建与运行仓库根目录基于 Bazel 构建example-tls的 BUILD.bazel 定义了proto_library→java_proto_library→java_grpc_library的生成链以及两个java_binary目标。运行方式如下$ bazel build :hello-world-tls-server :hello-world-tls-client $ # 运行服务端 $ ../bazel-bin/hello-world-tls-server 50440 ../../testing/src/main/resources/certs/server1.pem ../../testing/src/main/resources/certs/server1.key $ # 在另一个终端运行客户端 $ ../bazel-bin/hello-world-tls-client localhost 50440 ../../testing/src/main/resources/certs/ca.pemBazel 生成的二进制输出到仓库根目录的bazel-bin/因此命令中需要../bazel-bin/...前缀相对于examples/example-tls。另外该 BUILD 文件中两个java_binary目标均标记为testonly 1说明官方将示例视为测试用途生产代码不应直接引用。常见问题与排查思路证书与主机名不匹配UNAVAILABLE / SSL 握手失败检查服务器证书 SAN 是否包含客户端连接的 host或是否遗漏.overrideAuthority(...)私钥格式错误gRPC 需要 PKCS#8 无加密私钥用openssl pkcs8 -topk8 -in key.rsa -out key -nocrypt转换mTLS 客户端未提供证书服务端clientAuth(REQUIRE)后若客户端只传了trustCertCollectionFilePathargs.length 3握手会因缺少客户端证书而失败使用真实证书时的简化真实 CA 签发的证书可省略客户端trustCertCollectionFilePath直接依赖系统默认 CA 存储运行顺序务必先启动服务端再启动客户端这与普通 Hello World 示例的行为一致。小结本文完整还原了 examples/example-tls/README.md 的全部构建与运行流程并深入到HelloWorldServerTls、HelloWorldClientTls的源码实现与TlsServerCredentials/TlsChannelCredentials的 Builder API。核心要点可归纳为三条单向 TLS 只需服务端证书双向 mTLS 需服务端clientAuth(REQUIRE)且客户端提供证书测试证书必须配合overrideAuthority使用而真实证书则可直接走系统默认 CA。无论使用 Gradle、Maven 还是 Bazelexample-tls都是一份可直接复制的 TLS 接入范本——只需将测试证书替换为正式签发的证书即可无缝迁移到生产环境。【免费下载链接】grpc-javaThe Java gRPC implementation. HTTP/2 based RPC项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-java创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →