/*
* Copyright 2015 the original author or authors.
*
* Licensed 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.gradle.internal.resource.transport.http;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import org.apache.http.ssl.SSLInitializationException;
import org.gradle.internal.SystemProperties;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class DefaultSslContextFactory implements SslContextFactory {
private static final char[] EMPTY_PASSWORD = "".toCharArray();
private static final Set<String> SSL_SYSTEM_PROPERTIES = ImmutableSet.of(
"ssl.TrustManagerFactory.algorithm",
"javax.net.ssl.trustStoreType",
"javax.net.ssl.trustStore",
"javax.net.ssl.trustStoreProvider",
"javax.net.ssl.trustStorePassword",
"ssl.KeyManagerFactory.algorithm",
"javax.net.ssl.keyStoreType",
"javax.net.ssl.keyStore",
"javax.net.ssl.keyStoreProvider",
"javax.net.ssl.keyStorePassword"
);
private LoadingCache<Map<String, String>, SSLContext> cache = CacheBuilder.newBuilder().softValues().build(new SslContextCacheLoader());
@Override
public SSLContext createSslContext() {
return cache.getUnchecked(getCurrentProperties());
}
private Map<String, String> getCurrentProperties() {
Map<String, String> currentProperties = new TreeMap<String, String>();
for (String prop : SSL_SYSTEM_PROPERTIES) {
currentProperties.put(prop, System.getProperty(prop));
}
currentProperties.put("java.home", SystemProperties.getInstance().getJavaHomeDir().getPath());
return currentProperties;
}
private static class SslContextCacheLoader extends CacheLoader<Map<String, String>, SSLContext> {
@Override
public SSLContext load(Map<String, String> props) {
// This implementation is borrowed from the Apache HttpClient project
// https://github.com/apache/httpclient/blob/4.2.2/httpclient/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java#L246-L354
try {
TrustManagerFactory tmFactory;
String trustAlgorithm = props.get("ssl.TrustManagerFactory.algorithm");
if (trustAlgorithm == null) {
trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
}
String trustStoreType = props.get("javax.net.ssl.trustStoreType");
if (trustStoreType == null) {
trustStoreType = KeyStore.getDefaultType();
}
if ("none".equalsIgnoreCase(trustStoreType)) {
tmFactory = TrustManagerFactory.getInstance(trustAlgorithm);
} else {
File trustStoreFile;
String s = props.get("javax.net.ssl.trustStore");
if (s != null) {
trustStoreFile = new File(s);
tmFactory = TrustManagerFactory.getInstance(trustAlgorithm);
String trustStoreProvider = props.get("javax.net.ssl.trustStoreProvider");
KeyStore trustStore;
if (trustStoreProvider != null) {
trustStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
} else {
trustStore = KeyStore.getInstance(trustStoreType);
}
String trustStorePassword = props.get("javax.net.ssl.trustStorePassword");
FileInputStream instream = new FileInputStream(trustStoreFile);
try {
trustStore.load(instream, trustStorePassword != null ? trustStorePassword.toCharArray() : null);
} finally {
instream.close();
}
tmFactory.init(trustStore);
} else {
File javaHome = new File(props.get("java.home"));
File file = new File(javaHome, "lib/security/jssecacerts");
if (!file.exists()) {
file = new File(javaHome, "lib/security/cacerts");
trustStoreFile = file;
} else {
trustStoreFile = file;
}
tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
String trustStorePassword = props.get("javax.net.ssl.trustStorePassword");
FileInputStream instream = new FileInputStream(trustStoreFile);
try {
trustStore.load(instream, trustStorePassword != null ? trustStorePassword.toCharArray() : null);
} finally {
instream.close();
}
tmFactory.init(trustStore);
}
}
KeyManagerFactory kmFactory = null;
String keyAlgorithm = props.get("ssl.KeyManagerFactory.algorithm");
if (keyAlgorithm == null) {
keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
}
String keyStoreType = props.get("javax.net.ssl.keyStoreType");
if (keyStoreType == null) {
keyStoreType = KeyStore.getDefaultType();
}
if ("none".equalsIgnoreCase(keyStoreType)) {
kmFactory = KeyManagerFactory.getInstance(keyAlgorithm);
} else {
File keyStoreFile = null;
String s = props.get("javax.net.ssl.keyStore");
if (s != null) {
keyStoreFile = new File(s);
}
if (keyStoreFile != null) {
kmFactory = KeyManagerFactory.getInstance(keyAlgorithm);
String keyStoreProvider = props.get("javax.net.ssl.keyStoreProvider");
KeyStore keyStore;
if (keyStoreProvider != null) {
keyStore = KeyStore.getInstance(keyStoreType, keyStoreProvider);
} else {
keyStore = KeyStore.getInstance(keyStoreType);
}
String keyStorePassword = props.get("javax.net.ssl.keyStorePassword");
FileInputStream instream = new FileInputStream(keyStoreFile);
try {
keyStore.load(instream, keyStorePassword != null ? keyStorePassword.toCharArray() : EMPTY_PASSWORD);
} finally {
instream.close();
}
kmFactory.init(keyStore, keyStorePassword != null ? keyStorePassword.toCharArray() : EMPTY_PASSWORD);
}
}
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(
kmFactory != null ? kmFactory.getKeyManagers() : null,
tmFactory != null ? tmFactory.getTrustManagers() : null,
null);
return sslcontext;
} catch (GeneralSecurityException e) {
throw new SSLInitializationException(e.getMessage(), e);
} catch (IOException e) {
throw new SSLInitializationException(e.getMessage(), e);
}
}
}
}