在Java中混淆​​SSL客户端Hello v2消息

时间:2011-07-18 20:41:49

标签: java ssl client-server obfuscation jsse

我目前正在开发一个客户端/服务器TLS工具,它要求我们通过防火墙进行连接。 由于我们无法控制的原因,我们只被授予传出TCP连接。

问题是我们客户的防火墙会阻止客户端Hello v2消息(可能还有整个SSL握手)。

有没有办法以某种方式对流进行模糊处理? 我正在考虑尝试使用压缩来使流不可读到防火墙。 (也许使用JDK7的GzipOutputStream,现在可以进行syncFlush刷新)

我不是SSL专家,但在我看来,应该可以翻译整个流,这样防火墙就无法获取连接并阻止它。

据我所知,有几种(两种?)方法可以解决这个问题:

  • 覆盖默认实施
  • 实施SSLServerSocketFactory

第一个选项对我来说没有用,因为我无法找到com.sun.net.ssl.internal.ssl.SSLServerSocketFactoryImpl的源代码,这是默认实现。 我确实为它浏览了openJDK源代码,但即便如此,这些消息来源似乎也不见了。

实现SSLServerSocketFactory超出了我的能力范围。正如我所说,我不是SSL专家。

请注意,该应用程序通过其他不太激进的防火墙/防火墙规则可以正常工作。

4 个答案:

答案 0 :(得分:4)

压缩加密流是没有用的,实际上只需要一些屏蔽来避开防火墙。

在客户端,您可以使用SSLSocketFactory的方法createSocket(socket, host, port, autoclose)创建基于另一个套接字的SSL套接字 - 这个另一个套接字可以获得特殊的SocketImpl实现,执行简单的XOR - 掩盖前几个字节。

在服务器端,它更复杂,因为SSLServerSocketFactory没有这样的方法。

answer to Java RMI + SSL + Compression = IMPOSSIBLE!中,我描述了如何构建一个委托的Socket工厂。它是为Rmi(客户端服务器)SocketFactory完成的,但对于ServerSocketFactory或SocketFactory它将以类似的方式工作。


但当然可能是您的防火墙实际上并未阻止SSL流量,而是阻止任何未列入白名单的内容(如HTTP)。在构建包装套接字实现之前,请尝试使用简单的socket + serversocket发送一些随机数据并将其接收回来。

答案 1 :(得分:2)

可以通过HTTP隧道传输TCP,无需额外开发。有各种工具。看看GNU httptunnel。 httptunnel创建在HTTP请求中隧道传输的双向虚拟数据连接。如果需要,可以通过HTTP代理发送HTTP请求。Httpc也非常有趣。

答案 2 :(得分:2)

也许稍微偏离主题,但如果问题是关于SSLv2的,并且如果SSLv3或TLS 1.x握手工作正常,则可以使用SSLSocket.setEnabledProtocols(new String[] { "SSLv3", "TLSv1", "TLSv1.1" })禁用V2 ClientHello。 / p>

请参阅JSSE Reference guide (section on SSLContext)

编辑:对于那些没有阅读评论的人,这里有@ EJP答案的链接,其中包含有关此主题的更多详细信息:Why does Java's SSLSocket send a version 2 client hello?

答案 3 :(得分:1)

似乎解决方案是结合布鲁诺的建议和保罗的解决方案。

Paulo的解决方案允许我们使用委托自定义SSLSocket或SSLServerSocket的行为。

Bruno的建议允许我们告诉默认的SSL实现使用我们修改的SSLSocket或SSLServerSocket。

这是我做的:

  • 创建委托ServerSocket类(MyServerSocket)
  • 创建委托ServerSocketFactory类(MyServerSocketFactory)
  • 创建委托SocketFactory类(MySocketFactory)
  • 创建委托Socket类(MySocket)
  • 创建XorInputStream(找到它here
  • 创建XorOutputStream(找到它here

在服务器端:

    // 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);
    }

    }