fr.dyade.aaa.agent.HttpsNetwork Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of a3-rt Show documentation
Show all versions of a3-rt Show documentation
Builds the Joram a3 rt project.
/*
* Copyright (C) 2005 - 2022 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package fr.dyade.aaa.agent;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.net.UnknownHostException;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.objectweb.util.monolog.api.BasicLevel;
/**
* HttpNetwork is a specialization of HttpNetwork
* for SSL.
*/
public final class HttpsNetwork extends HttpNetwork {
/**
* Name of property that allow to fix the keystore's password:
* "HttpsNetwork.pass". By default the password is "changeit".
* This property can be fixed either from java
launching
* command (-Dname=value), or by in a3servers.xml
configuration
* file (property element).
*/
public final static String PASS = "HttpsNetwork.pass";
/**
* Name of property that allow to fix the keystore's pathname:
* "HttpsNetwork.keyfile". By default the key file is ".keystore".
* This property can be fixed either from java
launching
* command (-Dname=value), or by in a3servers.xml
configuration
* file (property element).
*/
public final static String KEYFILE = "HttpsNetwork.keyfile";
/**
* HttpsNetwork needs specific use of SSLSocketFactory.
*/
private SSLSocketFactory sslSocketFactory = null;
/**
* HttpsNetwork needs specific use of SSLServerSocketFactory.
*/
private SSLServerSocketFactory sslServerSocketFactory = null;
public HttpsNetwork() throws Exception {
super();
}
private SSLSocketFactory getSSLSocketFactory() throws IOException {
if (sslSocketFactory == null) {
try {
char[] pass = AgentServer.getProperty(PASS, "changeit").toCharArray();
String keyFile = AgentServer.getProperty(KEYFILE, ".keystore");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyFile), pass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslSocketFactory = ctx.getSocketFactory();
} catch (IOException exc) {
throw exc;
} catch (Exception exc) {
logmon.log(BasicLevel.ERROR,
this.getName() + ", cannot initialize SSLSocketFactory", exc);
throw new IOException(exc.getMessage());
}
}
return sslSocketFactory;
}
private SSLServerSocketFactory getSSLServerSocketFactory() throws IOException {
if (sslServerSocketFactory == null) {
try {
char[] pass = AgentServer.getProperty(PASS, "changeit").toCharArray();
String keyFile = AgentServer.getProperty(KEYFILE, ".keystore");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyFile), pass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, pass);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), null, null);
sslServerSocketFactory = ctx.getServerSocketFactory();
} catch (IOException exc) {
throw exc;
} catch (Exception exc) {
logmon.log(BasicLevel.ERROR,
this.getName() + ", cannot initialize SSLServerSocketFactory", exc);
throw new IOException(exc.getMessage());
}
}
return sslServerSocketFactory;
}
/**
* This method creates and returns a SSL server socket which is bound to
* the specified port.
*
* @param port the port to listen to.
* @return a SSL server socket bound to the specified port.
*
* @exception IOException for networking errors
*/
ServerSocket createServerSocket(int port) throws IOException {
ServerSocket serverSocket =
getSSLServerSocketFactory().createServerSocket(port, backlog, inLocalAddr);
((SSLServerSocket) serverSocket).setNeedClientAuth(false);
return serverSocket;
}
/**
* This method creates and returns a SSL socket connected to a ServerSocket
* at the specified network address and port.
*
* @param addr the server address.
* @param port the server port.
* @return a socket connected to a ServerSocket at the specified
* network address and port.
*
* @exception IOException if the connection can't be established
*/
Socket createSocket(InetAddress addr, int port) throws IOException {
if (addr == null)
throw new UnknownHostException();
// AF: Be careful SSLSocketFactory don't allow to use ConnectTimeout
return getSSLSocketFactory().createSocket(addr, port,
outLocalAddr, outLocalPort);
}
/**
* This method creates a tunnelling socket if a proxy is used.
*
* @param host the server host.
* @param port the server port.
* @param proxy the proxy host.
* @param proxyport the proxy port.
* @return a socket connected to a ServerSocket at the specified
* network address and port.
*
* @exception IOException if the connection can't be established
*/
Socket createTunnelSocket(InetAddress host, int port,
InetAddress proxy, int proxyport) throws IOException {
// Set up a socket to do tunneling through the proxy.
// Start it off as a regular socket, then layer SSL over the top of it.
Socket tunnel = new Socket(proxy, proxyport);
doTunnelHandshake(tunnel, host, port);
// Ok, let's overlay the tunnel socket with SSL.
SSLSocket socket =
(SSLSocket) getSSLSocketFactory().createSocket(tunnel,host.getHostName(), port, true);
// register a callback for handshaking completion event
socket.addHandshakeCompletedListener(
new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
}
});
return socket;
}
private void doTunnelHandshake(Socket tunnel, InetAddress host, int port)
throws IOException {
OutputStream out = tunnel.getOutputStream();
String msg = "CONNECT " + host.getHostName() + ":" + port + " HTTP/1.0\r\n\r\n";
byte b[];
try {
// We really do want ASCII7 -- the http protocol doesn't change
// with locale.
b = msg.getBytes("ASCII7");
} catch (UnsupportedEncodingException ignored) {
// If ASCII7 isn't there, something serious is wrong
b = msg.getBytes();
}
out.write(b);
out.flush();
// We need to store the reply so we can create a detailed
// error message to the user.
byte reply[] = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false; /* Done on first newline */
InputStream in = tunnel.getInputStream();
while (newlinesSeen < 2) {
int i = in.read();
if (i < 0) {
throw new IOException("Unexpected EOF from proxy");
}
if (i == '\n') {
headerDone = true;
++newlinesSeen;
} else if (i != '\r') {
newlinesSeen = 0;
if (!headerDone && replyLen < reply.length) {
reply[replyLen++] = (byte) i;
}
}
}
// Converting the byte array to a string is slightly wasteful
// in the case where the connection was successful, but it's
// insignificant compared to the network overhead.
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, "ASCII7");
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
/* We asked for HTTP/1.0, so we should get that back */
if (! (replyStr.startsWith("HTTP/1.0 200") ||
(replyStr.startsWith("HTTP/1.1 200")))) {
throw new IOException("Unable to tunnel , proxy returns \"" + replyStr + "\"");
}
// tunneling Handshake was successful!
}
}