我目前正在开发一个客户端/服务器TLS工具,它要求我们通过防火墙进行连接。 由于我们无法控制的原因,我们只被授予传出TCP连接。
问题是我们客户的防火墙会阻止客户端Hello v2消息(可能还有整个SSL握手)。
有没有办法以某种方式对流进行模糊处理? 我正在考虑尝试使用压缩来使流不可读到防火墙。 (也许使用JDK7的GzipOutputStream,现在可以进行syncFlush刷新)
我不是SSL专家,但在我看来,应该可以翻译整个流,这样防火墙就无法获取连接并阻止它。
据我所知,有几种(两种?)方法可以解决这个问题:
第一个选项对我来说没有用,因为我无法找到com.sun.net.ssl.internal.ssl.SSLServerSocketFactoryImpl的源代码,这是默认实现。 我确实为它浏览了openJDK源代码,但即便如此,这些消息来源似乎也不见了。
实现SSLServerSocketFactory超出了我的能力范围。正如我所说,我不是SSL专家。
请注意,该应用程序通过其他不太激进的防火墙/防火墙规则可以正常工作。
答案 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。
这是我做的:
// 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);
}
}