package org.opennaas.extensions.transports.sockets;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.opennaas.core.resources.transport.IStreamTransport;
import org.opennaas.core.resources.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class handles all the SSL Transport details to be able to send/receive data from the switch.
* <p>
* Known Limitations: N/A
* <p>
* <b>History</b> <br>
* <p>
* 2006/04: Change in the transport to be integrated in the engine. <br>
* Inocybe Technologies - Copyright © 2006 <br>
* <p>
* 2003/12: Initial file creation. <br>
* Communications Research Centre and University of Ottawa - Copyright © 2003 <br>
* <p>
*
* @author Mathieu Lemay - Research Technologist, Communications Research Centre
* @version 2.2
*/
public class SSLTransport implements IStreamTransport {
public static final String SSL = "ssl";
static private Logger logger = LoggerFactory.getLogger(SSLTransport.class);
/** HostName for the transport */
private String host;
/** Port Number for the transport */
private int port;
/** KeyFile name */
private String keyfile;
/** password */
private String pwd;
/** SSL Socket */
private SSLSocket sslSocket = null;
private KeyStore serverKeyStore = null;
private TrustManagerFactory tmf = null;
private KeyManagerFactory kmf = null;
private SecureRandom secureRandom = null;
/** SSL PrintWriter to write to the socket */
private PrintWriter outPrint;
private boolean initialized = false;
private static final int READ_TIMEOUT = 300000;
public char[] msgtok = { '\n' };
public SSLTransport(String host, String port, String keystore, String keystorePassword) {
this.host = host;
this.port = Integer.parseInt(port);
this.keyfile = keystore;
this.pwd = keystorePassword;
}
/**
* Connects to the Device
*
* @throws IOException
* Throws IOException if it couldn't connection to host.. This can be either an UnknownHostException or a IO Read/Write Exception
*/
public void connect() throws TransportException {
if (!initialized) {
initialize();
initialized = true;
}
// connect to server
// Get a SocketFactory object
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
// TLS = Transport Layer Security - New name for SSL
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom);
SSLSocketFactory factory = sslContext.getSocketFactory();
sslSocket = (SSLSocket) factory.createSocket(host, port);
sslSocket.setSoTimeout(READ_TIMEOUT);
SSLSession session = sslSocket.getSession();
if (session.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL")) {
throw new TransportException("Could not connect to SSL Server. Invalid cipher suite: " + session.getCipherSuite());
}
X509Certificate cert;
logger.info("Connecting to " + host);
outPrint = new PrintWriter(sslSocket.getOutputStream(), true);
try {
cert = (X509Certificate) session.getPeerCertificates()[0];
} catch (SSLPeerUnverifiedException e) {
// If no or invalid certificate
logger.info(session.getPeerHost() + " No Valid Certificate Found");
throw new TransportException("Could not connect to SSL Server. "
+ session.getPeerHost() + " No Valid Certificate Found");
}
// Display details about the certificate
logger.info(session.getPeerHost() + " has this certifcate:");
logger.info("Subject: [" + cert.getSubjectDN().getName() + "]");
logger.info("The signature is:");
logger.info("Name :[" + cert.getIssuerDN().getName() + "]");
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Could not connect to SSL Server. " + e.getMessage());
}
}
private void initialize() throws TransportException {
logger.info("Initializing SSL transport remote host: " + host + " remote port " + port);
// Initialize the keystore
try
{
getKeys(keyfile, pwd);
tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(serverKeyStore);
// Sets the key Manager to use X.509 protocol
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(serverKeyStore, pwd.toCharArray());
secureRandom = new SecureRandom();
secureRandom.nextInt();
} catch (Exception e) {
e.printStackTrace();
throw new TransportException(e.getMessage());
}
}
/**
* Load the keystore in memory
*
* @param serverkeyfile
* @param password
* @throws GeneralSecurityException
* @throws TransportException
*/
private void getKeys(String serverkeyfile, String password) throws TransportException {
logger.info("Loading keys");
FileInputStream fis;
try {
serverKeyStore = KeyStore.getInstance("JKS");
// Read the keystore file
fis = new FileInputStream(new File(serverkeyfile));
} catch (FileNotFoundException e) {
throw new TransportException("Unable to open keyStore at: " + serverkeyfile);
} catch (KeyStoreException e) {
throw new TransportException(e.getMessage());
}
// Load the keystore in memory
try {
serverKeyStore.load(fis, password.toCharArray());
logger.debug("There are " + serverKeyStore.size() + " entries in the keystore");
} catch (NoSuchAlgorithmException e) {
throw new TransportException("Problems occurred while loading the SSL certificate from the keyStore. " + e.getMessage());
} catch (CertificateException e) {
throw new TransportException("Problems occurred while loading the SSL certificate from the keyStore. " + e.getMessage());
} catch (IOException e) {
throw new TransportException("Invalid keyStore password. " + e.getMessage());
} catch (KeyStoreException e) {
throw new TransportException(e.getMessage());
}
}
/** Disconnects from the switch */
public void disconnect() throws TransportException {
try {
logger.debug("Disconnecting SSL Transport");
sslSocket.close();
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Problems disconnecting from SSL Server. " + e.getMessage());
}
}
/**
* Sends a raw array of bytes to the device
*
* @param rawInput
* Sends String to send
*/
public void send(byte[] rawInput) throws TransportException {
logger.debug("Message to be sent: " + rawInput);
try {
outPrint.println(rawInput);
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Problems when disconnecting: " + e.getMessage());
}
}
/**
* Sends a raw array of chars to the device
*
* @param rawInput
* Sends String to send
*/
public void send(char[] rawInput) throws TransportException {
logger.debug("Message to be sent: " + rawInput.toString());
try {
outPrint.println(rawInput);
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Problems when disconnecting: " + e.getMessage());
}
}
public InputStream getInputStream() throws TransportException {
try {
return sslSocket.getInputStream();
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Problems getting the transport input stream: " + e.getMessage());
}
}
public OutputStream getOutputStream() throws TransportException {
try {
return sslSocket.getOutputStream();
} catch (Exception e) {
e.printStackTrace();
throw new TransportException("Problems getting the transport output stream: " + e.getMessage());
}
}
}