0b47733c042b9410517c59385798575fa83f397b
[nonrtric.git] / information-coordinator-service / src / main / java / org / oransc / ics / clients / AsyncRestClientFactory.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2020 Nordix Foundation
6  * %%
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20
21 package org.oransc.ics.clients;
22
23 import io.netty.handler.ssl.SslContext;
24 import io.netty.handler.ssl.SslContextBuilder;
25 import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
26
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.lang.invoke.MethodHandles;
31 import java.security.KeyStore;
32 import java.security.KeyStoreException;
33 import java.security.NoSuchAlgorithmException;
34 import java.security.UnrecoverableKeyException;
35 import java.security.cert.Certificate;
36 import java.security.cert.CertificateException;
37 import java.security.cert.X509Certificate;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.stream.Collectors;
41 import javax.net.ssl.KeyManagerFactory;
42
43 import org.oransc.ics.configuration.WebClientConfig;
44 import org.oransc.ics.configuration.WebClientConfig.HttpProxyConfig;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.util.ResourceUtils;
48
49 /**
50  * Factory for a generic reactive REST client.
51  */
52 public class AsyncRestClientFactory {
53     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
54
55     private final SslContextFactory sslContextFactory;
56     private final HttpProxyConfig httpProxyConfig;
57
58     public AsyncRestClientFactory(WebClientConfig clientConfig) {
59         if (clientConfig != null) {
60             this.sslContextFactory = new CachingSslContextFactory(clientConfig);
61             this.httpProxyConfig = clientConfig.httpProxyConfig();
62         } else {
63             logger.warn("No configuration for web client defined, HTTPS will not work");
64             this.sslContextFactory = null;
65             this.httpProxyConfig = null;
66         }
67     }
68
69     public AsyncRestClient createRestClientNoHttpProxy(String baseUrl) {
70         return createRestClient(baseUrl, false);
71     }
72
73     public AsyncRestClient createRestClientUseHttpProxy(String baseUrl) {
74         return createRestClient(baseUrl, true);
75     }
76
77     private AsyncRestClient createRestClient(String baseUrl, boolean useHttpProxy) {
78         if (this.sslContextFactory != null) {
79             try {
80                 return new AsyncRestClient(baseUrl, this.sslContextFactory.createSslContext(),
81                     useHttpProxy ? httpProxyConfig : null);
82             } catch (Exception e) {
83                 String exceptionString = e.toString();
84                 logger.error("Could not init SSL context, reason: {}", exceptionString);
85             }
86         }
87         return new AsyncRestClient(baseUrl, null, httpProxyConfig);
88     }
89
90     private class SslContextFactory {
91         private final WebClientConfig clientConfig;
92
93         public SslContextFactory(WebClientConfig clientConfig) {
94             this.clientConfig = clientConfig;
95         }
96
97         public SslContext createSslContext() throws UnrecoverableKeyException, NoSuchAlgorithmException,
98             CertificateException, KeyStoreException, IOException {
99             return this.createSslContext(createKeyManager());
100         }
101
102         private SslContext createSslContext(KeyManagerFactory keyManager)
103             throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
104             if (this.clientConfig.isTrustStoreUsed()) {
105                 return createSslContextRejectingUntrustedPeers(this.clientConfig.trustStore(),
106                     this.clientConfig.trustStorePassword(), keyManager);
107             } else {
108                 // Trust anyone
109                 return SslContextBuilder.forClient() //
110                     .keyManager(keyManager) //
111                     .trustManager(InsecureTrustManagerFactory.INSTANCE) //
112                     .build();
113             }
114         }
115
116         private SslContext createSslContextRejectingUntrustedPeers(String trustStorePath, String trustStorePass,
117             KeyManagerFactory keyManager)
118             throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
119
120             final KeyStore trustStore = getTrustStore(trustStorePath, trustStorePass);
121             List<Certificate> certificateList = Collections.list(trustStore.aliases()).stream() //
122                 .filter(alias -> isCertificateEntry(trustStore, alias)) //
123                 .map(alias -> getCertificate(trustStore, alias)) //
124                 .collect(Collectors.toList());
125             final X509Certificate[] certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
126
127             return SslContextBuilder.forClient() //
128                 .keyManager(keyManager) //
129                 .trustManager(certificates) //
130                 .build();
131         }
132
133         private boolean isCertificateEntry(KeyStore trustStore, String alias) {
134             try {
135                 return trustStore.isCertificateEntry(alias);
136             } catch (KeyStoreException e) {
137                 logger.error("Error reading truststore {}", e.getMessage());
138                 return false;
139             }
140         }
141
142         private Certificate getCertificate(KeyStore trustStore, String alias) {
143             try {
144                 return trustStore.getCertificate(alias);
145             } catch (KeyStoreException e) {
146                 logger.error("Error reading truststore {}", e.getMessage());
147                 return null;
148             }
149         }
150
151         private KeyManagerFactory createKeyManager() throws NoSuchAlgorithmException, CertificateException, IOException,
152             UnrecoverableKeyException, KeyStoreException {
153             final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
154             final KeyStore keyStore = KeyStore.getInstance(this.clientConfig.keyStoreType());
155             final String keyStoreFile = this.clientConfig.keyStore();
156             final String keyStorePassword = this.clientConfig.keyStorePassword();
157             final String keyPassword = this.clientConfig.keyPassword();
158             try (final InputStream inputStream = new FileInputStream(keyStoreFile)) {
159                 keyStore.load(inputStream, keyStorePassword.toCharArray());
160             }
161             keyManager.init(keyStore, keyPassword.toCharArray());
162             return keyManager;
163         }
164
165         private synchronized KeyStore getTrustStore(String trustStorePath, String trustStorePass)
166             throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
167
168             KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
169             store.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
170             return store;
171         }
172     }
173
174     public class CachingSslContextFactory extends SslContextFactory {
175         private SslContext cachedContext = null;
176
177         public CachingSslContextFactory(WebClientConfig clientConfig) {
178             super(clientConfig);
179         }
180
181         @Override
182         public SslContext createSslContext() throws UnrecoverableKeyException, NoSuchAlgorithmException,
183             CertificateException, KeyStoreException, IOException {
184             if (this.cachedContext == null) {
185                 this.cachedContext = super.createSslContext();
186             }
187             return this.cachedContext;
188
189         }
190     }
191 }