001/*- 002 * #%L 003 * HAPI FHIR - Client Framework 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.client.tls; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.tls.BaseStoreInfo; 024import ca.uhn.fhir.tls.KeyStoreInfo; 025import ca.uhn.fhir.tls.PathType; 026import ca.uhn.fhir.tls.TlsAuthentication; 027import ca.uhn.fhir.tls.TrustStoreInfo; 028import jakarta.annotation.Nonnull; 029import org.apache.commons.lang3.Validate; 030import org.apache.http.conn.ssl.DefaultHostnameVerifier; 031import org.apache.http.conn.ssl.NoopHostnameVerifier; 032import org.apache.http.conn.ssl.TrustSelfSignedStrategy; 033import org.apache.http.ssl.PrivateKeyStrategy; 034import org.apache.http.ssl.SSLContextBuilder; 035import org.apache.http.ssl.SSLContexts; 036 037import java.io.FileInputStream; 038import java.io.InputStream; 039import java.security.KeyStore; 040import java.util.Optional; 041import javax.net.ssl.HostnameVerifier; 042import javax.net.ssl.SSLContext; 043import javax.net.ssl.TrustManager; 044import javax.net.ssl.TrustManagerFactory; 045import javax.net.ssl.X509TrustManager; 046 047import static org.apache.commons.lang3.StringUtils.isNotBlank; 048 049public class TlsAuthenticationSvc { 050 051 private TlsAuthenticationSvc() {} 052 053 public static SSLContext createSslContext(@Nonnull TlsAuthentication theTlsAuthentication) { 054 Validate.notNull(theTlsAuthentication, "theTlsAuthentication cannot be null"); 055 056 try { 057 SSLContextBuilder contextBuilder = SSLContexts.custom(); 058 059 if (theTlsAuthentication.getKeyStoreInfo().isPresent()) { 060 KeyStoreInfo keyStoreInfo = 061 theTlsAuthentication.getKeyStoreInfo().get(); 062 PrivateKeyStrategy privateKeyStrategy = null; 063 if (isNotBlank(keyStoreInfo.getAlias())) { 064 privateKeyStrategy = (aliases, socket) -> keyStoreInfo.getAlias(); 065 } 066 KeyStore keyStore = createKeyStore(keyStoreInfo); 067 contextBuilder.loadKeyMaterial(keyStore, keyStoreInfo.getKeyPass(), privateKeyStrategy); 068 } 069 070 if (theTlsAuthentication.getTrustStoreInfo().isPresent()) { 071 TrustStoreInfo trustStoreInfo = 072 theTlsAuthentication.getTrustStoreInfo().get(); 073 KeyStore trustStore = createKeyStore(trustStoreInfo); 074 contextBuilder.loadTrustMaterial(trustStore, TrustSelfSignedStrategy.INSTANCE); 075 } 076 077 return contextBuilder.build(); 078 } catch (Exception e) { 079 throw new TlsAuthenticationException(Msg.code(2102) + "Failed to create SSLContext", e); 080 } 081 } 082 083 public static KeyStore createKeyStore(BaseStoreInfo theStoreInfo) { 084 try { 085 KeyStore keyStore = KeyStore.getInstance(theStoreInfo.getType().toString()); 086 087 if (PathType.RESOURCE.equals(theStoreInfo.getPathType())) { 088 try (InputStream inputStream = 089 TlsAuthenticationSvc.class.getResourceAsStream(theStoreInfo.getFilePath())) { 090 validateKeyStoreExists(inputStream); 091 keyStore.load(inputStream, theStoreInfo.getStorePass()); 092 } 093 } else if (PathType.FILE.equals(theStoreInfo.getPathType())) { 094 try (InputStream inputStream = new FileInputStream(theStoreInfo.getFilePath())) { 095 validateKeyStoreExists(inputStream); 096 keyStore.load(inputStream, theStoreInfo.getStorePass()); 097 } 098 } 099 return keyStore; 100 } catch (Exception e) { 101 throw new TlsAuthenticationException(Msg.code(2103) + "Failed to create KeyStore", e); 102 } 103 } 104 105 public static void validateKeyStoreExists(InputStream theInputStream) { 106 if (theInputStream == null) { 107 throw new TlsAuthenticationException(Msg.code(2116) + "Keystore does not exists"); 108 } 109 } 110 111 public static X509TrustManager createTrustManager(Optional<TrustStoreInfo> theTrustStoreInfo) { 112 try { 113 TrustManagerFactory trustManagerFactory = 114 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 115 if (!theTrustStoreInfo.isPresent()) { 116 trustManagerFactory.init((KeyStore) null); // Load Trust Manager Factory with default Java truststore 117 } else { 118 TrustStoreInfo trustStoreInfo = theTrustStoreInfo.get(); 119 KeyStore trustStore = createKeyStore(trustStoreInfo); 120 trustManagerFactory.init(trustStore); 121 } 122 for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { 123 if (trustManager instanceof X509TrustManager) { 124 return (X509TrustManager) trustManager; 125 } 126 } 127 throw new TlsAuthenticationException(Msg.code(2104) + "Could not find X509TrustManager"); 128 } catch (Exception e) { 129 throw new TlsAuthenticationException(Msg.code(2105) + "Failed to create X509TrustManager"); 130 } 131 } 132 133 public static HostnameVerifier createHostnameVerifier(Optional<TrustStoreInfo> theTrustStoreInfo) { 134 return theTrustStoreInfo.isPresent() ? new DefaultHostnameVerifier() : new NoopHostnameVerifier(); 135 } 136 137 public static class TlsAuthenticationException extends RuntimeException { 138 private static final long serialVersionUID = 1l; 139 140 public TlsAuthenticationException(String theMessage, Throwable theCause) { 141 super(theMessage, theCause); 142 } 143 144 public TlsAuthenticationException(String theMessage) { 145 super(theMessage); 146 } 147 } 148}