Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.baeldung.mtls.calls;

import javax.net.ssl.HostnameVerifier;

public class HostNameVerifierBuilder {

static HostnameVerifier allHostsValid = (hostname, session) -> true;

public static HostnameVerifier getAllHostsValid() {
return allHostsValid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.baeldung.mtls.calls;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Collection;
import java.util.Properties;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SslContextBuilder {

public static SSLContext buildSslContext()
throws IOException, CertificateException, InvalidKeySpecException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException,
KeyManagementException {
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

String privateKeyPath = "src/main/resources/keys/client.key.pkcs8";
String publicKeyPath = "src/main/resources/keys/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);

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) {
}
} };
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), acceptAllTrustManager, new java.security.SecureRandom());
return sslContext;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA8qLnlv
dXIuaG9zdG5hbWUwHhcNMjUwNzA3MTY0NTMzWhcNMjYwNzA3MTY0NTMzWjAcMRow
GAYDVQQDDBEqLmNsaWVudC5ob3N0bmFtZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAKavVV7T7MEWY2pVUSWzIGfaVqSEBgKmdUJWnNGHwZrBX/XjJ9LN
srBAOjT/mJ4ccoMTKY8agDmF7z0nz8fQSr5D4JQ6C1yBbjKL04BwLSrNIRPIzWrb
F4ztADOrrh1l3YaRYbwMWkFZjcoRX9zXYooMbZPrgBSskQ8hdnrIMtc04+FvFhyP
5hEtqvR9I8qGjxGx/wXAYA539Owh9T3Xl0vVroxtv2eFNYIIg7BV1yHrX1RalEbx
5mzfeM7o/IJRvj/73jVhdvu2csUM4J20NxSx1B9XoFZI8Y0JPOR4bo3j7zZXE0iH
ib6/pWYxdZknWDsm7qHTLZJNEFPNk/W2/0UCAwEAAaNCMEAwHQYDVR0OBBYEFOkk
ZcxKbJpkiG0Mr5ce/6ykH9rGMB8GA1UdIwQYMBaAFARhDN6rdEw0ylzmwgVRXUbO
BNmJMA0GCSqGSIb3DQEBCwUAA4ICAQAGPhAPinkHWHfSiQRChtxEAnTPVavsuC6X
UyGGpWHz7OD475SbzYnuaTN+O/2HUoP3qyVWH8igSOLBY1vpUXthkSHBltH21Gog
NFW4Z4/8NBlvM25BiBA/hGANFu5MvWuB9gNfHryWSZHFf0fyOd7ITIY2pDUHkqlc
e5pAkjGAlvATGeF8PcMzYDAF6DamtJVZtqha/ssAGPlDggbr55LqtKos9TphYGsN
LOnWv+f81TB8euLUTJpFg4i+t5QGmQ1UWv2N1U4TEo5fpRb+y6E/vorUH4qpDKOn
31mvjxkgW05Jf21GKQU5LtYIfR3ZVa7UlWkdr9x763pzNUB0q8ioPQ2jQ3bzrJEO
El3dhiWCUAXGxljKWeuUwkdws3D4mOru6hVwE7vE31ZD3mnO52uOtwd6sKeGg7zj
OgTu06/KSbYEVsZ1yic8CWVSR2Sn+4HtXo7cEuBCnWJIkqRNGoFTbKULaSWLN+Lh
wzTIcBA6E5SoHXY0T80EsVQAq2LV7bymDklHeBWUMr47guUUyBsoZg36njA7geT5
T8dIeyClWHZNwqa8kxbQt6WAY21qqUyovsn0js26Ni8sr3iv+akXZkeJGopgYV4g
BNMow0BNLsKLRhDM0gkIqlOwHMRIYwsdNkrSk4mnZoxlGIotVb4JCAazxss5rJR2
IVboXKO91g==
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmr1Ve0+zBFmNq
VVElsyBn2lakhAYCpnVCVpzRh8GawV/14yfSzbKwQDo0/5ieHHKDEymPGoA5he89
J8/H0Eq+Q+CUOgtcgW4yi9OAcC0qzSETyM1q2xeM7QAzq64dZd2GkWG8DFpBWY3K
EV/c12KKDG2T64AUrJEPIXZ6yDLXNOPhbxYcj+YRLar0fSPKho8Rsf8FwGAOd/Ts
IfU915dL1a6Mbb9nhTWCCIOwVdch619UWpRG8eZs33jO6PyCUb4/+941YXb7tnLF
DOCdtDcUsdQfV6BWSPGNCTzkeG6N4+82VxNIh4m+v6VmMXWZJ1g7Ju6h0y2STRBT
zZP1tv9FAgMBAAECggEAC5oBjaeff3MA+Wo1yzN0CnZyeGHuDyop2DOyF41k5tIV
zUYBxBToHodh5cVyiHK/b6saRekYyqgtViratfQj96k+zOJbXxVtJ5x+3J4yLpv3
dOqRjaHxOjBWxsHozQgFirO8wzty3sCOc2WRMAxXwfcKe3S1Rfsa35w7JGGh1EOv
ygOACa+9iLsT8iAVGtmaFybp3wNFS+MYibe/v7qhM1MLktGJH8tZIzYr87iLP7uF
6WZve1/QCvNwyvKsdSYIvNzaVYJTuWacTVKaANmEci5TYtQzFVQScX9PdrdNtQdu
2pxtbI0Y8oT04KXQ0Bsnejc5ckE/pzgIzB17lF+O0QKBgQDhIZ48YxaRWN1JEsii
zvzcEz3hMKBzZ//oFB0/tb4AFuIrMaeoVZf0jH571KWO9BV+ExxCBIROr7twdIxk
OfwCGN4034+hJlBxrBSf8lN3jYHV6t1xBniz1PkoUjUI+RzjoPY9T0hsYUv76vcZ
2uqgCCXlu2Ssj+MPRkeH2laXyQKBgQC9iizU1NiHgwxL7TVf9Wiz7wxC/UWe9/32
EZyFS83GIJffLXowQA997qWa/NtbcP+Dpdm6vbYbW6FBRE7EH6zQgLWZtollmuaR
cmCXzSmB84P5wz8fF0o8HZnjzMiM4Dm8pUXlNj/05QUGBT+4YG5pKKqR5RLj3rXE
i4eUaDMhnQKBgQClZ2OwjkSIaTe7dld+doEE2AZAqs9XuvMjeZO7uTVtL2LfxU2e
ubQ48fgD1soEa4RW6od6YYMrpKUcDCURhiCHEepAAniuN04nFfzZPtrgHVFk73fe
kJih1zlvzGY2v3/gJeSESvm01w9SeOEvV83F4famALYIqnZyRHpNb7brMQKBgQCp
bBp4wC0wrEZQlB9SwBWwSOyH8MbLu1bKHqHvUHwGLtoyRv9io9B1O93R9VXKne33
6kb+MlfWiohQw9M4YiviUDqDxPN53AVfW4LWDjCdFWQR3KHOk83qgHcvdbyKmF9j
rcQVh/GRYSmlYQm9MI1g+FXHhaDmCQwnPKWbVazmzQKBgHV3r3ahlszePYHQmQLr
4eJM7Kj3Y0SydM3402TLH8DG4CeuOkO+/ZhHAE3AgAzQptOqbZ25/RS+7O6N+Wa1
Lo6kbrSgoqQgqzyHrp3PcWeJ1n/mef0QxbV/fKWWfdzFRtA2oTwXteW3Dzmu7A84
65QBcsuKKf34GJfvwl8eQT/O
-----END PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.baeldung.mtls.calls;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;

