Remove Tabs, per Jococo
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / config / SecurityInfo.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
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
22 package org.onap.aaf.cadi.config;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.net.InetAddress;
28 import java.net.UnknownHostException;
29 import java.rmi.AccessException;
30 import java.security.KeyManagementException;
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.CertificateException;
36 import java.security.cert.X509Certificate;
37 import java.util.ArrayList;
38
39 import javax.net.ssl.HostnameVerifier;
40 import javax.net.ssl.HttpsURLConnection;
41 import javax.net.ssl.KeyManager;
42 import javax.net.ssl.KeyManagerFactory;
43 import javax.net.ssl.SSLContext;
44 import javax.net.ssl.SSLSession;
45 import javax.net.ssl.SSLSocketFactory;
46 import javax.net.ssl.TrustManager;
47 import javax.net.ssl.TrustManagerFactory;
48 import javax.net.ssl.X509KeyManager;
49 import javax.net.ssl.X509TrustManager;
50
51 import org.onap.aaf.cadi.Access;
52 import org.onap.aaf.cadi.CadiException;
53 import org.onap.aaf.cadi.Access.Level;
54 import org.onap.aaf.cadi.util.MaskFormatException;
55 import org.onap.aaf.cadi.util.NetMask;
56 import org.onap.aaf.cadi.util.Split;
57
58 public class SecurityInfo {
59     private static final String SECURITY_ALGO = "RSA";
60     private static final String HTTPS_PROTOCOLS = "https.protocols";
61     private static final String JDK_TLS_CLIENT_PROTOCOLS = "jdk.tls.client.protocols";
62     private static final String INITIALIZING_ERR_FMT = "Error initializing %s: %s";
63     private static final String LOADED_FROM_CADI_PROPERTIES = "%s loaded from CADI Properties";
64     private static final String LOADED_FROM_SYSTEM_PROPERTIES = "%s loaded from System Properties";
65
66     public static final String SSL_KEY_MANAGER_FACTORY_ALGORITHM;
67     
68     private SSLSocketFactory socketFactory;
69     private X509KeyManager[] x509KeyManager;
70     private X509TrustManager[] x509TrustManager;
71     public final String defaultAlias;
72     public final String defaultClientAlias;
73     private NetMask[] trustMasks;
74     private SSLContext context;
75     private HostnameVerifier maskHV;
76     public final Access access;
77
78     // Change Key Algorithms for IBM's VM.  Could put in others, if needed.
79     static {
80         if ("IBM Corporation".equalsIgnoreCase(System.getProperty("java.vm.vendor"))) {
81             SSL_KEY_MANAGER_FACTORY_ALGORITHM = "IbmX509";
82         } else {
83             SSL_KEY_MANAGER_FACTORY_ALGORITHM = "SunX509";
84         }
85     }
86     
87
88     public SecurityInfo(final Access access) throws CadiException {
89         String msgHelp = "";
90         try {
91             this.access = access;
92             // reuse DME2 Properties for convenience if specific Properties don't exist
93             
94             String str = access.getProperty(Config.CADI_ALIAS, null);
95             if(str==null || str.isEmpty()) {
96                 defaultAlias = null;
97             } else {
98                 defaultAlias = str;
99             }
100             
101             str = access.getProperty(Config.CADI_CLIENT_ALIAS, null);
102             if(str==null) {
103                 defaultClientAlias = defaultAlias;
104             } else if(str.isEmpty()) {
105                 // intentionally off, i.e. cadi_client_alias=
106                 defaultClientAlias = null;
107             } else {
108                 defaultClientAlias = str;
109             }
110
111             msgHelp = String.format(INITIALIZING_ERR_FMT,"Keystore", access.getProperty(Config.CADI_KEYSTORE, ""));
112             initializeKeyManager();
113             
114             msgHelp = String.format(INITIALIZING_ERR_FMT,"Truststore", access.getProperty(Config.CADI_TRUSTSTORE, ""));
115             initializeTrustManager();
116             
117
118             msgHelp = String.format(INITIALIZING_ERR_FMT,"Trustmasks", access.getProperty(Config.CADI_TRUST_MASKS, ""));
119             initializeTrustMasks();
120
121             msgHelp = String.format(INITIALIZING_ERR_FMT,"HTTP Protocols", "access properties");
122             setHTTPProtocols(access);
123             
124             msgHelp = String.format(INITIALIZING_ERR_FMT,"Context", "TLS");
125             context = SSLContext.getInstance("TLS");
126             context.init(x509KeyManager, x509TrustManager, null);
127             SSLContext.setDefault(context);
128             socketFactory = context.getSocketFactory();
129         } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | UnrecoverableKeyException | IOException e) {
130             throw new CadiException(msgHelp,e);
131         }
132     }
133
134     public static void setHTTPProtocols(Access access) {
135         String httpsProtocols = System.getProperty(Config.HTTPS_PROTOCOLS);
136         if(httpsProtocols!=null) {
137             access.printf(Level.INIT, LOADED_FROM_SYSTEM_PROPERTIES, HTTPS_PROTOCOLS);
138         } else {
139             httpsProtocols = access.getProperty(Config.HTTPS_PROTOCOLS,null);
140             if(httpsProtocols!=null) {
141                 access.printf(Level.INIT, LOADED_FROM_CADI_PROPERTIES, HTTPS_PROTOCOLS);
142             } else {
143                 httpsProtocols = access.getProperty(HTTPS_PROTOCOLS, Config.HTTPS_PROTOCOLS_DEFAULT);
144                 access.printf(Level.INIT, "%s set by %s in CADI Properties",Config.HTTPS_PROTOCOLS,Config.CADI_PROTOCOLS);
145             }
146             // This needs to be set when people do  not.
147             System.setProperty(HTTPS_PROTOCOLS, httpsProtocols);
148         }
149         String httpsClientProtocols = System.getProperty(JDK_TLS_CLIENT_PROTOCOLS,null); 
150         if(httpsClientProtocols!=null) {
151             access.printf(Level.INIT, LOADED_FROM_SYSTEM_PROPERTIES, JDK_TLS_CLIENT_PROTOCOLS);
152         } else {
153             httpsClientProtocols = access.getProperty(Config.HTTPS_CLIENT_PROTOCOLS, null);
154             if(httpsClientProtocols!=null) {
155                 access.printf(Level.INIT, LOADED_FROM_CADI_PROPERTIES, Config.HTTPS_CLIENT_PROTOCOLS);
156             } else {
157                 httpsClientProtocols = Config.HTTPS_PROTOCOLS_DEFAULT;
158                 access.printf(Level.INIT, "%s set from %s",Config.HTTPS_CLIENT_PROTOCOLS, "Default Protocols");
159             }
160             System.setProperty(JDK_TLS_CLIENT_PROTOCOLS, httpsClientProtocols);
161         }
162     }
163
164     /**
165      * @return the scf
166      */
167     public SSLSocketFactory getSSLSocketFactory() {
168         return socketFactory;
169     }
170
171     public SSLContext getSSLContext() {
172         return context;
173     }
174
175     /**
176      * @return the km
177      */
178     public X509KeyManager[] getKeyManagers() {
179         return x509KeyManager;
180     }
181
182     public void checkClientTrusted(X509Certificate[] certarr) throws CertificateException {
183         for (X509TrustManager xtm : x509TrustManager) {
184             xtm.checkClientTrusted(certarr, SECURITY_ALGO);
185         }
186     }
187
188     public void checkServerTrusted(X509Certificate[] certarr) throws CertificateException {
189         for (X509TrustManager xtm : x509TrustManager) {
190             xtm.checkServerTrusted(certarr, SECURITY_ALGO);
191         }
192     }
193
194     public void setSocketFactoryOn(HttpsURLConnection hsuc) {
195         hsuc.setSSLSocketFactory(socketFactory);
196         if (maskHV != null && !maskHV.equals(hsuc.getHostnameVerifier())) {
197             hsuc.setHostnameVerifier(maskHV);
198         }
199     }
200     
201     protected void initializeKeyManager() throws CadiException, IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException {
202         String keyStore = access.getProperty(Config.CADI_KEYSTORE, null);
203         if(keyStore==null) {
204             return;
205         } else if (!new File(keyStore).exists()) {
206             throw new CadiException(keyStore + " does not exist");
207         }
208
209         String keyStorePasswd = access.getProperty(Config.CADI_KEYSTORE_PASSWORD, null);
210         keyStorePasswd = (keyStorePasswd == null) ? null : access.decrypt(keyStorePasswd, false);
211         if (keyStore == null || keyStorePasswd == null) { 
212             x509KeyManager = new X509KeyManager[0];
213             return;
214         }
215
216         String keyPasswd = access.getProperty(Config.CADI_KEY_PASSWORD, null);
217         keyPasswd = (keyPasswd == null) ? keyStorePasswd : access.decrypt(keyPasswd, false);
218
219         KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SSL_KEY_MANAGER_FACTORY_ALGORITHM);
220
221         ArrayList<X509KeyManager> keyManagers = new ArrayList<>();
222         File file;
223         for (String ksname : Split.splitTrim(',', keyStore)) {
224             String keystoreFormat;
225             if (ksname.endsWith(".p12") || ksname.endsWith(".pkcs12")) {
226                 keystoreFormat = "PKCS12";
227             } else {
228                 keystoreFormat = "JKS";
229             }
230
231             file = new File(ksname);
232             if (file.exists()) {
233                 FileInputStream fis = new FileInputStream(file);
234                 try {
235                     KeyStore ks = KeyStore.getInstance(keystoreFormat);
236                     ks.load(fis, keyStorePasswd.toCharArray());
237                     keyManagerFactory.init(ks, keyPasswd.toCharArray());
238                 } finally {
239                     fis.close();
240                 }
241             }
242         }
243         
244         StringBuilder sb = null;
245         for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
246             if (keyManager instanceof X509KeyManager) {
247                 X509KeyManager xkm = (X509KeyManager)keyManager;
248                 keyManagers.add(xkm);
249                 if(defaultAlias!=null) {
250                     sb=new StringBuilder("X509 Chain\n");
251                     x509Info(sb,xkm.getCertificateChain(defaultAlias));
252                 }
253                 if(defaultClientAlias!=null && !defaultClientAlias.equals(defaultAlias)) {
254                     if(sb==null) {
255                         sb = new StringBuilder();
256                     } else {
257                         sb.append('\n');
258                     }
259                     sb.append("X509 Client Chain\n");
260                     x509Info(sb,xkm.getCertificateChain(defaultAlias));
261                 }
262             }
263         }
264         x509KeyManager = new X509KeyManager[keyManagers.size()];
265         keyManagers.toArray(x509KeyManager);
266         
267         if(sb!=null) {
268             access.log(Level.INIT, sb);
269         }
270     }
271     
272     private void x509Info(StringBuilder sb, X509Certificate[] chain) {
273         if(chain!=null) {
274             int i=0;
275             for(X509Certificate x : chain) {
276                 sb.append("  ");
277                 sb.append(i++);
278                 sb.append(')');
279                 sb.append("\n    Subject: ");
280                 sb.append(x.getSubjectDN());
281                 sb.append("\n    Issuer : ");
282                 sb.append(x.getIssuerDN());
283                 sb.append("\n    Expires: ");
284                 sb.append(x.getNotAfter());
285                 sb.append('\n');
286             }
287         }
288     }
289
290     protected void initializeTrustManager() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, CadiException {
291         String trustStore = access.getProperty(Config.CADI_TRUSTSTORE, null);
292         if(trustStore==null) {
293             return; 
294         } else if(!new File(trustStore).exists()) {
295             throw new CadiException(trustStore + " does not exist");
296         }
297
298         String trustStorePasswd = access.getProperty(Config.CADI_TRUSTSTORE_PASSWORD, null);
299         trustStorePasswd = (trustStorePasswd == null ) ? "changeit"/*defacto Java Trust Pass*/ : access.decrypt(trustStorePasswd, false);
300
301         TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SSL_KEY_MANAGER_FACTORY_ALGORITHM);
302         File file;
303         for (String trustStoreName : Split.splitTrim(',',trustStore)) {
304             file = new File(trustStoreName);
305             if (file.exists()) {
306                 FileInputStream fis = new FileInputStream(file);
307                 try {
308                     KeyStore ts = KeyStore.getInstance("JKS");
309                     ts.load(fis, trustStorePasswd.toCharArray());
310                     trustManagerFactory.init(ts); 
311                 } finally {
312                     fis.close();
313                 }
314             }
315         }
316
317         TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
318         if (trustManagers == null || trustManagers.length == 0) {
319             return;
320         }
321
322         x509TrustManager = new X509TrustManager[trustManagers.length];
323         for (int i = 0; i < trustManagers.length; ++i) {
324             try {
325                 x509TrustManager[i] = (X509TrustManager)trustManagers[i];
326             } catch (ClassCastException e) {
327                 access.log(Level.WARN, "Non X509 TrustManager", x509TrustManager[i].getClass().getName(), "skipped in SecurityInfo");
328             }
329         }
330     }
331     
332     protected void initializeTrustMasks() throws AccessException {
333         String tips = access.getProperty(Config.CADI_TRUST_MASKS, null);
334         if (tips == null) {
335             return;
336         }
337
338         access.log(Level.INIT, "Explicitly accepting valid X509s from", tips);
339         String[] ipsplit = Split.splitTrim(',', tips);
340         trustMasks = new NetMask[ipsplit.length];
341         for (int i = 0; i < ipsplit.length; ++i) {
342             try {
343                 trustMasks[i] = new NetMask(ipsplit[i]);
344             } catch (MaskFormatException e) {
345                 throw new AccessException("Invalid IP Mask in " + Config.CADI_TRUST_MASKS, e);
346             }
347         }
348     
349         final HostnameVerifier origHV = HttpsURLConnection.getDefaultHostnameVerifier();
350         maskHV = new HostnameVerifier() {
351             @Override
352             public boolean verify(final String urlHostName, final SSLSession session) {
353                 try {
354                     // This will pick up /etc/host entries as well as DNS
355                     InetAddress ia = InetAddress.getByName(session.getPeerHost());
356                     for (NetMask tmask : trustMasks) {
357                         if (tmask.isInNet(ia.getHostAddress())) {
358                             return true;
359                         }
360                     }
361                 } catch (UnknownHostException e) {
362                     // It's ok. do normal Verify
363                 }
364                 return origHV.verify(urlHostName, session);
365             };
366         };
367         HttpsURLConnection.setDefaultHostnameVerifier(maskHV);
368     }
369     
370 }