使用Java执行mTLS调用

在本教程中,我们将学习如何通过使用不同的客户端使我们的Java应用程序能够使用mTLS。我们将使用将mTLS添加到Nginx实例的现有示例。

假设我们有一个Nginx实例,使用SSL还有。如果使用Java与使用mTLS保护的服务进行交互,则需要对代码库进行一些更改。在本教程中,我们将使Java应用程序能够使用不同客户端的mTLS。

要快速入门,我们可以使用现有的示例添加MTLS一个Nginx实例。我们的java mTLS配置将使用用于将mTLS添加到Nginx的证书和密钥。

为了为我们的Java客户端进行SSL配置,我们需要首先设置一个SSLContext。这简化了一些事情,因为SSLContext可以用于现有的各种http客户机。

因为我们有客户端的公钥和私钥,所以我们需要将私钥从PEM格式转换为der。

kcs8 -topk8 -inform PEM -outform PEM -in /path/to/generated/client.key -out /path/to/generated/client.key.pkcs8 -nocrypt
复制代码

通过在本例中使用本地Nginx服务,我们需要禁用主机名验证。

final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
复制代码

在其他客户端中,这可能需要设置接受所有连接的HostVerifier。

HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};
复制代码

下一步是将客户端键加载到java代码中,并创建一个KeyManagerFactory。

String privateKeyPath = "/path/to/generated/client.key.pkcs8";
String publicKeyPath = "/path/to/generated/client.crt";
 
final byte[] publicData = Files.readAllBytes(Path.of(publicKeyPath));
final byte[] privateData = Files.readAllBytes(Path.of(privateKeyPath));
 
String privateString = new String(privateData, Charset.defaultCharset())
        .replace("-----BEGIN PRIVATE KEY-----", "")
        .replaceAll(System.lineSeparator(), "")
        .replace("-----END PRIVATE KEY-----", "");
 
byte[] encoded = Base64.getDecoder().decode(privateString);
 
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
final Collection<? extends Certificate> chain = certificateFactory.generateCertificates(
        new ByteArrayInputStream(publicData));
 
Key key = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encoded));
 
KeyStore clientKeyStore = KeyStore.getInstance("jks");
final char[] pwdChars = "test".toCharArray();
clientKeyStore.load(null, null);
clientKeyStore.setKeyEntry("test", key, pwdChars, chain.toArray(new Certificate[0]));
 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(clientKeyStore, pwdChars);
复制代码

在上面的片段中:

  • 我们从文件中读取字节。
  • 我们从公钥创建了一个证书链。
  • 我们使用私钥创建了一个密钥实例。
  • 使用链和键创建了一个Keystore
  • 创建了一个KeyManagerFactory

现在我们已经创建了一个KeyManagerFactory,我们可以使用它来创建一个SSLContext。

由于使用自签名证书,我们需要使用TrustManager来接受它们.在本例中,信任管理器将接受来自服务器的所有证书。

TrustManager[] acceptAllTrustManager = {
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
 
                    public void checkClientTrusted(
                            X509Certificate[] certs, String authType) {
                    }
 
                    public void checkServerTrusted(
                            X509Certificate[] certs, String authType) {
                    }
                }
        };
复制代码

然后是SSL上下文初始化。

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), acceptAllTrustManager, new java.security.SecureRandom());

复制代码

让我们使用一个客户端,看看它的行为。

HttpClient client = HttpClient.newBuilder()
                                     .sslContext(sslContext)
                                     .build();
 
 
 
       HttpRequest exactRequest = HttpRequest.newBuilder()
                                     .uri(URI.create("https://127.0.0.1"))
                                     .GET()
                                     .build();
 
       var exactResponse = client.sendAsync(exactRequest, HttpResponse.BodyHandlers.ofString())
                                 .join();
       System.out.println(exactResponse.statusCode());
复制代码

我们将收到404代码(Nginx安装的默认代码),这意味着我们的请求获得了成功的mTLS握手。

现在,让我们尝试使用另一个客户端,即旧的同步HttpsURLConnection。注意:我使用前面创建的allHostsValid。

HttpsURLConnection httpsURLConnection = (HttpsURLConnection)   new URL("https://127.0.0.1").openConnection();
httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpsURLConnection.setHostnameVerifier(allHostsValid);
 
InputStream  inputStream = httpsURLConnection.getInputStream();
String result =  new String(inputStream.readAllBytes(), Charset.defaultCharset());
复制代码

这将引发404错误,这意味着握手成功。

因此,无论您有异步HTTP客户机还是异步客户机,只要配置了正确的SSLContext,您都应该能够进行握手。

小伙伴们如果觉得我写的不错,不妨帮个忙,给我点赞+收藏,可以让更多的人看到这篇文章! 完整资料已经给大家打包完毕,完整资料已经给大家打包完毕,需要的小伙伴可以点击获取学习资料


版权声明:本文为i6588662原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。