import javax.net.ssl.SSLContext;

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class MutualTLSCallWithHttpClientLiveTest {

@Test
public void whenWeExecuteMutualTLSCallToNginxServerWithHttpClient_thenItShouldReturnStatusOK()
throws UnrecoverableKeyException, CertificateException, IOException, InvalidKeySpecException, NoSuchAlgorithmException, KeyStoreException,
KeyManagementException {
SSLContext sslContext = SslContextBuilder.buildSslContext();
HttpClient client = HttpClient.newBuilder()
.sslContext(sslContext)
.build();

HttpRequest exactRequest = HttpRequest.newBuilder()
.uri(URI.create("https://localhost/ping"))
.GET()
.build();

HttpResponse<String> response = client.sendAsync(exactRequest, HttpResponse.BodyHandlers.ofString())
.join();
Assertions.assertThat(response)
.isNotNull();
Assertions.assertThat(response.statusCode())
.isEqualTo(200);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.baeldung.mtls.calls;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class MutualTLSCallWithHttpURLConnectionLiveTest {

@Test
public void whenWeExecuteMutualTLSCallToNginxServerWithHttpURLConnection_thenItShouldReturnNonNullResponse()
throws UnrecoverableKeyException, CertificateException, IOException, InvalidKeySpecException, NoSuchAlgorithmException, KeyStoreException,
KeyManagementException {
SSLContext sslContext = SslContextBuilder.buildSslContext();
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL("https://127.0.0.1/ping").openConnection();
httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpsURLConnection.setHostnameVerifier(HostNameVerifierBuilder.getAllHostsValid());
InputStream inputStream = httpsURLConnection.getInputStream();
String response = new String(inputStream.readAllBytes(), Charset.defaultCharset());
Assertions.assertThat(response)
.isNotNull();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.baeldung.mtls.calls;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;

import javax.net.ssl.SSLContext;

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class SslContextBuilderUnitTest {

@Test
public void whenPrivateAndPublicKeysAreGiven_thenAnSSLContextShouldBeCreated()
throws UnrecoverableKeyException, CertificateException, IOException, InvalidKeySpecException, NoSuchAlgorithmException, KeyStoreException,
KeyManagementException {
SSLContext sslContext = SslContextBuilder.buildSslContext();
Assertions.assertThat(sslContext)
.isNotNull();
}
}