Запутывание сообщения клиента SSL Hello v2 в Java - PullRequest
2 голосов
/ 19 июля 2011

В настоящее время я работаю над инструментом клиент / сервер TLS, который требует от нас подключения через брандмауэры.По независящим от нас причинам, нам предоставляется только исходящее TCP-соединение.

Проблема в том, что брандмауэр нашего клиента блокирует клиентское сообщение Hello v2 (ивозможно, все рукопожатие SSL).

Есть ли способ каким-либо образом запутать поток? Я думал о попытке использовать сжатие, чтобы сделать поток нечитаемым для брандмауэра.(Может быть, с помощью GzipOutputStream JDK7, который теперь допускает сброс syncFlush)

Я не эксперт по SSL, но мне кажется, что должна быть возможность транслировать весь поток, который не позволит брандмауэру установить соединениеи заблокировать его.

Насколько я вижу, есть несколько (два?) способов сделать это:

  • Переопределить реализацию по умолчанию
  • Реализация SSLServerSocketFactory

Первый вариант не сработал для меня, так как я не могу найти исходный код com.sun.net.ssl.internal.ssl.SSLServerSocketFactoryImpl, который является реализацией по умолчанию,Я просматривал исходный код openJDK для него, но даже там источники, похоже, отсутствуют.

Реализация SSLServerSocketFactory выходит за рамки моих возможностей.Как я уже сказал, я не эксперт по SSL.

Обратите внимание, что приложение отлично работает через другие, менее агрессивные брандмауэры / правила брандмауэра.

Ответы [ 4 ]

4 голосов
/ 19 июля 2011

Сжатие зашифрованного потока бесполезно, когда вам на самом деле требуется лишь маскировка, чтобы избежать брандмауэра.

На стороне клиента вы можете использовать метод SSLSocketFactory createSocket(socket, host, port, autoclose) для создания сокета SSL на основе другого сокета - и этот другой сокет может получить вашу специальную реализацию SocketImpl, выполняя простой XOR Маскировка на первые несколько байтов.

На стороне сервера все сложнее, поскольку в SSLServerSocketFactory такого метода нет.

В ответе на Java RMI + SSL + Сжатие = НЕВОЗМОЖНО! я описал, как создать делегирующую фабрику сокетов. Там это было сделано для Rmi (Client | Server) SocketFactory, но это будет работать аналогичным образом для ServerSocketFactory или SocketFactory.


Но, конечно, может быть так, что ваш брандмауэр на самом деле не блокирует трафик SSL, а блокирует все, что не занесено в белый список (например, HTTP). Перед построением реализации сокета-обертки попробуйте, если простой сокет + serversocket, который отправляет некоторые случайные данные и получает их обратно, даже работает.

2 голосов
/ 19 июля 2011

Возможно, немного не по теме, но если проблема связана именно с SSLv2, и если рукопожатия SSLv3 или TLS 1.x работают нормально, вы можете отключить V2 ClientHello, используя SSLSocket.setEnabledProtocols(new String[] { "SSLv3", "TLSv1", "TLSv1.1" }).

См. Справочное руководство JSSE (раздел SSLContext) .

РЕДАКТИРОВАТЬ : для тех, кто не читает комментарии, здесьссылка на ответ @ EJP с более подробной информацией по этой теме: Почему Java SSLSocket передает привет клиенту версии 2?

2 голосов
/ 19 июля 2011

Можно туннелировать TCP через HTTP без дополнительной разработки . Есть разные инструменты. Посмотрите на GNU httptunnel . httptunnel создает двунаправленное виртуальное соединение для передачи данных, туннелированное в HTTP-запросах. HTTP-запросы могут быть отправлены через HTTP-прокси, если это необходимо. Httpc тоже весьма интересен.

1 голос
/ 19 июля 2011

Похоже, решение состоит в том, чтобы объединить предложение Бруно и решение Пауло.Решение Пауло позволяет нам настраивать поведение нашего SSLSocket или SSLServerSocket с использованием делегатов.Предложение Бруно позволяет нам сообщить реализации SSL по умолчанию использовать наш модифицированный SSLSocket или SSLServerSocket.

Вот что я сделал:

  • Создание делегата класса ServerSocket (MyServerSocket)
  • Создание делегата класса ServerSocketFactory (MyServerSocketFactory)
  • Созданиекласс SocketFactory делегата (MySocketFactory)
  • Создать класс сокета делегата (MySocket)
  • Создать XorInputStream (найти его здесь )
  • Создать XorOutputStream (найтиэто здесь )

