/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.configuration.jsse; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.SystemPropertyAction; import org.apache.cxf.configuration.security.FiltersType; /** * Holder for utility methods related to manipulating SSL settings, common * to the connection and listener factories (previously duplicated). */ public final class SSLUtils { static final String PKCS12_TYPE = "PKCS12"; private static final String DEFAULT_KEYSTORE_TYPE = "PKCS12"; private static final String DEFAULT_TRUST_STORE_TYPE = "JKS"; private static final String DEFAULT_SECURE_SOCKET_PROTOCOL = "TLSv1"; private static final String HTTPS_CIPHER_SUITES = "https.cipherSuites"; private static final List<String> DEFAULT_CIPHERSUITE_FILTERS_INCLUDE = Arrays.asList(new String[] {".*"}); /** * By default, exclude NULL, anon, EXPORT, DES ciphersuites */ private static final List<String> DEFAULT_CIPHERSUITE_FILTERS_EXCLUDE = Arrays.asList(new String[] {".*_NULL_.*", ".*_anon_.*", ".*_EXPORT_.*", ".*_DES_.*"}); private static volatile KeyManager[] defaultManagers; private SSLUtils() { } public static KeyManager[] getDefaultKeyStoreManagers(Logger log) { if (defaultManagers == null) { loadDefaultKeyManagers(log); } if (defaultManagers.length == 0) { return null; } return defaultManagers; } private static synchronized void loadDefaultKeyManagers(Logger log) { if (defaultManagers != null) { return; } String location = getKeystore(null, log); String keyStorePassword = getKeystorePassword(null, log); String keyPassword = getKeyPassword(null, log); InputStream is = null; try { File file = new File(location); if (file.exists()) { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); is = Files.newInputStream(file.toPath()); ks.load(is, (keyStorePassword != null) ? keyStorePassword.toCharArray() : null); kmf.init(ks, (keyPassword != null) ? keyPassword.toCharArray() : null); defaultManagers = kmf.getKeyManagers(); } else { log.log(Level.FINER, "No default keystore {0}", location); defaultManagers = new KeyManager[0]; } } catch (Exception e) { log.log(Level.WARNING, "Default key managers cannot be initialized: " + e.getMessage(), e); defaultManagers = new KeyManager[0]; } finally { if (is != null) { try { is.close(); } catch (IOException e) { log.warning("Keystore stream cannot be closed: " + e.getMessage()); } } } } public static KeyManager[] loadKeyStore(KeyManagerFactory kmf, KeyStore ks, ByteArrayInputStream bin, String keyStoreLocation, String keyStorePassword, Logger log) { KeyManager[] keystoreManagers = null; try { ks.load(bin, keyStorePassword.toCharArray()); kmf.init(ks, keyStorePassword.toCharArray()); keystoreManagers = kmf.getKeyManagers(); LogUtils.log(log, Level.FINE, "LOADED_KEYSTORE", keyStoreLocation); } catch (Exception e) { LogUtils.log(log, Level.WARNING, "FAILED_TO_LOAD_KEYSTORE", new Object[]{keyStoreLocation, e.getMessage()}); } return keystoreManagers; } protected static byte[] loadFile(String fileName) throws IOException { if (fileName == null) { return null; } Path path = FileSystems.getDefault().getPath(fileName); return Files.readAllBytes(path); } public static String getKeystore(String keyStoreLocation, Logger log) { String logMsg = null; if (keyStoreLocation != null) { logMsg = "KEY_STORE_SET"; } else { keyStoreLocation = SystemPropertyAction.getProperty("javax.net.ssl.keyStore"); if (keyStoreLocation != null) { logMsg = "KEY_STORE_SYSTEM_PROPERTY_SET"; } else { keyStoreLocation = SystemPropertyAction.getProperty("user.home") + "/.keystore"; logMsg = "KEY_STORE_NOT_SET"; } } LogUtils.log(log, Level.FINE, logMsg, keyStoreLocation); return keyStoreLocation; } public static String getKeystoreType(String keyStoreType, Logger log) { return getKeystoreType(keyStoreType, log, DEFAULT_KEYSTORE_TYPE); } public static String getKeystoreType(String keyStoreType, Logger log, String def) { String logMsg = null; if (keyStoreType != null) { logMsg = "KEY_STORE_TYPE_SET"; } else { keyStoreType = SystemPropertyAction.getProperty("javax.net.ssl.keyStoreType", null); if (keyStoreType == null) { keyStoreType = def; logMsg = "KEY_STORE_TYPE_NOT_SET"; } else { logMsg = "KEY_STORE_TYPE_SYSTEM_SET"; } } LogUtils.log(log, Level.FINE, logMsg, keyStoreType); return keyStoreType; } public static String getKeystoreProvider(String keyStoreProvider, Logger log) { String logMsg = null; if (keyStoreProvider != null) { logMsg = "KEY_STORE_PROVIDER_SET"; } else { keyStoreProvider = SystemPropertyAction.getProperty("javax.net.ssl.keyStoreProvider", null); if (keyStoreProvider == null) { logMsg = "KEY_STORE_PROVIDER_NOT_SET"; } else { logMsg = "KEY_STORE_PROVIDER_SYSTEM_SET"; } } LogUtils.log(log, Level.FINE, logMsg, keyStoreProvider); return keyStoreProvider; } public static String getKeystorePassword(String keyStorePassword, Logger log) { String logMsg = null; if (keyStorePassword != null) { logMsg = "KEY_STORE_PASSWORD_SET"; } else { keyStorePassword = SystemPropertyAction.getProperty("javax.net.ssl.keyStorePassword"); logMsg = keyStorePassword != null ? "KEY_STORE_PASSWORD_SYSTEM_PROPERTY_SET" : "KEY_STORE_PASSWORD_NOT_SET"; } LogUtils.log(log, Level.FINE, logMsg); return keyStorePassword; } public static String getKeyPassword(String keyPassword, Logger log) { String logMsg = null; if (keyPassword != null) { logMsg = "KEY_PASSWORD_SET"; } else { keyPassword = SystemPropertyAction.getProperty("javax.net.ssl.keyPassword"); if (keyPassword == null) { keyPassword = SystemPropertyAction.getProperty("javax.net.ssl.keyStorePassword"); } logMsg = keyPassword != null ? "KEY_PASSWORD_SYSTEM_PROPERTY_SET" : "KEY_PASSWORD_NOT_SET"; } LogUtils.log(log, Level.FINE, logMsg); return keyPassword; } public static String getKeystoreAlgorithm( String keyStoreMgrFactoryAlgorithm, Logger log) { String logMsg = null; if (keyStoreMgrFactoryAlgorithm != null) { logMsg = "KEY_STORE_ALGORITHM_SET"; } else { keyStoreMgrFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); logMsg = "KEY_STORE_ALGORITHM_NOT_SET"; } LogUtils.log(log, Level.FINE, logMsg, keyStoreMgrFactoryAlgorithm); return keyStoreMgrFactoryAlgorithm; } public static String getTrustStoreAlgorithm( String trustStoreMgrFactoryAlgorithm, Logger log) { String logMsg = null; if (trustStoreMgrFactoryAlgorithm != null) { logMsg = "TRUST_STORE_ALGORITHM_SET"; } else { trustStoreMgrFactoryAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); logMsg = "TRUST_STORE_ALGORITHM_NOT_SET"; } LogUtils.log(log, Level.FINE, logMsg, trustStoreMgrFactoryAlgorithm); return trustStoreMgrFactoryAlgorithm; } public static SSLContext getSSLContext(String protocol, KeyManager[] keyStoreManagers, TrustManager[] trustStoreManagers) throws NoSuchAlgorithmException, KeyManagementException { SSLContext ctx = SSLContext.getInstance(protocol); ctx.init(keyStoreManagers, trustStoreManagers, null); return ctx; } public static String[] getSupportedCipherSuites(SSLContext context) { return context.getSocketFactory().getSupportedCipherSuites(); } public static String[] getServerSupportedCipherSuites(SSLContext context) { return context.getServerSocketFactory().getSupportedCipherSuites(); } public static String[] getCiphersuitesToInclude(List<String> cipherSuitesList, FiltersType filters, String[] defaultCipherSuites, String[] supportedCipherSuites, Logger log) { // CipherSuites are returned in the following priority: // 1) If we have defined explicit "cipherSuite" configuration // 2) If we have defined ciphersuites via a system property. // 3) The default JVM CipherSuites, if no filters have been defined // 4) Filter the supported cipher suites (*not* the default JVM CipherSuites) if (!(cipherSuitesList == null || cipherSuitesList.isEmpty())) { return getCiphersFromList(cipherSuitesList, log, false); } String[] cipherSuites = getSystemCiphersuites(log); if (cipherSuites != null) { return cipherSuites; } // If we have no explicit cipherSuites (for the include case as above), and no filters, // then just use the defaults if ((defaultCipherSuites != null && defaultCipherSuites.length != 0) && (filters == null || !(filters.isSetInclude() || filters.isSetExclude()))) { LogUtils.log(log, Level.FINE, "CIPHERSUITES_SET", defaultCipherSuites.toString()); return defaultCipherSuites; } LogUtils.log(log, Level.FINE, "CIPHERSUITES_NOT_SET"); return getFilteredCiphersuites(filters, supportedCipherSuites, log, false); } public static String[] getFilteredCiphersuites(FiltersType filters, String[] supportedCipherSuites, Logger log, boolean exclude) { // We have explicit filters, so use the "include/exclude" cipherSuiteFilter configuration List<String> filteredCipherSuites = new ArrayList<>(); List<String> excludedCipherSuites = new ArrayList<>(); List<Pattern> includes = filters != null ? compileRegexPatterns(filters.getInclude(), true, log) : compileRegexPatterns(DEFAULT_CIPHERSUITE_FILTERS_INCLUDE, true, log); List<Pattern> excludes = filters != null ? compileRegexPatterns(filters.getExclude(), false, log) : compileRegexPatterns(DEFAULT_CIPHERSUITE_FILTERS_EXCLUDE, true, log); for (int i = 0; i < supportedCipherSuites.length; i++) { if (matchesOneOf(supportedCipherSuites[i], includes) && !matchesOneOf(supportedCipherSuites[i], excludes)) { LogUtils.log(log, Level.FINE, "CIPHERSUITE_INCLUDED", supportedCipherSuites[i]); filteredCipherSuites.add(supportedCipherSuites[i]); } else { LogUtils.log(log, Level.FINE, "CIPHERSUITE_EXCLUDED", supportedCipherSuites[i]); excludedCipherSuites.add(supportedCipherSuites[i]); } } LogUtils.log(log, Level.FINE, "CIPHERSUITES_FILTERED", filteredCipherSuites); LogUtils.log(log, Level.FINE, "CIPHERSUITES_EXCLUDED", excludedCipherSuites); if (exclude) { return getCiphersFromList(excludedCipherSuites, log, exclude); } else { return getCiphersFromList(filteredCipherSuites, log, exclude); } } private static String[] getSystemCiphersuites(Logger log) { String jvmCipherSuites = System.getProperty(HTTPS_CIPHER_SUITES); if ((jvmCipherSuites != null) && (!jvmCipherSuites.isEmpty())) { LogUtils.log(log, Level.FINE, "CIPHERSUITES_SYSTEM_PROPERTY_SET", jvmCipherSuites); return jvmCipherSuites.split(","); } else { return null; } } private static List<Pattern> compileRegexPatterns(List<String> regexes, boolean include, Logger log) { List<Pattern> patterns = new ArrayList<>(); if (regexes != null) { String msg = include ? "CIPHERSUITE_INCLUDE_FILTER" : "CIPHERSUITE_EXCLUDE_FILTER"; for (String s : regexes) { LogUtils.log(log, Level.FINE, msg, s); patterns.add(Pattern.compile(s)); } } return patterns; } private static boolean matchesOneOf(String s, List<Pattern> patterns) { boolean matches = false; if (patterns != null) { for (Pattern pattern : patterns) { Matcher matcher = pattern.matcher(s); if (matcher.matches()) { matches = true; break; } } } return matches; } private static String[] getCiphersFromList(List<String> cipherSuitesList, Logger log, boolean exclude) { int numCipherSuites = cipherSuitesList.size(); String[] cipherSuites = cipherSuitesList.toArray(new String[numCipherSuites]); if (log.isLoggable(Level.FINE)) { StringBuilder ciphsStr = new StringBuilder(); for (String s : cipherSuites) { if (ciphsStr.length() != 0) { ciphsStr.append(", "); } ciphsStr.append(s); } LogUtils.log(log, Level.FINE, exclude ? "CIPHERSUITES_EXCLUDED" : "CIPHERSUITES_SET", ciphsStr.toString()); } return cipherSuites; } public static String getTrustStoreType(String trustStoreType, Logger log) { return getTrustStoreType(trustStoreType, log, DEFAULT_TRUST_STORE_TYPE); } public static String getTrustStoreType(String trustStoreType, Logger log, String def) { String logMsg = null; if (trustStoreType != null) { logMsg = "TRUST_STORE_TYPE_SET"; } else { //Can default to JKS trustStoreType = SystemPropertyAction.getProperty("javax.net.ssl.trustStoreType"); if (trustStoreType == null) { trustStoreType = def; logMsg = "TRUST_STORE_TYPE_NOT_SET"; } else { logMsg = "TRUST_STORE_TYPE_SYSTEM_SET"; } } LogUtils.log(log, Level.FINE, logMsg, trustStoreType); return trustStoreType; } public static String getTruststorePassword(String trustStorePassword, Logger log) { String logMsg = null; if (trustStorePassword != null) { logMsg = "TRUST_STORE_PASSWORD_SET"; } else { trustStorePassword = SystemPropertyAction.getProperty("javax.net.ssl.trustStorePassword"); logMsg = trustStorePassword != null ? "TRUST_STORE_PASSWORD_SYSTEM_PROPERTY_SET" : "TRUST_STORE_PASSWORD_NOT_SET"; } LogUtils.log(log, Level.FINE, logMsg); return trustStorePassword; } public static String getTruststoreProvider(String trustStoreProvider, Logger log) { String logMsg = null; if (trustStoreProvider != null) { logMsg = "TRUST_STORE_PROVIDER_SET"; } else { trustStoreProvider = SystemPropertyAction.getProperty("javax.net.ssl.trustStoreProvider", null); if (trustStoreProvider == null) { logMsg = "TRUST_STORE_PROVIDER_NOT_SET"; } else { logMsg = "TRUST_STORE_PROVIDER_SYSTEM_SET"; } } LogUtils.log(log, Level.FINE, logMsg, trustStoreProvider); return trustStoreProvider; } public static String getSecureSocketProtocol(String secureSocketProtocol, Logger log) { if (secureSocketProtocol != null) { LogUtils.log(log, Level.FINE, "SECURE_SOCKET_PROTOCOL_SET", secureSocketProtocol); } else { LogUtils.log(log, Level.FINE, "SECURE_SOCKET_PROTOCOL_NOT_SET"); secureSocketProtocol = DEFAULT_SECURE_SOCKET_PROTOCOL; } return secureSocketProtocol; } }