001/*
002Copyright 2015 Hendrik Saly
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008    http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015 */
016
017package de.saly.es.example.tssl.util;
018
019import java.io.File;
020import java.io.UnsupportedEncodingException;
021import java.net.URL;
022import java.net.URLDecoder;
023import java.security.NoSuchAlgorithmException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027
028import javax.crypto.Cipher;
029import javax.net.ssl.SSLContext;
030import javax.net.ssl.SSLEngine;
031
032import org.elasticsearch.common.logging.ESLogger;
033import org.elasticsearch.common.logging.Loggers;
034
035public class SecurityUtil {
036
037    private static final ESLogger log = Loggers.getLogger(SecurityUtil.class);
038    private static final String[] PREFERRED_SSL_CIPHERS = { "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA",
039            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
040            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
041            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
042            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256" };
043    private static final String[] PREFERRED_SSL_PROTOCOLS = { "TLSv1", "TLSv1.1", "TLSv1.2" };
044
045    public static String[] ENABLED_SSL_PROTOCOLS = null;
046    public static String[] ENABLED_SSL_CIPHERS = null;
047    public static boolean UNLIMITED_STRENGTH_SUPPORTED;
048
049    private SecurityUtil() {
050
051    }
052
053    static {
054        try {
055            final int aesMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
056
057            if (aesMaxKeyLength < 256) {
058                log.warn("AES 256 not supported, max key length for AES is " + aesMaxKeyLength
059                        + ". To enable AES 256 install 'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files'");
060            } else {
061                UNLIMITED_STRENGTH_SUPPORTED = true;
062            }
063        } catch (final NoSuchAlgorithmException e) {
064            log.error("AES encryption not supported. " + e);
065
066        }
067
068        try {
069
070            final SSLContext serverContext = SSLContext.getInstance("TLS");
071            serverContext.init(null, null, null);
072            final SSLEngine engine = serverContext.createSSLEngine();
073            final List<String> supportedCipherSuites = new ArrayList<String>(Arrays.asList(engine.getSupportedCipherSuites()));
074            final List<String> supportedProtocols = new ArrayList<String>(Arrays.asList(engine.getSupportedProtocols()));
075
076            final List<String> preferredCipherSuites = Arrays.asList(PREFERRED_SSL_CIPHERS);
077            final List<String> preferredProtocols = Arrays.asList(PREFERRED_SSL_PROTOCOLS);
078
079            supportedCipherSuites.retainAll(preferredCipherSuites);
080            supportedProtocols.retainAll(preferredProtocols);
081
082            if (supportedCipherSuites.isEmpty()) {
083                log.error("No usable SSL/TLS cipher suites found");
084            } else {
085                ENABLED_SSL_CIPHERS = supportedCipherSuites.toArray(new String[supportedCipherSuites.size()]);
086            }
087
088            if (supportedProtocols.isEmpty()) {
089                log.error("No usable SSL/TLS protocols found");
090            } else {
091                ENABLED_SSL_PROTOCOLS = supportedProtocols.toArray(new String[supportedProtocols.size()]);
092            }
093
094            log.debug("Usable SSL/TLS protocols: {}", supportedProtocols);
095            log.debug("Usable SSL/TLS cipher suites: {}", supportedCipherSuites);
096
097        } catch (final Exception e) {
098            log.error("Error while evaluating supported crypto", e);
099        }
100    }
101
102    public static File getAbsoluteFilePathFromClassPath(final String fileNameFromClasspath) {
103
104        File jaasConfigFile = null;
105        final URL jaasConfigURL = SecurityUtil.class.getClassLoader().getResource(fileNameFromClasspath);
106        if (jaasConfigURL != null) {
107            try {
108                jaasConfigFile = new File(URLDecoder.decode(jaasConfigURL.getFile(), "UTF-8"));
109            } catch (final UnsupportedEncodingException e) {
110                return null;
111            }
112
113            if (jaasConfigFile.exists() && jaasConfigFile.canRead()) {
114                return jaasConfigFile;
115            } else {
116                log.error("Cannot read from {}, maybe the file does not exists? ", jaasConfigFile.getAbsolutePath());
117            }
118
119        } else {
120            log.error("Failed to load " + fileNameFromClasspath);
121        }
122
123        return null;
124
125    }
126
127    public static boolean setSystemPropertyToAbsoluteFilePathFromClassPath(final String property, final String fileNameFromClasspath) {
128        if (System.getProperty(property) == null) {
129            File jaasConfigFile = null;
130            final URL jaasConfigURL = SecurityUtil.class.getClassLoader().getResource(fileNameFromClasspath);
131            if (jaasConfigURL != null) {
132                try {
133                    jaasConfigFile = new File(URLDecoder.decode(jaasConfigURL.getFile(), "UTF-8"));
134                } catch (final UnsupportedEncodingException e) {
135                    return false;
136                }
137
138                if (jaasConfigFile.exists() && jaasConfigFile.canRead()) {
139                    System.setProperty(property, jaasConfigFile.getAbsolutePath());
140
141                    log.debug("Load " + fileNameFromClasspath + " from {} ", jaasConfigFile.getAbsolutePath());
142                    return true;
143                } else {
144                    log.error("Cannot read from {}, maybe the file does not exists? ", jaasConfigFile.getAbsolutePath());
145                }
146
147            } else {
148                log.error("Failed to load " + fileNameFromClasspath);
149            }
150        } else {
151            log.warn("Property " + property + " already set to " + System.getProperty(property));
152        }
153
154        return false;
155    }
156
157    public static boolean setSystemPropertyToAbsoluteFile(final String property, final String fileName) {
158        if (System.getProperty(property) == null) {
159
160            if (fileName == null) {
161                log.error("Cannot set property " + property + " because filename is null");
162
163                return false;
164            }
165
166            final File jaasConfigFile = new File(fileName).getAbsoluteFile();
167
168            if (jaasConfigFile.exists() && jaasConfigFile.canRead()) {
169                System.setProperty(property, jaasConfigFile.getAbsolutePath());
170
171                log.debug("Load " + fileName + " from {} ", jaasConfigFile.getAbsolutePath());
172                return true;
173            } else {
174                log.error("Cannot read from {}, maybe the file does not exists? ", jaasConfigFile.getAbsolutePath());
175            }
176
177        } else {
178            log.warn("Property " + property + " already set to " + System.getProperty(property));
179        }
180
181        return false;
182    }
183}