package org.simpleframework.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate;
import org.simpleframework.common.buffer.Allocator;
import org.simpleframework.common.buffer.FileAllocator;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Client.AnonymousTrustManager;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerTransportProcessor;
import org.simpleframework.transport.Certificate;
import org.simpleframework.transport.CertificateChallenge;
import org.simpleframework.transport.TransportProcessor;
import org.simpleframework.transport.TransportSocketProcessor;
import org.simpleframework.transport.SocketProcessor;
import org.simpleframework.transport.Socket;
import org.simpleframework.transport.Transport;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.simpleframework.transport.trace.TraceAnalyzer;
import org.simpleframework.transport.trace.Trace;
public class MockRenegotiationServer implements Container {
private final ConfigurableCertificateServer server;
private final Connection connection;
private final SocketAddress address;
private final SSLContext context;
private final TraceAnalyzer agent;
public static void main(String[] list) throws Exception {
System.err.println("Starting renegotiation test.....");
//System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
//System.setProperty("sun.security.ssl.allowLegacyHelloMessages", "true");
//File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-site\\etc\\www.yieldbroker.com.pfx");
File file = new File("/Users/niallg/Work/development/yieldbroker/proxy/yieldbroker-proxy-site/certificate/www.yieldbroker.com.pfx");
//File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-trading\\etc\\uat.yieldbroker.com.pfx");
KeyStoreReader reader = new KeyStoreReader(KeyStoreType.PKCS12, file, "p", "p");
SecureSocketContext context = new SecureSocketContext(reader, SecureProtocol.TLS);
SSLContext sslContext = context.getContext();
MockRenegotiationServer server = new MockRenegotiationServer(sslContext, false, 10001);
server.start();
}
public MockRenegotiationServer(SSLContext context, boolean certRequired, int port) throws IOException {
Allocator allocator = new FileAllocator();
ContainerTransportProcessor processor = new ContainerTransportProcessor(this, allocator, 4);
TransportGrabber grabber = new TransportGrabber(processor);
TransportSocketProcessor processorServer = new TransportSocketProcessor(grabber);
this.server = new ConfigurableCertificateServer(processorServer, certRequired);
this.agent = new ConsoleAgent();
this.connection = new SocketConnection(server, agent);
this.address = new InetSocketAddress(port);
this.context = context;
}
public void handle(final Request req, final Response resp) {
boolean challengeForCertificate = false;
try {
final PrintStream out = resp.getPrintStream();
String normal = req.getPath().getPath();
if(normal.indexOf(".ico") == -1) {
SSLEngine engine = (SSLEngine)req.getAttribute(SSLEngine.class);
if(normal.startsWith("/niall/cert")) {
SocketChannel channel = ((Transport)req.getAttribute(Transport.class)).getChannel();
System.err.println("NEW REQUEST ("+System.identityHashCode(engine)+"): " + req);
try {
resp.setContentType("text/plain");
resp.setValue("Connection", "keep-alive");
String certificateInfo = null;
try {
X509Certificate[] list = req.getClientCertificate().getChain();
StringBuilder builder = new StringBuilder();
for(X509Certificate cert : list) {
X509Certificate x509 = (X509Certificate)cert;
builder.append(x509);
}
certificateInfo = builder.toString();
} catch(Exception e) {
e.printStackTrace();
certificateInfo = e.getMessage();
challengeForCertificate = true;
// http://stackoverflow.com/questions/14281628/ssl-renegotiation-with-client-certificate-causes-server-buffer-overflow
// Perhaps an expect 100 continue does something here?????
if(challengeForCertificate) {
Certificate certificate = req.getClientCertificate();
CertificateChallenge challenge = certificate.getChallenge();
Future<Certificate> future = challenge.challenge(new Runnable() {
public void run() {
System.err.println("FINISHED THE CHALLENGE!!!");
}
});
Certificate futureCert = future.get(10, TimeUnit.SECONDS);
if(futureCert == null) {
System.err.println("FAILED TO GET CERT!!!!");
} else {
System.err.println("**** GOT THE CERT");
}
String text= "Challenge finished without cert";
try {
X509Certificate[] list = req.getClientCertificate().getChain();
StringBuilder builder = new StringBuilder();
for(X509Certificate x509 : list) {
builder.append(x509);
}
text = builder.toString();
} catch(Exception ex) {
e.printStackTrace();
}
out.print(text);
out.flush();
try {
resp.close();
} catch(Exception ex){
e.printStackTrace();
}
}
}
// Thread.sleep(10000);
if(!challengeForCertificate) {
try {
X509Certificate[] list = req.getClientCertificate().getChain();
StringBuilder builder = new StringBuilder();
for(X509Certificate cert : list) {
X509Certificate x509 = (X509Certificate)cert;
builder.append(x509);
}
certificateInfo = builder.toString();
} catch(Exception e) {
e.printStackTrace();
}
out.print(certificateInfo);
out.flush();
resp.close();
}
} finally {
if(!challengeForCertificate) {
System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!("+System.identityHashCode(engine)+")!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WORKING");
}
}
} else {
resp.setStatus(org.simpleframework.http.Status.NOT_FOUND);
resp.setValue("Connection", "keep-alive");
resp.setValue("Content-Type", "text/plain");
out.println("Ok normal request with NO renegotiation " + req);
}
} else {
resp.setStatus(org.simpleframework.http.Status.NOT_FOUND);
resp.setValue("Connection", "keep-alive");
resp.setValue("Content-Type", "text/plain");
out.println("fuck off!!");
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if(!challengeForCertificate) {
resp.close();
} else {
System.err.println("NOT DOING ANYTHING WITH THE REQUEST, AS A CHALLENGE IS UNDERWAY challengeForCertificate="+challengeForCertificate+" path="+req);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
public void start() throws IOException {
connection.connect(address, context);
}
public void stop() throws IOException {
connection.close();
}
private static class ConsoleAgent implements TraceAnalyzer {
private final Map<SelectableChannel, Integer> map;
private final AtomicInteger count;
public ConsoleAgent() {
this.map = new ConcurrentHashMap<SelectableChannel, Integer>();
this.count = new AtomicInteger();
}
public Trace attach(SelectableChannel channel) {
if(map.containsKey(channel)) {
throw new IllegalStateException("Can't attach twice");
}
final int counter = count.getAndIncrement();
map.put(channel, counter);
return new Trace() {
public void trace(Object event) {
trace(event, "");
}
public void trace(Object event, Object value) {
if(value instanceof Throwable) {
StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
((Exception)value).printStackTrace(out);
out.flush();
value = writer.toString();
}
if(value != null && !String.valueOf(value).isEmpty()) {
System.err.printf("(%s) [%s] %s: %s%n", Thread.currentThread().getName(), counter, event, value);
} else {
System.err.printf("(%s) [%s] %s%n", Thread.currentThread().getName(), counter, event);
}
}
};
}
public void stop() {
System.err.println("Stop agent");
}
}
public static class TransportGrabber implements TransportProcessor {
private TransportProcessor processor;
public TransportGrabber(TransportProcessor processor) {
this.processor = processor;
}
public void process(Transport transport) throws IOException {
transport.getAttributes().put(Transport.class, transport);
processor.process(transport);
}
public void stop() throws IOException {
processor.stop();
}
}
public static class ConfigurableCertificateServer implements SocketProcessor {
private SocketProcessor server;
private boolean certRequired;
public ConfigurableCertificateServer(SocketProcessor server) {
this(server, false);
}
public ConfigurableCertificateServer(SocketProcessor server, boolean certRequired) {
this.certRequired = certRequired;
this.server = server;
}
public void setCertRequired(boolean certRequired) {
this.certRequired = certRequired;
}
public void process(Socket socket) throws IOException {
SSLEngine engine = socket.getEngine();
socket.getAttributes().put(SSLEngine.class, engine);
if(certRequired) {
socket.getEngine().setNeedClientAuth(true);
}
server.process(socket);
}
public void stop() throws IOException {
System.err.println("stop");
}
}
public enum KeyStoreType {
JKS("JKS", "SunX509"),
PKCS12("PKCS12", "SunX509");
private final String algorithm;
private final String type;
private KeyStoreType(String type, String algorithm) {
this.algorithm = algorithm;
this.type = type;
}
public String getType() {
return type;
}
public KeyStore getKeyStore() throws KeyStoreException {
return KeyStore.getInstance(type);
}
public KeyManagerFactory getKeyManagerFactory() throws NoSuchAlgorithmException {
return KeyManagerFactory.getInstance(algorithm);
}
}
private static class KeyStoreManager {
private final KeyStoreType keyStoreType;
public KeyStoreManager(KeyStoreType keyStoreType) {
this.keyStoreType = keyStoreType;
}
public KeyManager[] getKeyManagers(InputStream keyStoreSource, String keyStorePassword, String keyManagerPassword) throws Exception {
KeyStore keyStore = keyStoreType.getKeyStore();
KeyManagerFactory keyManagerFactory = keyStoreType.getKeyManagerFactory();
keyStore.load(keyStoreSource, keyManagerPassword.toCharArray());
keyManagerFactory.init(keyStore, keyManagerPassword.toCharArray());
return keyManagerFactory.getKeyManagers();
}
}
private static class KeyStoreReader {
private final KeyStoreManager keyStoreManager;
private final String keyManagerPassword;
private final String keyStorePassword;
private final File keyStore;
public KeyStoreReader(KeyStoreType keyStoreType, File keyStore, String keyStorePassword, String keyManagerPassword) {
this.keyStoreManager = new KeyStoreManager(keyStoreType);
this.keyManagerPassword = keyManagerPassword;
this.keyStorePassword = keyStorePassword;
this.keyStore = keyStore;
}
public KeyManager[] getKeyManagers() throws Exception {
InputStream storeSource = new FileInputStream(keyStore);
try {
return keyStoreManager.getKeyManagers(storeSource, keyStorePassword, keyManagerPassword);
} finally {
storeSource.close();
}
}
}
public enum SecureProtocol {
DEFAULT("Default"),
SSL("SSL"),
TLS("TLS");
private final String protocol;
private SecureProtocol(String protocol) {
this.protocol = protocol;
}
public SSLContext getContext() throws NoSuchAlgorithmException {
return SSLContext.getInstance(protocol);
}
}
private static class SecureSocketContext {
private final X509TrustManager trustManager;
private final X509TrustManager[] trustManagers;
private final KeyStoreReader keyStoreReader;
private final SecureProtocol secureProtocol;
public SecureSocketContext(KeyStoreReader keyStoreReader, SecureProtocol secureProtocol) {
this.trustManager = new AnonymousTrustManager();
this.trustManagers = new X509TrustManager[]{trustManager};
this.keyStoreReader = keyStoreReader;
this.secureProtocol = secureProtocol;
}
public SSLContext getContext() throws Exception {
KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
SSLContext secureContext = secureProtocol.getContext();
secureContext.init(keyManagers, trustManagers, null);
return secureContext;
}
public SocketFactory getSocketFactory() throws Exception {
KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
SSLContext secureContext = secureProtocol.getContext();
secureContext.init(keyManagers, trustManagers, null);
return secureContext.getSocketFactory();
}
}
}