/** * OnionCoffee - Anonymous Communication through TOR Network * Copyright (C) 2005-2007 RWTH Aachen University, Informatik IV * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package TorJava; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import TorJava.Common.TorException; /** * functionality for the TLS connections bridging the gap to the first nodes in * the routes. * * @author Lexi Pimenidis * @author Vinh Pham * @version unstable */ class TLSConnection { // pointer to the server Server server; // the physical connection (if any) to the node SSLSocket tls; // boolean handshake_finished = false; boolean closed = false; TLSDispatcher dispatcher; DataOutputStream sout; ConcurrentHashMap<Integer,Circuit> circuits; static String[] enabledSuites = { /*"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA".*/ "DHE-RSA-AES128-SHA"}; static String filenameKeyStore = "/tmp"; /** * creates the TLS connection and installs a dispatcher for incoming data. * * @param server * the server to connect to * @see TLSDispatcher * @exception IOException * @exception SSLPeerUnverifiedException */ TLSConnection(Server server, PrivateKeyHandler pkh) throws IOException, SSLPeerUnverifiedException, SSLException { if (server == null) throw new IOException("TLSConnection: server variable is NULL"); this.server = server; circuits = new ConcurrentHashMap<Integer,Circuit>(); // create new certificates and use them ad-hoc // KeyManager kms[] = new KeyManager[1]; // XXX: Leave out the PrivateKeyHandler, should be needed for // server operation and hidden services only //kms[0] = pkh; // use the keys and certs from above to connect to Tor-network try { TrustManager[] tms = { new TorX509TrustManager() }; SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tms, null); //Log.i("TorTLS", "Here"); //context.init(kms, tms, null); //Log.i("TorTLS", "Here2"); SSLSocketFactory factory = (SSLSocketFactory) context.getSocketFactory(); tls = (SSLSocket) factory.createSocket(server.hostname, server.orPort); // FIXME: check certificates received in TLS // (note: not an important security bug, since it only affects hop2hop-encryption, real // data is encrypted anyway on top of TLS) /* * // for debugging purposes * javax.net.ssl.HandshakeCompletedListener hscl = new * javax.net.ssl.HandshakeCompletedListener() { public void * handshakeCompleted(HandshakeCompletedEvent e) { try { * System.out.println("Cipher: "+e.getCipherSuite()); * java.security.cert.Certificate[] chain = * e.getLocalCertificates(); System.out.println("Send cert-chain of * length "+chain.length); for(int i=0;i<chain.length;++i) * System.out.println(" cert "+i+": "+chain[i].toString()); chain = * e.getPeerCertificates(); System.out.println("Received cert-chain * of length "+chain.length); for(int i=0;i<chain.length;++i) * System.out.println(" cert "+i+": "+chain[i].toString()); } * catch(Exception ex) {} } }; * tls.addHandshakeCompletedListener(hscl); */ tls.setEnabledCipherSuites(enabledSuites); //String[] strs = tls.getEnabledCipherSuites(); // create object to write data to stream sout = new DataOutputStream(tls.getOutputStream()); // start listening for incoming data this.dispatcher = new TLSDispatcher(this, new DataInputStream(tls.getInputStream())); /*} catch (NoSuchProviderException e) { SSLException e2 = new SSLException(e.getMessage()); e2.setStackTrace(e.getStackTrace()); throw e2;*/ } catch (NoSuchAlgorithmException e) { SSLException e2 = new SSLException(e.getMessage()); e2.setStackTrace(e.getStackTrace()); throw e2; } catch (KeyManagementException e) { SSLException e2 = new SSLException(e.getMessage()); e2.setStackTrace(e.getStackTrace()); throw e2; } } /** * constructor for incoming TLS-Connections */ TLSConnection(SSLSocket ssl) throws IOException, SSLPeerUnverifiedException, SSLException { server = null; tls = ssl; // create object to write data to stream sout = new DataOutputStream(tls.getOutputStream()); // start listening for incoming data this.dispatcher = new TLSDispatcher(this, new DataInputStream(tls.getInputStream())); } /** * converts a cell to bytes and transmitts it over the line. received data * is dispatched by the class TLSDispatcher * * @param c * the cell to send * @exception IOException * @see TLSDispatcher */ synchronized void send_cell(Cell c) throws IOException { try{ sout.write(c.toByteArray()); } catch(IOException e) { // force to close the connection close(true); // rethrow error throw e; } } /** * returns a free circID and save that it points to "c", save it to "c", * too. Throws an exception, if no more free IDs are available, or the TLS * connection is marked as closed.<br> * FIXME: replace this code with something more beautiful * * @param c * the circuit that is going to be build through this * TLS-Connection * @return an identifier for this new circuit * @exception TorException */ synchronized int assign_circID(Circuit c) throws TorException { if (closed) throw new TorException("TLSConnection.assign_circID(): Connection to "+server.nickname+" is closed for new circuits"); // find a free number (other than zero) int ID; int j = 0; do { if (++j > 1000) throw new TorException("TLSConnection.assign_circID(): no more free IDs"); // Deprecated: 16 bit unsigned Integers with MSB set // ID = FirstNodeHandler.rnd.nextInt() & 0xffff | 0x8000; // XXX: Since the PrivateKeyHandler is gone, we don't need to consider // the MSB as long as we are in client mode (see main-tor-spec.txt, Section 5.1) ID = FirstNodeHandler.rnd.nextInt() & 0xffff; // & 0x7fff; if (circuits.containsKey(new Integer(ID))) ID = 0; } while (ID == 0); // assign ID to circuit, memorize circuit c.ID = ID; circuits.put(new Integer(ID), c); return ID; } /** * marks as closed. closes if no more data or forced closed on real close: * kill dispatcher * * @param force * set to TRUE if established circuits shall be cut and * terminated. */ void close(boolean force) { Logger.logTLS(Logger.VERBOSE, "TLSConnection.close(): Closing TLS to " + server.nickname); closed = true; // FIXME: a problem with (!force) is, that circuits, that are currently // still build up // are not killed. their build-up should be stopped // close circuits, if forced Iterator<Integer> i = circuits.keySet().iterator(); while (i.hasNext()) { Object nick = i.next(); Circuit onlist = (Circuit) circuits.get(nick); if (onlist.close(force)) i.remove(); } if (!(force || circuits.isEmpty())) return; // kill dispatcher dispatcher.close(); // close TLS connection try { sout.close(); tls.close(); } catch (IOException e) { } } }