假设我们有一个使用SSL和mTLS保护的NGINX实例。如果使用 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 -nocryptbr通过在此示例中使用本地NGINX服务,我们需要禁用主机名验证。
final Properties props = System.getProperties();brprops.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());br在其他客户端中,这可能需要设置接受所有连接的 HostVerifier。
HostnameVerifier allHostsValid = new HostnameVerifier() {br public boolean verify(String hostname, SSLSession session) {br return true;br }br};下一步是将客户端密钥加载到java代码中,并创建一个KeyManagerFactory。
String privateKeyPath = "/path/to/generated/client.key.pkcs8";brString publicKeyPath = "/path/to/generated/client.crt";br brfinal byte[] publicData = Files.readAllBytes(Path.of(publicKeyPath));brfinal byte[] privateData = Files.readAllBytes(Path.of(privateKeyPath));br brString privateString = new String(privateData, Charset.defaultCharset())br .replace("-----BEGIN PRIVATE KEY-----", "")br .replaceAll(System.lineSeparator(), "")br .replace("-----END PRIVATE KEY-----", "");br brbyte[] encoded = Base64.getDecoder().decode(privateString);br brfinal CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");brfinal Collection<? extends Certificate> chain = certificateFactory.generateCertificates(br new ByteArrayInputStream(publicData));br brKey key = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encoded));br brKeyStore clientKeyStore = KeyStore.getInstance("jks");brfinal char[] pwdChars = "test".toCharArray();brclientKeyStore.load(null, null);brclientKeyStore.setKeyEntry("test", key, pwdChars, chain.toArray(new Certificate[0]));br brKeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");brkeyManagerFactory.init(clientKeyStore, pwdChars);在上面的片段中:
现在我们已经创建了一个KeyManagerFactory,我们可以使用它来创建SSLContext。
由于使用自签名证书,我们需要使用将接受它们的 TrustManager。在此示例中,信任管理器将接受从服务器提供的所有证书。
TrustManager[] acceptAllTrustManager = {br new X509TrustManager() {br public X509Certificate[] getAcceptedIssuers() {br return new X509Certificate[0];br }br br public void checkClientTrusted(br X509Certificate[] certs, String authType) {br }br br public void checkServerTrusted(br X509Certificate[] certs, String authType) {br }br }br };然后是 SSL 上下文初始化。
SSLContext sslContext = SSLContext.getInstance("TLS");brsslContext.init(keyManagerFactory.getKeyManagers(), acceptAllTrustManager, new java.security.SecureRandom());br让我们使用一个客户端,看看它的行为。
HttpClient client = HttpClient.newBuilder()br .sslContext(sslContext)br .build();br br br br HttpRequest exactRequest = HttpRequest.newBuilder()br .uri(URI.create("https://127.0.0.1"))br .GET()br .build();br br var exactResponse = client.sendAsync(exactRequest, HttpResponse.BodyHandlers.ofString())br .join();br System.out.println(exactResponse.statusCode());我们将收到一个404代码(该NGINX安装的默认值),这意味着我们的请求已成功获得MTLS握手。
现在让我们尝试使用另一个客户端,即老式的同步HttpsURLConnection。注意:我使用之前创建的allHostsValid。
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL("https://127.0.0.1").openConnection();brhttpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());brhttpsURLConnection.setHostnameVerifier(allHostsValid);br brInputStream inputStream = httpsURLConnection.getInputStream();brString result = new String(inputStream.readAllBytes(), Charset.defaultCharset());这将引发 404 错误,这意味着握手已成功进行。
因此,无论您使用的是异步HTTP客户端还是异步HTTP客户端,只要您配置了正确的SSLContext,您都应该能够进行握手。
| 留言与评论(共有 0 条评论) “” |