DummySSLServerSocketFactory.java
/*
* Copyright (c) 2014 Wael Chatila / Icegreen Technologies. All Rights Reserved.
* This software is released under the Apache license 2.0
*/
package com.icegreen.greenmail.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ServerSocketFactory;
import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* DummySSLServerSocketFactory - NOT SECURE
* <p/>
* Contains a preconfigured key store for convenience in testing by avoiding
* having to manually set up, install, and generate keystore / keys.
* <p/>
* By default, the factory loads the resource <code>{@value #GREENMAIL_KEYSTORE_P12}</code> from classpath.
* A fallback to old <code>>{@value #GREENMAIL_KEYSTORE_JKS}</code> exists.
* <p/>
* The system property {@value #GREENMAIL_KEYSTORE_FILE_PROPERTY} can override the default keystore location.
* <p/>
* The system property {@value #GREENMAIL_KEYSTORE_PASSWORD_PROPERTY} can override the default keystore password.
* <p/>
* The system property {@value #GREENMAIL_KEY_PASSWORD_PROPERTY} can override the default key password
* (defaults to keystore password).
* <p/>
* GreenMail provides the keystore resource. For customization, place your greenmail.p12 before
* greenmail JAR in the classpath.
*
* @author Wael Chatila
* @since Feb 2006
*/
public class DummySSLServerSocketFactory extends SSLServerSocketFactory {
protected final Logger log = LoggerFactory.getLogger(DummySSLServerSocketFactory.class);
public static final String GREENMAIL_KEYSTORE_FILE_PROPERTY = "greenmail.tls.keystore.file";
public static final String GREENMAIL_KEYSTORE_PASSWORD_PROPERTY = "greenmail.tls.keystore.password";
public static final String GREENMAIL_KEY_PASSWORD_PROPERTY = "greenmail.tls.key.password";
public static final String GREENMAIL_KEYSTORE_P12 = "greenmail.p12";
public static final String GREENMAIL_KEYSTORE_JKS = "greenmail.jks";
private final SSLServerSocketFactory factory;
private final KeyStore ks;
// From https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SupportedCipherSuites
static final String[] ANONYMOUS_CIPHERS = {
"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
"SSL_DH_anon_WITH_DES_CBC_SHA",
"SSL_DH_anon_WITH_RC4_128_MD5",
"TLS_DH_anon_WITH_AES_128_CBC_SHA",
"TLS_DH_anon_WITH_AES_128_CBC_SHA256",
"TLS_DH_anon_WITH_AES_256_CBC_SHA",
"TLS_DH_anon_WITH_AES_256_CBC_SHA256",
"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
"TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
"TLS_ECDH_anon_WITH_NULL_SHA",
"TLS_ECDH_anon_WITH_RC4_128_SHA"};
public DummySSLServerSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
String defaultAlg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory km = KeyManagerFactory.getInstance(defaultAlg);
ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] pass = System.getProperty(GREENMAIL_KEYSTORE_PASSWORD_PROPERTY, "changeit").toCharArray();
loadKeyStore(pass);
String keyPassStr = System.getProperty(GREENMAIL_KEY_PASSWORD_PROPERTY);
char[] keyPass = keyPassStr != null ? keyPassStr.toCharArray() : pass;
km.init(ks, keyPass);
KeyManager[] kma = km.getKeyManagers();
sslcontext.init(kma, new TrustManager[]{new DummyTrustManager()}, null);
factory = sslcontext.getServerSocketFactory();
} catch (Exception e) {
throw new IllegalStateException("Can not create and initialize SSL", e);
}
}
private void loadKeyStore(char[] pass) throws NoSuchAlgorithmException, CertificateException {
String keystore = System.getProperty(GREENMAIL_KEYSTORE_FILE_PROPERTY);
if (null != keystore) {
loadKeyStore(ks, pass, new File(keystore));
} else {
try {
loadKeyStore(ks, pass, GREENMAIL_KEYSTORE_P12);
} catch (IllegalStateException ex) {
// Fallback to legacy JKS keystore
loadKeyStore(ks, pass, GREENMAIL_KEYSTORE_JKS);
}
}
}
private void loadKeyStore(KeyStore keyStore, char[] pass, String keystoreResource)
throws NoSuchAlgorithmException, CertificateException {
log.debug("Loading keystore from resource {} ...", keystoreResource);
try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(keystoreResource)) {
keyStore.load(is, pass);
} catch (IOException ex) {
// Try hard coded default keystore
throw new IllegalStateException(
"Can not load greenmail keystore from '" + keystoreResource + "' in classpath", ex);
}
}
private void loadKeyStore(KeyStore keyStore, char[] pass, File keystoreResource)
throws NoSuchAlgorithmException, CertificateException {
log.debug("Loading keystore from file {} ...", keystoreResource);
try (InputStream is = Files.newInputStream(keystoreResource.toPath())) {
keyStore.load(is, pass);
} catch (IOException ex) {
// Try hard coded default keystore
throw new IllegalStateException("Can not load greenmail keystore from file '" + keystoreResource + "'", ex);
}
}
private SSLServerSocket addAnonCipher(ServerSocket socket) {
SSLServerSocket ssl = (SSLServerSocket) socket;
ssl.setEnabledCipherSuites(addAnonCiphers(ssl.getEnabledCipherSuites()));
return ssl;
}
static String[] addAnonCiphers(String[] ciphers) {
final String[] newCiphers = new String[ciphers.length + ANONYMOUS_CIPHERS.length];
System.arraycopy(ciphers, 0, newCiphers, 0, ciphers.length);
System.arraycopy(ANONYMOUS_CIPHERS, 0, newCiphers, ciphers.length, ANONYMOUS_CIPHERS.length);
return newCiphers;
}
private enum Holder {
INSTANCE;
final DummySSLServerSocketFactory value = new DummySSLServerSocketFactory();
}
public static ServerSocketFactory getDefault() {
return Holder.INSTANCE.value;
}
@Override
public ServerSocket createServerSocket() throws IOException {
return addAnonCipher(factory.createServerSocket());
}
@Override
public ServerSocket createServerSocket(int i) throws IOException {
return addAnonCipher(factory.createServerSocket(i));
}
@Override
public ServerSocket createServerSocket(int i, int i1) throws IOException {
return addAnonCipher(factory.createServerSocket(i, i1));
}
@Override
public ServerSocket createServerSocket(int i, int i1, InetAddress inetAddress) throws IOException {
return addAnonCipher(factory.createServerSocket(i, i1, inetAddress));
}
@Override
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
public KeyStore getKeyStore() {
return ks;
}
}