Hakky54/mutual-tls-ssl

GitHub: Hakky54/mutual-tls-ssl

一份全面的 TLS/SSL 双向认证教程项目,提供从证书生成到 40 余种 JVM 语言 HTTP 客户端 SSL 配置的完整示例与自动化脚本。

Stars: 634 | Forks: 127

[![Actions 状态](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/13d9ec9562054223.svg)](https://github.com/Hakky54/mutual-tls-ssl/actions) [![可维护性评分](https://sonarcloud.io/api/project_badges/measure?project=nl.altindag%3Amutual-tls-ssl&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=nl.altindag%3Amutual-tls-ssl) [![质量门状态](https://sonarcloud.io/api/project_badges/measure?project=nl.altindag%3Amutual-tls-ssl&metric=alert_status)](https://sonarcloud.io/dashboard?id=nl.altindag%3Amutual-tls-ssl) [![覆盖率](https://sonarcloud.io/api/project_badges/measure?project=nl.altindag%3Amutual-tls-ssl&metric=coverage)](https://sonarcloud.io/dashboard?id=nl.altindag%3Amutual-tls-ssl) [![JDK 兼容性: 21](https://img.shields.io/badge/JDK_compatibility-21-blue.svg)](#) [![Apache2 许可证](https://img.shields.io/badge/license-Aache2.0-blue.svg)](https://github.com/Hakky54/mutual-tls-ssl/blob/master/LICENSE) [![GitHub 星标图表](https://img.shields.io/badge/github%20stars-chart-blue.svg)](https://seladb.github.io/StarTrack-js/#/preload?r=hakky54,mutual-tls-ssl) [![加入聊天 https://gitter.im/hakky54/mutual-tls-ssl](https://badges.gitter.im/hakky54/mutual-tls-ssl.svg)](https://gitter.im/hakky54/mutual-tls-ssl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-white.svg)](https://sonarcloud.io/dashboard?id=nl.altindag%3Amutual-tls-ssl) # 掌握双向 TLS 🔐 [![发推文](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=If%20you%20are%20interested%20in%20securing%20your%20web%20application,%20you%20might%20want%20to%20read%20the%20tutorial%20Mastering%20two-way%20tls&url=https://github.com/Hakky54/mutual-tls&via=hakky541&hashtags=encryption,security,https,ssl,tls,certificate,developer,java,scala,kotlin,sslcontextkickstart) 嘿,你好呀 👋 欢迎来到本教程!希望你会喜欢。 这个教程的诞生最初是因为我想学习基于 TLS 的双向认证。所以它一开始根本算不上教程,只是一个练手项目。 我想了解如何在本地设置它以及需要使用哪些命令。当我成功运行之后,我认为它可能对其他人也有用,所以我进行了重构,使其易于复现,并将其作为教程放在这个页面上,以便其他人可以从中学习。 本教程将引导你完成使用 TLS 认证保护应用程序的过程,仅允许持有特定证书的用户进行访问。这意味着你可以选择允许哪些用户调用你的应用程序。 # 目录 1. [简介](#introduction) 2. [教程](#tutorial) - [启动服务器](#starting-the-server) - [向服务器发送问候(无加密)](#saying-hello-to-the-server-without-encryption) - [在服务器上启用 HTTPS(单向 TLS)](#enabling-https-on-the-server-one-way-tls) - [要求客户端表明身份(双向 TLS)](#require-the-client-to-identify-itself-two-way-tls) - [基于信任证书颁发机构的双向 TLS](#two-way-tls-based-on-trusting-the-certificate-authority) 3. [自动化脚本](#automated-script-for-enabling-authentication-with-tls) 4. [已测试的 Http 客户端](#tested-clients) 5. [演示和演练视频](#demo-and-walk-through-video) 6. [贡献](#contributing) # 简介 这个示例项目演示了服务器和客户端的基本设置。服务器和客户端之间的通信通过 HTTP 进行,因此目前完全没有加密。目标是确保所有通信都被加密。 **定义** * Identity(身份):一个包含密钥对(也称为私钥和公钥)的 KeyStore * TrustStore(信任库):一个包含一个或多个证书(也称为公钥)的 KeyStore。此 KeyStore 包含受信任证书的列表 * 单向认证(也称为单向 tls,单向 ssl):客户端验证对端证书的 Https 连接 * 双向认证(也称为双向 tls,双向 ssl,相互认证):客户端和对端互相验证证书的 Https 连接,也称为相互认证 **有用的链接** * [Keytool 备忘录](https://gist.github.com/Hakky54/7a2f0fcbcf5fdf4674d48f1a0b31c862) * [Openssl 备忘录](https://gist.github.com/Hakky54/b30418b25215ad7d18f978bc0b448d81) * [Curl 配合 Java KeyStore 使用](https://gist.github.com/Hakky54/049299f0874fd4b870257c6458e0dcbd) * [Http 客户端配置备忘录](#tested-clients) **一些历史** 我大部分时间都在使用 Apache Http Client,因此我最初创建这个项目时只使用了 Apache 的 http 客户端。 一段时间后,我发现还有很多其他的 Java 客户端,并且也有一些基于 Kotlin 和 Scala 的客户端可用。 配置 ssl/tls 可能很困难,而且每个客户端都需要不同的设置。我想与其他开发人员分享这些知识,因为每个开发人员可能会使用不同的 http 客户端。 我还想为所有开发人员和社区提供一份**备忘录**,请参阅[此处](#tested-clients)获取包含示例客户端配置和示例 http 请求的 [40+ http 客户端](#tested-clients)) 列表。 客户端模块随着时间推移不断增长,并包含了大量用于测试所有这些适用于 Java、Scala 和 Kotlin 的 http 客户端的依赖项。因此,客户端模块可能看起来很复杂。请注意,出于这个特定原因,它会在初次构建期间下载大量依赖项。 此外,在这个项目的生命周期中,[GitHub - Ayza](https://github.com/Hakky54/ayza) 也应运而生,以轻松配置所有这些客户端。每个 http 客户端可能需要不同的 ssl 对象来启用 ssl,而该库确保在拥有基本 ssl 配置的同时,能够提供所有类型来配置这些客户端。 # 教程 ## 启动服务器 **最低要求:** 1. Java 21 2. Maven 3.9.10 3. Eclipse、Intellij IDEA(或任何其他文本编辑器,如 VIM) 4. 一个终端 本项目包含一个 Maven 包装器,因此你可以在不安装 Maven 的情况下运行此项目。本教程的文档除了默认的 mvn 命令外,还包含了 Maven 包装器的命令。 如果你想使用 Java 8 或 Java 11 运行此项目,可以通过下面的 git 命令获取较旧的版本。 - `git checkout tags/java-8-compatible` - `git checkout tags/java-11-compatible` 服务器依赖于项目的其他组件,因此首先需要在根目录中运行 `mvn install`。 ``` mvn install ``` 通过运行服务器项目中 [App 类](server/src/main/java/nl/altindag/server/App.java) 的 main 方法,或者在根目录的终端中运行以下命令来启动服务器: ``` cd server/ && mvn spring-boot:run ``` 或者使用 Maven 包装器 ``` cd server/ && ./../mvnw spring-boot:run ``` ## 向服务器发送问候(无加密) 当前,服务器在默认端口 8080 上运行且未启用加密。你可以在终端中使用以下 curl 命令调用 hello 接口: ``` curl -i -XGET http://localhost:8080/api/hello ``` 它应该会给出以下响应: ``` HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 5 Date: Sun, 11 Nov 2018 14:21:50 GMT Hello ``` 你也可以使用 client 目录中提供的客户端来调用服务器。该客户端是一个基于 Cucumber 的集成测试,你可以通过在 IDE 中运行 [ClientRunnerIT](client/src/test/java/nl/altindag/client/ClientRunnerIT.java) 类,或者在终端的根目录中运行以下命令来启动它:`cd client/ && mvn exec:java`,或者使用 Maven 包装器 `cd client/ && ./../mvnw exec:java`。 有一个描述集成测试步骤的 [Hello.feature](client/src/test/resources/features/Hello.feature) 文件。你可以在客户端项目的测试资源中找到它。 还有另一种方法可以同时运行服务器和客户端,即在根目录中使用以下命令:`mvn clean verify`,或者使用 Maven 包装器 `./mvnw clean verify`。 客户端默认向 localhost 发送请求,因为它期望服务器在同一台机器上。如果服务器运行在不同的机器上,你仍然可以在运行客户端时通过以下 VM 参数提供自定义 url:`-Durl=http://[HOST]:[PORT]` ## 在服务器上启用 HTTPS(单向 TLS) 现在,你将学习如何通过启用 TLS 来保护你的服务器。你可以通过将所需属性添加到名为 `application.yml` 的应用程序属性文件中来实现此目的。 添加以下属性: ``` server: port: 8443 ssl: enabled: true ``` 你可能会问自己为什么端口设置为 8443。使用 https 的 Tomcat 服务器的端口约定是 8443,而 http 的约定是 8080。因此,我们可以将端口 8080 用于 https 连接,但这是一种糟糕的做法。有关端口约定的更多信息,请参阅 [Wikipedia](https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers)。 重启服务器,以便它能够应用你所做的更改。你可能会遇到以下异常:`IllegalArgumentException: Resource location must not be null`。 你收到此消息是因为服务器需要一个包含服务器证书的 keystore,以确保与外部世界的连接是安全的。如果你提供以下 VM 参数,服务器可以为你提供更多信息:`-Djavax.net.debug=SSL,keymanager,trustmanager,ssl:handshake` 为了解决这个问题,你将为服务器创建一个包含公钥和私钥的 keystore。公钥将与用户共享,以便他们可以加密通信。用户和服务器之间的通信可以使用服务器的私钥进行解密。请永远不要分享服务器的私钥,因为其他人可能会拦截通信,并能够看到加密通信的内容。 要创建包含公钥和私钥的 keystore,请在终端中执行以下命令: ``` keytool -v -genkeypair -dname "CN=Hakan,OU=Amsterdam,O=Thunderberry,C=NL" -keystore server/src/main/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias server -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi,IP:127.0.0.1 ``` 现在,你需要告诉你的服务器 keystore 的位置并提供密码。将以下内容粘贴到你的 `application.yml` 文件中: ``` server: port: 8443 ssl: enabled: true key-store: classpath:identity.jks key-password: secret key-store-password: secret ``` 恭喜!你在服务器和客户端之间启用了 TLS 加密连接!现在,你可以尝试使用以下 curl 命令调用服务器:`curl -i --insecure -v -XGET https://localhost:8443/api/hello` 让我们同时运行 [ClientRunnerIT](client/src/test/java/nl/altindag/client/ClientRunnerIT.java) 类中的客户端。 你将看到以下错误消息:`java.net.ConnectException: Connection refused (Connection refused)`。看起来客户端正试图向服务器发送问候,但服务器似乎并不存在。问题在于客户端正试图在端口 8080 上向服务器发送问候,而服务器在端口 8443 上处于活动状态。对 Constants 类应用以下更改: **从:** ``` private static final String DEFAULT_SERVER_URL = "http://localhost:8080"; ``` **到:** ``` private static final String DEFAULT_SERVER_URL = "https://localhost:8443"; ``` 让我们再次尝试运行客户端,你会看到出现以下消息:`javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target`。 这意味着客户端希望通过 HTTPS 进行通信,并且在握手过程中收到了它尚未识别的服务器证书。 因此,你还需要创建一个 truststore。truststore 就像一个包含受信任证书的手提箱。客户端会将其在 SSL 握手过程中收到的证书与其 truststore 中的内容进行比较。如果匹配,则 SSL 握手过程将继续。在创建 truststore 之前,你需要拥有服务器的证书。你可以使用以下命令获取它: **导出服务器的证书** ``` keytool -v -exportcert -file server/src/main/resources/server.cer -alias server -keystore server/src/main/resources/identity.jks -storepass secret -rfc ``` 现在,你可以为客户端创建 truststore,并使用以下命令导入服务器的证书: ``` keytool -v -importcert -file server/src/main/resources/server.cer -alias server -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt ``` 你已为客户端创建了 truststore。不幸的是,客户端对此并不知情。现在,你需要告诉它需要使用具有正确位置和密码的 truststore。你还需要告诉客户端已启用认证。在客户端的 `application.yml` 文件中提供以下属性: ``` client: ssl: one-way-authentication-enabled: true two-way-authentication-enabled: false trust-store: truststore.jks trust-store-password: secret ``` ## 要求客户端表明身份(双向 TLS) 下一步是要求客户端进行认证。这将强制客户端表明身份,通过这种方式,服务器还可以验证客户端的身份以及它是否是受信任的。你可以通过使用 client-auth 属性告诉服务器你也希望验证客户端来启用此功能。将以下属性放入服务器的 `application.yml` 中: ``` server: port: 8443 ssl: enabled: true key-store: classpath:identity.jks key-password: secret key-store-password: secret client-auth: need ``` 如果你运行客户端,它将失败并显示以下错误消息:`javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate`。这表明客户端的证书无效,因为根本没有证书。那么,让我们使用以下命令创建一个: ``` keytool -v -genkeypair -dname "CN=Suleyman,OU=Altindag,O=Altindag,C=NL" -keystore client/src/test/resources/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias client -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth ``` 你还需要为服务器创建一个 truststore。在创建 truststore 之前,你需要拥有客户端的证书。你可以使用以下命令获取它: **导出客户端的证书** ``` keytool -v -exportcert -file client/src/test/resources/client.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret -rfc ``` **使用客户端证书创建服务器的 truststore** ``` keytool -v -importcert -file client/src/test/resources/client.cer -alias client -keystore server/src/main/resources/truststore.jks -storepass secret -noprompt ``` 你为客户端创建了额外的 keystore。不幸的是,客户端对此并不知情。现在,你需要告诉它也需要使用具有正确位置和密码的 keystore。你还需要告诉客户端已启用双向认证。在客户端的 `application.yml` 文件中提供以下属性: ``` client: ssl: one-way-authentication-enabled: false two-way-authentication-enabled: true key-store: identity.jks key-password: secret key-store-password: secret trust-store: truststore.jks trust-store-password: secret ``` 服务器也不知道新创建的 truststore。因此,将当前属性替换为以下属性: ``` server: port: 8443 ssl: enabled: true key-store: classpath:identity.jks key-password: secret key-store-password: secret trust-store: classpath:truststore.jks trust-store-password: secret client-auth: need ``` 如果你再次运行客户端,你将看到测试通过,并且客户端以安全的方式收到了来自服务器的 hello 消息。恭喜!你完成了双向 TLS 的安装! ## 基于信任证书颁发机构的双向 TLS 还有另一种实现双向认证的方法,那就是基于信任证书颁发机构。它有优点也有缺点。 **优点** - 客户端无需添加服务器的证书 - 服务器无需添加所有客户端的证书 - 维护工作量会减少,因为只有证书颁发机构的证书有效期会过期 **缺点** - 你无法再控制哪些应用程序被允许调用你的应用程序。你向任何拥有由证书颁发机构签名证书的应用程序授予权限。 以下是具体步骤: 1. [创建证书颁发机构](#creating-a-certificate-authority) 2. [创建证书签名请求](_URL_34/>) 3. [使用证书签名请求签署证书](#signing-the-certificate-with-the-certificate-signing-request) 4. [用已签名的证书替换未签名的证书](#replace-the-unsigned-certificate-with-a-signed-one) 5. [仅信任证书颁发机构](#trusting-the-certificate-authority-only) #### 创建证书颁发机构 通常已经存在一个证书颁发机构,你需要提供你的证书以使其被签名。在这里你将创建自己的证书颁发机构,并用它来签名客户端和服务器证书。要创建一个证书颁发机构,你可以执行以下命令: ``` keytool -v -genkeypair -dname "CN=Root-CA,OU=Certificate Authority,O=Thunderberry,C=NL" -keystore root-ca/identity.jks -storepass secret -keypass secret -keyalg RSA -keysize 2048 -alias root-ca -validity 3650 -deststoretype pkcs12 -ext KeyUsage=digitalSignature,keyCertSign -ext BasicConstraints=ca:true,PathLen:3 ``` 或者你可以使用仓库中已提供的那个,请参阅 [identity.jks](root-ca/identity.jks) #### 创建证书签名请求 要让你的证书被签名,你需要提供一个证书签名请求文件。可以使用以下命令创建它: ##### 服务器的证书签名请求 ``` keytool -v -certreq -file server/src/main/resources/server.csr -keystore server/src/main/resources/identity.jks -alias server -keypass secret -storepass secret -keyalg rsa ``` ##### 客户端的证书签名请求 ``` keytool -v -certreq -file client/src/test/resources/client.csr -keystore client/src/test/resources/identity.jks -alias client -keypass secret -storepass secret -keyalg rsa ``` 证书颁发机构需要这些 csr 文件才能对其进行签名。下一步将是对请求进行签名。 #### 使用证书签名请求签署证书 ##### 签署客户端证书 ``` keytool -v -gencert -infile client/src/test/resources/client.csr -outfile client/src/test/resources/client-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -rfc ``` ##### 签署服务器证书 ``` keytool -v -gencert -infile server/src/main/resources/server.csr -outfile server/src/main/resources/server-signed.cer -keystore root-ca/identity.jks -storepass secret -alias root-ca -validity 3650 -ext KeyUsage=digitalSignature,dataEncipherment,keyEncipherment,keyAgreement -ext ExtendedKeyUsage=serverAuth,clientAuth -ext SubjectAlternativeName:c=DNS:localhost,DNS:raspberrypi,IP:127.0.0.1 -rfc ``` #### 用已签名的证书替换未签名的证书 服务器和客户端的 identity keystore 仍然保留着未签名的证书。现在你可以用已签名的证书替换它。keytool 有一个奇怪的限制/设计。它不允许你直接导入已签名的证书,如果你尝试这样做,它会报错。证书颁发机构的证书必须存在于 identity.jks 中。 **导出 CA 证书** ``` keytool -v -exportcert -file root-ca/root-ca.pem -alias root-ca -keystore root-ca/identity.jks -storepass secret -rfc ``` **客户端** ``` keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret -noprompt keytool -v -importcert -file client/src/test/resources/client-signed.cer -alias client -keystore client/src/test/resources/identity.jks -storepass secret keytool -v -delete -alias root-ca -keystore client/src/test/resources/identity.jks -storepass secret ``` **服务器** ``` keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore server/src/main/resources/identity.jks -storepass secret -noprompt keytool -v -importcert -file server/src/main/resources/server-signed.cer -alias server -keystore server/src/main/resources/identity.jks -storepass secret keytool -v -delete -alias root-ca -keystore server/src/main/resources/identity.jks -storepass secret ``` #### 仅信任证书颁发机构 现在你需要将客户端和服务器配置为仅信任证书颁发机构。你可以通过将证书颁发机构的证书导入客户端和服务器的 truststore 来实现。你可以使用以下两个命令来完成此操作: **客户端** ``` keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore client/src/test/resources/truststore.jks -storepass secret -noprompt ``` **服务器** ``` keytool -v -importcert -file root-ca/root-ca.pem -alias root-ca -keystore server/src/main/resources/truststore.jks -storepass secret -noprompt ``` truststore 中仍然包含客户端和服务器特定的证书,这些需要被移除。你可以使用以下命令来完成此操作: **客户端** ``` keytool -v -delete -alias server -keystore client/src/test/resources/truststore.jks -storepass secret ``` **服务器** ``` keytool -v -delete -alias client -keystore server/src/main/resources/truststore.jks -storepass secret ``` 如果你再次运行客户端,你将看到测试通过,并且客户端收到了来自服务器的 hello 消息,并且是基于由证书颁发机构签名的证书。 ## 用于启用 TLS 认证的自动化脚本 你还可以使用此项目 [脚本目录](script) 中提供的脚本,将上述所有步骤自动化。运行以下命令之一来执行脚本: * 单向认证:`./configure-one-way-authentication` * 双向认证:`./configure-two-way-authentication-by-trusting-each-other my-company-name` * 基于信任证书颁发机构的双向认证:`./configure-two-way-authentication-by-trusting-root-ca my-company-name` ## 已测试的客户端 以下是已测试客户端的列表,基于纯 Java 的 Http Client 配置可以在 [ClientConfig 类](client/src/main/java/nl/altindag/client/ClientConfig.java) 中找到。 基于 Kotlin 和 Scala 的 http 客户端配置作为嵌套类包含在内,请参阅此处获取完整列表:[服务目录](client/src/main/java/nl/altindag/client/service)。 [服务目录](client/src/main/java/nl/altindag/client/service) 包含各个 Http 客户端及其示例请求。 所有客户端示例都使用在 [SSLConfig 类](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/SSLConfig.java) 中创建的相同基础 ssl 配置。 **Java** * [Apache HttpClient](https://hc.apache.org/httpcomponents-client-4.5.x/index.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L68) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ApacheHttpClientService.java) * [Apache HttpAsyncClient](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/index.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L76) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ApacheHttpAsyncClientService.java) * [Apache 5 HttpClient](https://hc.apache.org/) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L86) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Apache5HttpClientService.java) * [Apache 5 HttpAsyncClient](https://hc.apache.org/) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L97) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Apache5HttpAsyncClientService.java) * [JDK HttpClient](https://openjdk.java.net/groups/net/httpclient/intro.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L111) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/JdkHttpClientService.java) * [旧版 JDK HttpClient](https://docs.oracle.com/javase/tutorial/networking/urls/readingWriting.html) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/OldJdkHttpClientService.java) * [Netty Reactor](https://github.com/reactor/reactor-netty) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L134) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ReactorNettyService.java) * [Jetty Reactive HttpClient](https://github.com/jetty-project/jetty-reactive-httpclient) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L142) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/JettyReactiveHttpClientService.java) * [Spring RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L119) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/SpringRestTemplateService.java) * [Spring WebFlux WebClient Netty](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L148) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/SpringWebClientService.java) * [Spring WebFlux WebClient Jetty](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L155) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/SpringWebClientService.java) * [OkHttp](https://github.com/square/okhttp) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L125) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/OkHttpClientService.java) * [Jersey Client](https://eclipse-ee4j.github.io/jersey/) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L162) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/JerseyClientService.java) * 旧版 Jersey Client -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L170) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/OldJerseyClientService.java) * [Apache CXF JAX-RS](https://cxf.apache.org/) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L182) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ApacheCXFJaxRsClientService.java) * [使用 ConduitConfigurer 的 Apache CXF](https://cxf.apache.org/) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L191) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ApacheCXFWebClientService.java) * [Google HttpClient](https://github.com/googleapis/google-http-java-client) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L206) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/GoogleHttpClientService.java) * [Unirest](https://github.com/Kong/unirest-java) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L214) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/UnirestService.java) * [Retrofit](https://github.com/square/retrofit) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L224) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/RetrofitService.java) * [Async Http Client](https://github.com/AsyncHttpClient/async-http-client) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L262) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/AsyncHttpClientService.java) * [Feign](https://github.com/OpenFeign/feign) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L272) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/FeignService.java) * [Methanol](https://github.com/mizosoft/methanol) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L308) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/MethanolService.java) * [Vertx Webclient](https://github.com/vert-x3/vertx-web) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L316) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/VertxWebClientService.java) * [gRPC](https://grpc.io/) -> [客户端/服务端配置与示例请求](https://github.com/Hakky54/java-tutorials) * [ElasticSearch](https://www.elastic.co/) -> [RestHighLevelClient 配置与示例请求](https://github.com/Hakky54/java-tutorials/blob/main/elasticsearch-with-ssl/src/main/java/nl/altindag/ssl/es/App.java) * [Jetty WebSocket](https://www.eclipse.org/jetty/) -> [客户端配置与示例请求](https://github.com/Hakky54/java-tutorials/blob/2bf5d975347d500bb9d0aa3b32cbf33b345425ee/websocket-client-with-ssl/src/main/java/nl/altindag/ssl/ws/App.java#L14) **Kotlin** * [Fuel](https://github.com/kittinunf/fuel) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/FuelService.kt) * [Http4k 配合 Apache 4](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kApache4HttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kClientService.kt) * [Http4k 配合 Async Apache 4](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kApache4AsyncHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kAsyncClientService.kt) * [Http4k 配合 Apache 5](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kApache5HttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kClientService.kt) * [Http4k 配合 Async Apache 5](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kApache5AsyncHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kAsyncClientService.kt) * [Http4k 配合 Java Net](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kJavaHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kClientService.kt) * [Http4k 配合 Jetty](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kJettyHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kClientService.kt) * [Http4k 配合 OkHttp](https://github.com/http4k/http4k) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kOkHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4kClientService.kt) * [Kohttp](https://github.com/rybalkinsd/kohttp) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KohttpService.kt) * [Ktor 配合 Android 引擎](https://github.com/ktorio/ktor) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorAndroidHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorHttpClientService.kt) * [Ktor 配合 Apache 引擎](https://github.com/ktorio/ktor) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorApacheHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorHttpClientService.kt) * [Ktor 配合 CIO (基于协程的 I/O) 引擎](https://github.com/ktorio/ktor) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorCIOHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorHttpClientService.kt) * [Ktor 配合 Java 引擎](https://github.com/ktorio/ktor) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorJavaHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorHttpClientService.kt) * [Ktor 配合 Okhttp 引擎](https://github.com/ktorio/ktor) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorOkHttpClientService.kt) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/KtorHttpClientService.kt) **Scala** * [Twitter Finagle](https://github.com/twitter/finagle) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/FinagleHttpClientService.scala) * [Twitter Finagle Featherbed](https://github.com/finagle/featherbed) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/d78e4e81b8b775d3ff09c11b0a7c1532a741199e/client/src/main/java/nl/altindag/client/service/FeatherbedRequestService.scala#L19) * [Akka Http Client](https://github.com/akka/akka-http) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/35cba2f3a2dcd73b01fa323b99eec7777f7429bb/client/src/main/java/nl/altindag/client/ClientConfig.java#L253) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/f32fc11ac1bf879fe5f9ba55a57a4e8188eb29e3/client/src/main/java/nl/altindag/client/service/AkkaHttpClientService.java#L34) * [Dispatch Reboot](https://github.com/dispatch/reboot) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/DispatchRebootService.scala) * [ScalaJ / Simplified Http Client](https://github.com/scalaj/scalaj-http) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ScalaJHttpClientService.scala) * [Sttp](https://github.com/softwaremill/sttp) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/SttpHttpClientService.scala) * [Requests-Scala](https://github.com/lihaoyi/requests-scala) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/RequestsScalaService.scala) * [Http4s Blaze Client](https://github.com/http4s/http4s) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4sBlazeClientService.scala) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4sService.scala) * [Http4s Java Net Client](https://github.com/http4s/http4s) -> [客户端配置](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4sJavaNetClientService.scala) | [示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/Http4sService.scala) **Groovy** * Groovy 配合 Java Net -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/GroovyJavaNetClientService.groovy) * [Hyper Poet](https://github.com/tambapps/hyperpoet) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/HyperPoetService.groovy) **Clojure** * [clj-http](https://github.com/dakrone/clj-http) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/CljHttpClientService.clj) * JDK Http Client -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ClojureJdkHttpClientService.clj) * 旧版 JDK Http Client -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/ClojureHttpClientService.clj) * [Hato](https://github.com/gnarroway/hato) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/HatoHttpClientService.clj) * [Aleph](https://github.com/clj-commons/aleph) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/AlephHttpClientService.clj) * [Http-Kit](https://github.com/http-kit/http-kit) -> [客户端配置与示例请求](https://github.com/Hakky54/mutual-tls-ssl/blob/master/client/src/main/java/nl/altindag/client/service/HttpKitService.clj) **其他** 本教程不提供下列客户端的示例,但已证明它们可以与本项目提供的服务器配合使用。为了指引你正确的方向,你可以将创建的 Java keystore 文件转换为 pem 文件,然后将其与下面列出的其中一个客户端一起使用。以下 Gist 页面包含了将 Java keystore 转换为 pem 文件的命令:[Gist - Curl 配合 Java KeyStore 使用](https://gist.github.com/Hakky54/049299f0874fd4b870257c6458e0dcbd) * [curl](https://curl.se/) -> [客户端配置与示例请求](https://gist.github.com/Hakky54/049299f0874fd4b870257c6458e0dcbd) * [Postman](https://www.postman.com/) ## 演示和演练视频 [![包含服务器和客户端应用程序的 Java SSL TLS 演示](https://github.com/Hakky54/mutual-tls-ssl/blob/master/images/demo.png?raw=true "Java SSL TLS Demo with a server and a client application")](https://youtu.be/yfOknrCBNbQ) ## 贡献 有很多方法可以为本项目做出贡献: * 给个星标 * 通过 [GitHub](https://github.com/sponsors/Hakky54)、[Ko-fi](https://ko-fi.com/hakky54) 或 [open collective](https://opencollective.com/hakky54) 进行捐赠 * 通过 [![发推文](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=If%20you%20are%20interested%20in%20securing%20your%20web%20application,%20you%20might%20want%20to%20read%20the%20tutorial%20Mastering%20two-way%20tls&=https://github.com/Hakky54/mutual-tls&via=hakky541&hashtags=encryption,security,https,ssl,tls,certificate,developer,java,scala,kotlin,sslcontextkickstart) 分享 * 加入 [Gitter 聊天室](https://gitter.im/hakky54/mutual-tls-ssl) 并留下反馈或帮助解答用户的问题 * 提交 PR
标签:Apache HttpClient, API安全, ElasticSearch, Feign, Google HttpClient, Grizzly, gRPC, HTTPS, JDK HttpClient, Jersey, Jetty, JSON输出, JS文件枚举, Kotlin, mTLS, Netty, OkHttp, Python工具, RestTemplate, Retrofit, Scala, Spring Boot, Spring WebFlux, SSL/TLS, Unirest, WebClient, WebSocket, 依赖分析, 加密通信, 单向认证, 双向认证, 域名枚举, 客户端, 教程, 服务端, 案例, 网络安全, 证书配置, 隐私保护