На стороне сервера:

    // Initialisation as usual
    ...
    sslSocketFactory = sslContext.getSocketFactory();

    serverSocketFactory = ServerSocketFactory.getDefault();
    serverSocketFactory = new MyServerSocketFactory(serverSocketFactory);
    serverSocket = serverSocketFactory.createServerSocket(port);
    ...
    Socket s = (Socket) serverSocket.accept();
    sslSocket = (SSLSocket) sslSocketFactory.createSocket(s, null, s.getPort(), false);
    sslSocket.setUseClientMode(false);
    sslSocket.setEnabledCipherSuites(new String[]{"SSL_RSA_WITH_RC4_128_MD5"});
    sslSocket.setNeedClientAuth(true);
    ...

На стороне клиента:

    Socket s = new MySocketFactory(SocketFactory.getDefault()).createSocket(host, port);
    SSLSocket socket = (SSLSocket) factory.createSocket(s, host, port, false);

Источники



    public class MyServerSocket extends ServerSocket {
    private ServerSocket baseSocket;

    public MyServerSocket(ServerSocket baseSocket) throws IOException {
        this.baseSocket = baseSocket;
    }

    @Override
    public Socket accept() throws IOException {
        return new MySocket(baseSocket.accept());
    }

    @Override
    public void bind(SocketAddress endpoint) throws IOException {
        baseSocket.bind(endpoint);
    }

    @Override
    public void bind(SocketAddress endpoint, int backlog) throws IOException {
        baseSocket.bind(endpoint, backlog);
    }

    @Override
    public void close() throws IOException {
        baseSocket.close();
    }

    @Override
    public ServerSocketChannel getChannel() {
        return baseSocket.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return baseSocket.getInetAddress();
    }

    @Override
    public int getLocalPort() {
        return baseSocket.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return baseSocket.getLocalSocketAddress();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return baseSocket.getReceiveBufferSize();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return baseSocket.getReuseAddress();
    }

    @Override
    public synchronized int getSoTimeout() throws IOException {
        return baseSocket.getSoTimeout();
    }

    @Override
    public boolean isBound() {
        return baseSocket.isBound();
    }

    @Override
    public boolean isClosed() {
        return baseSocket.isClosed();
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        baseSocket.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        baseSocket.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean on) throws SocketException {
        baseSocket.setReuseAddress(on);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        baseSocket.setSoTimeout(timeout);
    }

    @Override
    public String toString() {
        return baseSocket.toString();
    }


    }


    public class MyServerSocketFactory extends ServerSocketFactory {

    private ServerSocketFactory baseFactory;

    public MyServerSocketFactory(ServerSocketFactory baseFactory) {
        this.baseFactory = baseFactory;
    }


    @Override
    public ServerSocket createServerSocket(int i) throws IOException {
        return new MyServerSocket(baseFactory.createServerSocket(i));
    }

    @Override
    public ServerSocket createServerSocket(int i, int i1) throws IOException {
        return new MyServerSocket(baseFactory.createServerSocket(i, i1));
    }

    @Override
    public ServerSocket createServerSocket(int i, int i1, InetAddress ia) throws IOException {
        return new MyServerSocket(baseFactory.createServerSocket(i, i1, ia));
    }


    }


    public class MySocket extends Socket {
    private Socket baseSocket;

    public MySocket(Socket baseSocket) {
        this.baseSocket = baseSocket;
    }

    private XorInputStream xorInputStream = null;
    private XorOutputStream xorOutputStream = null;
    private final byte pattern = (byte)0xAC;

    @Override
    public InputStream getInputStream() throws IOException {
        if (xorInputStream == null)
        {
            xorInputStream = new XorInputStream(baseSocket.getInputStream(), pattern);
        }
        return xorInputStream;
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        if (xorOutputStream == null)
        {
            xorOutputStream = new XorOutputStream(baseSocket.getOutputStream(), pattern);
        }
        return xorOutputStream;
    }

    @Override
    public void bind(SocketAddress bindpoint) throws IOException {
        baseSocket.bind(bindpoint);
    }

    @Override
    public synchronized void close() throws IOException {
        baseSocket.close();
    }

    @Override
    public void connect(SocketAddress endpoint) throws IOException {
        baseSocket.connect(endpoint);
    }

    @Override
    public void connect(SocketAddress endpoint, int timeout) throws IOException {
        baseSocket.connect(endpoint, timeout);
    }

    @Override
    public SocketChannel getChannel() {
        return baseSocket.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return baseSocket.getInetAddress();
    }

    @Override
    public boolean getKeepAlive() throws SocketException {
        return baseSocket.getKeepAlive();
    }

    @Override
    public InetAddress getLocalAddress() {
        return baseSocket.getLocalAddress();
    }

    @Override
    public int getLocalPort() {
        return baseSocket.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return baseSocket.getLocalSocketAddress();
    }

    @Override
    public boolean getOOBInline() throws SocketException {
        return baseSocket.getOOBInline();
    }

    @Override
    public int getPort() {
        return baseSocket.getPort();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return baseSocket.getReceiveBufferSize();
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return baseSocket.getRemoteSocketAddress();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return baseSocket.getReuseAddress();
    }

    @Override
    public synchronized int getSendBufferSize() throws SocketException {
        return baseSocket.getSendBufferSize();
    }

    @Override
    public int getSoLinger() throws SocketException {
        return baseSocket.getSoLinger();
    }

    @Override
    public synchronized int getSoTimeout() throws SocketException {
        return baseSocket.getSoTimeout();
    }

    @Override
    public boolean getTcpNoDelay() throws SocketException {
        return baseSocket.getTcpNoDelay();
    }

    @Override
    public int getTrafficClass() throws SocketException {
        return baseSocket.getTrafficClass();
    }

    @Override
    public boolean isBound() {
        return baseSocket.isBound();
    }

    @Override
    public boolean isClosed() {
        return baseSocket.isClosed();
    }

    @Override
    public boolean isConnected() {
        return baseSocket.isConnected();
    }

    @Override
    public boolean isInputShutdown() {
        return baseSocket.isInputShutdown();
    }

    @Override
    public boolean isOutputShutdown() {
        return baseSocket.isOutputShutdown();
    }

    @Override
    public void sendUrgentData(int data) throws IOException {
        baseSocket.sendUrgentData(data);
    }

    @Override
    public void setKeepAlive(boolean on) throws SocketException {
        baseSocket.setKeepAlive(on);
    }

    @Override
    public void setOOBInline(boolean on) throws SocketException {
        baseSocket.setOOBInline(on);
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        baseSocket.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        baseSocket.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean on) throws SocketException {
        baseSocket.setReuseAddress(on);
    }

    @Override
    public synchronized void setSendBufferSize(int size) throws SocketException {
        baseSocket.setSendBufferSize(size);
    }

    @Override
    public void setSoLinger(boolean on, int linger) throws SocketException {
        baseSocket.setSoLinger(on, linger);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        baseSocket.setSoTimeout(timeout);
    }

    @Override
    public void setTcpNoDelay(boolean on) throws SocketException {
        baseSocket.setTcpNoDelay(on);
    }

    @Override
    public void setTrafficClass(int tc) throws SocketException {
        baseSocket.setTrafficClass(tc);
    }

    @Override
    public void shutdownInput() throws IOException {
        baseSocket.shutdownInput();
    }

    @Override
    public void shutdownOutput() throws IOException {
        baseSocket.shutdownOutput();
    }

    @Override
    public String toString() {
        return baseSocket.toString();
    }



    }

    public class MySocketFactory extends SocketFactory {

    private SocketFactory baseFactory;


    public MySocketFactory(SocketFactory baseFactory) {
        this.baseFactory = baseFactory;
    }


    @Override
    public Socket createSocket() throws IOException {
        return baseFactory.createSocket();
    }


    @Override
    public boolean equals(Object obj) {
        return baseFactory.equals(obj);
    }



    @Override
    public int hashCode() {
        return baseFactory.hashCode();
    }

    @Override
    public String toString() {
        return baseFactory.toString();
    }



    @Override
    public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
        return new MySocket(baseFactory.createSocket(string, i));
    }

    @Override
    public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
        return baseFactory.createSocket(string, i, ia, i1);
    }

    @Override
    public Socket createSocket(InetAddress ia, int i) throws IOException {
        return baseFactory.createSocket(ia, i);
    }

    @Override
    public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
        return baseFactory.createSocket(ia, i, ia1, i1);
    }

    }

...