package totalcross.net.ssl; import totalcross.crypto.CryptoException; import totalcross.io.ByteArrayStream; import totalcross.io.IOException; import totalcross.net.Socket; import totalcross.net.UnknownHostException; import totalcross.sys.Vm; /** * This class extends Sockets and provides secure socket using protocols such as the "Secure Sockets Layer" (SSL) or * IETF "Transport Layer Security" (TLS) protocols.<br> * * The initial handshake on this connection is initiated by calling startHandshake which explicitly begins handshakes. * If handshaking fails for any reason, the SSLSocket is closed, and no further communications can be done. */ public class SSLSocket extends Socket { private SSLClient sslClient; private SSL sslConnection; private SSLReadHolder sslReader; private ByteArrayStream buffer = null; /** * Constructs an SSL connection to a named host at a specified port, with the specified connection timeout, binding * the client side of the connection a given address and port. This acts as the SSL client. * * @param host * the server's host * @param port * its port * @param timeout * the timeout for this operation * @throws UnknownHostException * if the host is not known * @throws IOException * if an I/O error occurs when creating the socket */ public SSLSocket(String host, int port, int timeout) throws UnknownHostException, IOException { super(host, port, timeout); } /** * Creates a new SSLClient to be used by this instance of SSLSocket during the handshake. The default implementation * does not perform any kind of validation. Subclasses may override this method to use their own implementation of * SSLClient. * * @return a SSLClient initialized with the objects required to perform the validation for this socket. * @throws CryptoException */ protected SSLClient prepareContext() throws CryptoException { return new SSLClient(Constants.SSL_SERVER_VERIFY_LATER, 0); } /** * Starts an SSL handshake on this connection. * * @throws IOException * on a network level error */ public void startHandshake() throws IOException { try { sslClient = prepareContext(); sslConnection = sslClient.connect(this, null); Exception e = sslConnection.getLastException(); if (e != null) throw new IOException(e.getMessage()); int status; while ((status = sslConnection.handshakeStatus()) == Constants.SSL_HANDSHAKE_IN_PROGRESS) Vm.sleep(25); if (status != Constants.SSL_OK) throw new IOException("SSL handshake failed"); sslReader = new SSLReadHolder(); buffer = new ByteArrayStream(256); buffer.mark(); } catch (Exception e) { try { this.close(); } catch (IOException e2) { } if (e instanceof IOException) throw (IOException) e; else throw new IOException(e.getMessage()); } } public int readBytes(byte[] buf, int start, int count) throws IOException { if (buffer == null) return super.readBytes(buf, start, count); if (buffer.available() == 0) { int sslReadBytes = sslConnection.read(sslReader); buffer.reuse(); if (sslReadBytes > 0) buffer.writeBytes(sslReader.getData(), 0, sslReadBytes); buffer.mark(); } int readBytes = buffer.readBytes(buf, start, count); return readBytes; } public int readBytes(byte[] buf) throws IOException { return this.readBytes(buf, 0, buf.length); } public int writeBytes(byte[] buf, int start, int count) throws IOException { if (buffer == null) return super.writeBytes(buf, start, count); if (start > 0) { byte[] buf2 = new byte[count]; Vm.arrayCopy(buf, start, buf2, 0, count); buf = buf2; } return sslConnection.write(buf, count); } public void close() throws IOException { if (buffer != null) buffer = null; if (sslConnection != null) sslConnection.dispose(); if (sslClient != null) sslClient.dispose(); super.close(); } }