diff --git a/core-java-modules/core-java-security-4/pom.xml b/core-java-modules/core-java-security-4/pom.xml index a4700c34ba..5bbf505079 100644 --- a/core-java-modules/core-java-security-4/pom.xml +++ b/core-java-modules/core-java-security-4/pom.xml @@ -31,4 +31,30 @@ 1.2.6 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + --add-opens java.base/sun.security.x509=ALL-UNNAMED + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + 17 + + --add-exports + java.base/sun.security.x509=ALL-UNNAMED + + + + + + \ No newline at end of file diff --git a/core-java-modules/core-java-security-4/src/main/java/com/baeldung/multiple_truststores/SslContextConfigurer.java b/core-java-modules/core-java-security-4/src/main/java/com/baeldung/multiple_truststores/SslContextConfigurer.java new file mode 100644 index 0000000000..c7de3a157d --- /dev/null +++ b/core-java-modules/core-java-security-4/src/main/java/com/baeldung/multiple_truststores/SslContextConfigurer.java @@ -0,0 +1,87 @@ +package com.baeldung.multiple_truststores; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +public class SslContextConfigurer { + + public static X509TrustManager addAdditionalTrustStore(String trustStoreLocation, String trustStorePassword) + throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, KeyManagementException { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + + X509TrustManager defaultX509CertificateTrustManager = null; + for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { + if (trustManager instanceof X509TrustManager) { + defaultX509CertificateTrustManager = (X509TrustManager) trustManager; + break; + } + } + + try (InputStream myKeys = SslContextConfigurer.class.getClassLoader().getResourceAsStream(trustStoreLocation)) { + KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + myTrustStore.load(myKeys, trustStorePassword.toCharArray()); + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(myTrustStore); + + X509TrustManager myTrustManager = null; + for (TrustManager tm : trustManagerFactory.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + myTrustManager = (X509TrustManager) tm; + break; + } + } + + final X509TrustManager finalDefaultTm = defaultX509CertificateTrustManager; + final X509TrustManager finalMyTm = myTrustManager; + + X509TrustManager wrapper = new X509TrustManager() { + + private X509Certificate[] mergeCertificates() { + ArrayList resultingCerts = new ArrayList<>(); + resultingCerts.addAll(Arrays.asList(finalDefaultTm.getAcceptedIssuers())); + resultingCerts.addAll(Arrays.asList(finalMyTm.getAcceptedIssuers())); + return resultingCerts.toArray(new X509Certificate[resultingCerts.size()]); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return mergeCertificates(); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + finalMyTm.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + finalDefaultTm.checkServerTrusted(chain, authType); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + finalDefaultTm.checkClientTrusted(mergeCertificates(), authType); + } + }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { wrapper }, null); + SSLContext.setDefault(sslContext); + + return wrapper; + } + } +} diff --git a/core-java-modules/core-java-security-4/src/main/resources/cert_to_check.pem b/core-java-modules/core-java-security-4/src/main/resources/cert_to_check.pem new file mode 100644 index 0000000000..f4803316d6 --- /dev/null +++ b/core-java-modules/core-java-security-4/src/main/resources/cert_to_check.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYTCCBEmgAwIBAgISBF19/wT1rCVfNkni8qzXVDQHMA0GCSqGSIb3DQEBCwUA +MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD +EwJSMzAeFw0yMzAyMDcxNjI1MzFaFw0yMzA1MDgxNjI1MzBaMBkxFzAVBgNVBAMT +DmFsZW5rYS5jYXBpdGFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +42xLbY0Xby6wz97gMeojVQF5EorW5GOuF0tvokXaBm7R5evhat/nRjYfO4MvFC/9 +McP6/+AfrIy8z/xsFe8JZ1WlNdkZj5Y60HAvVct53DPMdQlZlUxWDn+LnlXw7YcV +Vh2I9Lfucn88zmEupUfpfgCzAQISKhic6aBLHsxsz+j7jM1toHnf1Bj/rSnjBO+J +B1yFqkE/Js+NBJy2SwhQDsimtkz/hezAesEO0Yfl5WpBqIgEkoA/7esuNvlmcelT +lsFynvD72dX9zP+qIptX/59F/MzKKyskeAIQI3O+gM99ZWe0NUgBOvgDZBqxTsbU +X7vOUtdCm/nn1lmTZPe/lwIDAQABo4ICiDCCAoQwDgYDVR0PAQH/BAQDAgWgMB0G +A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud +DgQWBBQtALBDAFYOq3K8RuQC19+X2GpjZzAfBgNVHSMEGDAWgBQULrMXt1hWy65Q +CUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9y +My5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3Jn +LzBXBgNVHREEUDBOgg5hbGVua2EuY2FwaXRhbIIRYWxlbmthY2FwaXRhbC5jb22C +End3dy5hbGVua2EuY2FwaXRhbIIVd3d3LmFsZW5rYWNhcGl0YWwuY29tMEwGA1Ud +IARFMEMwCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0 +dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIIBBQYKKwYBBAHWeQIEAgSB9gSB8wDx +AHYAtz77JN+cTbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkAAAGGLOlI/QAABAMA +RzBFAiEAiEUAyyMSZ2Q/b06aISXfkWImzhs6AO87tFaC0+w59xoCIE6VbmOlnyiF +YH/sNNUkxbqIZ9lrindoBhW5HOZBI4i0AHcAejKMVNi3LbYg6jjgUh7phBZwMhOF +TTvSK8E6V6NS61IAAAGGLOlJEAAABAMASDBGAiEA34TXhp1cv/gKDpNk3yd+gLl6 +NcnoRiu/YJpYnRfAWJECIQDGTXDa6l2fZ3kAg1Ijeue0rBl7efIwpBUk0EmdC+Eq +aDANBgkqhkiG9w0BAQsFAAOCAQEABCopAP4By3YdmqgO2gcQJaLp0Fs67JGc9X6c +CPwbb5dIBn915cYKcupvfffr47/kA9o3kCqJNmhY3qgqgtaBwkJS3057ULmKG4Z2 +aNnJLP8wM84+dVKBiUQKxxSQQQGXldVkzeBj47bCJt60wkz/vgychQrS/oPQlqgN +KKuZ7+jdMOrFnRONuaFxovYIwra6dGekzEvCqP1TWDJ4BVsMFc2RL5Qy48/oEsTQ +x8ErPfxhQI1T9ChtphuxnA9pVtkhqOWB6R+4qeIIvEPoOmGTzzX39o90mIA1m5M2 +iKyX0O/CxtvvVxRmta8OQ6p4x558GxF7g9f3SjzrCNHkJ2iwpg== +-----END CERTIFICATE----- diff --git a/core-java-modules/core-java-security-4/src/main/resources/new_trustStore.p12 b/core-java-modules/core-java-security-4/src/main/resources/new_trustStore.p12 new file mode 100644 index 0000000000..bdcff2a4bf Binary files /dev/null and b/core-java-modules/core-java-security-4/src/main/resources/new_trustStore.p12 differ diff --git a/core-java-modules/core-java-security-4/src/test/java/com/baeldung/multiple_truststores/SslContextConfigurerUnitTest.java b/core-java-modules/core-java-security-4/src/test/java/com/baeldung/multiple_truststores/SslContextConfigurerUnitTest.java new file mode 100644 index 0000000000..d3559e1ade --- /dev/null +++ b/core-java-modules/core-java-security-4/src/test/java/com/baeldung/multiple_truststores/SslContextConfigurerUnitTest.java @@ -0,0 +1,32 @@ +package com.baeldung.multiple_truststores; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +import org.junit.jupiter.api.Test; + +import com.baeldung.multiple_truststores.SslContextConfigurer; +import sun.security.x509.X509CertImpl; + +public class SslContextConfigurerUnitTest { + + @Test + public void givenCustomTrustManager_whenCheckingCertificateValidity_thenTrustManagerApprovesCert() + throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + X509TrustManager trustManager = SslContextConfigurer.addAdditionalTrustStore( + "new_trustStore.p12", + "change" + ); + + InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("cert_to_check.pem"); + X509CertImpl x509Cert = new X509CertImpl(resourceAsStream); + trustManager.checkClientTrusted(new X509Certificate[]{x509Cert}, "server"); + } +} \ No newline at end of file