How to connect a SSL socket through a HTTP proxy? - java

I'm trying to use Java (Android) to connect to a server with a SSL socket. Please note that this is not HTTP data. This is proprietary protocol with a mix of text and binary data.
I want to relay that SSL connection through a HTTP proxy, but I am facing a lot of problems with that. Right now the scenario that I use and that my browser seems to use with a squid proxy is as follow
[client]->[http connection]->[proxy]->[ssl connection]->[server]
This works for the browser, because after the proxy makes the ssl connection, a TLS negotiation takes place immediately. However my code does not seem to do that.
final TrustManager[] trustManager = new TrustManager[] { new MyX509TrustManager() };
final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManager, null);
SSLSocketFactory factory = context.getSocketFactory();
Socket s = factory.createSocket(new Socket(proxy_ip, 3128), hostName, port, true);
The problem that I have is that createSocket NEVER RETURNS. With a wireshark dump from the proxy machine, I can see that a tcp handshake takes place between the proxy and the server. With dumps from web sessions, I can see that the client usually initiate a SSL handshake at this point, which does not happen in my scenario.
This is not a problem with the trust manager, because the certificate never gets back to me and it is never validated.
EDIT :
After discussion, this is the more complete version of the code I'm trying to run. This version above with the simple (new Socket(...)) as parameter is something I've tried later on.
The original version of the code I'm trying to debug throws
java.net.ConnectException: failed to connect to /192.168.1.100 (port 443): connect failed: ETIMEDOUT (Connection timed out)
The sequence is as follow (a bit simplified again) :
final Socket proxySocket = new Socket();
proxySocket.connect(proxyAddress, 2000); // 2 seconds as the connection timeout for connecting to the proxy server
[Start a thread and write to outputStream=socket.getOutputStream()]
final String proxyRequest = String.format("CONNECT %s:%d HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: %s:%d\r\n\r\n", hostName, port, hostName, port);
outputStream.close(); // Closing or not doesn't change anything
[Stop using that thread and let it exit by reaching the end of its main function]
Then read the response with the following code :
final InputStreamReader isr = new InputStreamReader(proxySocket.getInputStream());
final BufferedReader br = new BufferedReader(isr);
final String statusLine = br.readLine();
boolean proxyConnectSuccess = false;
// readLine consumed the CRLF
final Pattern statusLinePattern = Pattern.compile("^HTTP/\\d+\\.\\d+ (\\d\\d\\d) .*");
final Matcher statusLineMatcher = statusLinePattern.matcher(statusLine);
if (statusLineMatcher.matches())
{
final String statusCode = statusLineMatcher.group(1);
if (null != statusCode && 0 < statusCode.length() && '2' == statusCode.charAt(0))
{
proxyConnectSuccess = true;
}
}
// Consume rest of proxy response
String line;
while ( "".equals((line = br.readLine())) == false )
{
}
I can say that this code works because it works without SSL. The socket created here, proxySocket is the one that is passed to the createSocket function instead of just creating a new one on the fly like in my original example.

java.net.Proxy, or the https.proxyHost/proxyPort properties, only support HTTP proxying via HttpURLConnection, not via a Socket.
To make that work for an SSLSocket of your own, all you need to to is create a plaintext socket, issue an HTTP CONNECT command on it, check the response for 200, and then wrap it in an SSLSocket.
EDIT When sending the CONNECT command, you must not close the socket, of course; and when reading its reply you must not use a BufferedReader, otherwise you will lose data; either read the line by hand or use DataInputStream.readLine(), despite its deprecation. You also need to follow RFC 2616 entirely.

You have to use javax.net lib . you can archive to your target using javax.net.ssl.*.
I think you can get solution using oracle docs. Here is the link for that.
SSLSocketClientWithTunneling

Combine MacDaddy's answer and Viktor Mukhachev's comment, use SSLSocket over a Socket over a Proxy.
Code:
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SSLThroughProxy {
public static void main(String[] args) throws IOException {
final String REQUEST = "GET / HTTP/1.1\r\n" +
"Host: github.com\r\n" +
"Connection: close\r\n" +
"\r\n";
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("your-proxy-host", 8080));
Socket socket = new Socket(proxy);
InetSocketAddress address = new InetSocketAddress("github.com", 443);
socket.connect(address);
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, address.getHostName(), address.getPort(), true);
sslSocket.startHandshake();
sslSocket.getOutputStream().write(REQUEST.getBytes());
InputStream inputStream = sslSocket.getInputStream();
byte[] bytes = inputStream.readAllBytes();
System.out.println(new String(bytes, StandardCharsets.UTF_8));
sslSocket.close();
}
}

I don't have time to test/write a targetted solution now, however Upgrading socket to SSLSocket with STARTTLS: recv failed seems to cover the basic problem.
In your case, you need to connect to the proxy, issue a proxy connect, then upgrade the connection - essentially you CONNECT takes the place of the STARTTLS in the referenced question, and the check for " 670 " is not needed.

Related

Dart VM service client

I'm trying to get a simple TCP client going that can
connect to the Dart VM service, send a request and get a response.
I've tried it with and without connect().
The other question I have is what's the best Dart command
to "start" the VM service so that it's listening for requests?
With possibly giving what host and port to use, if needed.
Commands like --observe or --enable-vm-service are for the Observatory and I don't need that.
I've been using this
but I'm not sure what host and port it's using by default for that.
dart --pause_isolates_on_start bicycle.dart
So far, after I run the Dart command and run the client,
I get:
Exception: java.net.ConnectException: Connection refused: connect
or it seems to send the request, but it just gets back "-1".
So what Dart command should I be using and what changes do I need to
make to be able to get a response back from the VM service?
I need to be able to do the client in Java and not Dart.
UPDATE:
Is the problem that the Dart VM service is using WebSockets and that
isn't compatible with Java Socket I/O?
It's looking like it does need to connect via a WebSocket, so I'm
looking into trying to get that to build and try it.
Thanks!!
import java.net.Socket;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketAddress;
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class DartTest
{
public static void main(String[] args)
{
Socket socket = null;
InetAddress inetAddress = null;
InetSocketAddress inetSocketAddress = null;
OutputStream os = null;
InputStream is = null;
byte[] outBuf;
byte[] inBuf;
byte[] zeroDzeroAspaces = {(byte)0x0d, (byte)0x0a, (byte)0x20, (byte)0x20};
String sZeroDzeroAspaces = new String(zeroDzeroAspaces);
byte[] zeroDzeroA = {(byte)0x0d, (byte)0x0a};
String sZeroDzeroA = new String(zeroDzeroA);
int iAvail;
int iByte;
int iReadBytes;
StringBuffer readSb = new StringBuffer();
String sHost = "127.0.0.1";
int iPort = 8181;
try
{
//inetSocketAddress = new InetSocketAddress(sHost, iPort);
socket = new Socket(sHost, iPort);
//socket = new Socket();
//socket.connect((SocketAddress)inetSocketAddress);
os = socket.getOutputStream();
is = socket.getInputStream();
StringBuffer outSb = new StringBuffer();
outSb.append("{");
outSb.append(sZeroDzeroAspaces);
outSb.append((char)0x22);
outSb.append("jsonrpc");
outSb.append((char)0x22);
outSb.append(": ");
outSb.append((char)0x22);
outSb.append("2.0");
outSb.append((char)0x22);
outSb.append(",");
outSb.append(sZeroDzeroAspaces);
outSb.append((char)0x22);
outSb.append("method");
outSb.append((char)0x22);
outSb.append(": ");
outSb.append((char)0x22);
outSb.append("getVersion");
outSb.append((char)0x22);
outSb.append(",");
outSb.append(sZeroDzeroAspaces);
outSb.append((char)0x22);
outSb.append("params");
outSb.append((char)0x22);
outSb.append(": {},");
outSb.append(sZeroDzeroAspaces);
outSb.append((char)0x22);
outSb.append("id");
outSb.append((char)0x22);
outSb.append(": ");
outSb.append((char)0x22);
outSb.append("1");
outSb.append((char)0x22);
outSb.append(sZeroDzeroA);
outSb.append("}");
//System.out.println("outSb: '"+outSb.toString()+"'");
outBuf = outSb.toString().getBytes();
os.write(outBuf);
os.flush();
while ( true )
{
iByte = is.read();
System.out.println("iByte: "+iByte);
if ( iByte == -1 )
break;
readSb.append((char)iByte);
}
System.out.println("readSb: '"+readSb.toString()+"'");
if ( socket != null )
socket.close();
}
catch (Exception e)
{
System.out.println("Exception: "+e.toString());
}
}
}
Answering my own question:
Using WebSockets was the main thing, trying to connect to
the VM service/Observatory using "regular" Socket TCP
wasn't going to work.
Then the next roadblock came when the WebSocket server handshake
was failing when it would return "200 OK" instead of "101" to "Upgrade".
Then I was looking at the intelliJ plugin code, to see how they connected, and they used "ws://localhost:8181/ws", I was trying to use
"ws://localhost:8181", and once I used that, the handshake went through!!
When it worked I was using:
dart --enable-vm-service --pause_isolates_on_start bicycle.dart
I'm not sure who I'm actually connected to, the Observatory,
or the VM service, but at least I've finally gotten to the point
where I can try to send a request, and possibly get a reply.

Java server socket response to previouse connected client [duplicate]

I'm trying to implement a TCP connection, everything works fine from the server's side but when I run the client program (from client computer) I get the following error:
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:432)
at java.net.Socket.connect(Socket.java:529)
at java.net.Socket.connect(Socket.java:478)
at java.net.Socket.<init>(Socket.java:375)
at java.net.Socket.<init>(Socket.java:189)
at TCPClient.main(TCPClient.java:13)
I tried changing the socket number in case it was in use but to no avail, does anyone know what is causing this error & how to fix it.
The Server Code:
//TCPServer.java
import java.io.*;
import java.net.*;
class TCPServer {
public static void main(String argv[]) throws Exception {
String fromclient;
String toclient;
ServerSocket Server = new ServerSocket(5000);
System.out.println("TCPServer Waiting for client on port 5000");
while (true) {
Socket connected = Server.accept();
System.out.println(" THE CLIENT" + " " + connected.getInetAddress()
+ ":" + connected.getPort() + " IS CONNECTED ");
BufferedReader inFromUser = new BufferedReader(
new InputStreamReader(System.in));
BufferedReader inFromClient = new BufferedReader(
new InputStreamReader(connected.getInputStream()));
PrintWriter outToClient = new PrintWriter(
connected.getOutputStream(), true);
while (true) {
System.out.println("SEND(Type Q or q to Quit):");
toclient = inFromUser.readLine();
if (toclient.equals("q") || toclient.equals("Q")) {
outToClient.println(toclient);
connected.close();
break;
} else {
outToClient.println(toclient);
}
fromclient = inFromClient.readLine();
if (fromclient.equals("q") || fromclient.equals("Q")) {
connected.close();
break;
} else {
System.out.println("RECIEVED:" + fromclient);
}
}
}
}
}
The Client Code:
//TCPClient.java
import java.io.*;
import java.net.*;
class TCPClient {
public static void main(String argv[]) throws Exception {
String FromServer;
String ToServer;
Socket clientSocket = new Socket("localhost", 5000);
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(
System.in));
PrintWriter outToServer = new PrintWriter(
clientSocket.getOutputStream(), true);
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
while (true) {
FromServer = inFromServer.readLine();
if (FromServer.equals("q") || FromServer.equals("Q")) {
clientSocket.close();
break;
} else {
System.out.println("RECIEVED:" + FromServer);
System.out.println("SEND(Type Q or q to Quit):");
ToServer = inFromUser.readLine();
if (ToServer.equals("Q") || ToServer.equals("q")) {
outToServer.println(ToServer);
clientSocket.close();
break;
} else {
outToServer.println(ToServer);
}
}
}
}
}
This exception means that there is no service listening on the IP/port you are trying to connect to:
You are trying to connect to the wrong IP/Host or port.
You have not started your server.
Your server is not listening for connections.
On Windows servers, the listen backlog queue is full.
I would check:
Host name and port you're trying to connect to
The server side has managed to start listening correctly
There's no firewall blocking the connection
The simplest starting point is probably to try to connect manually from the client machine using telnet or Putty. If that succeeds, then the problem is in your client code. If it doesn't, you need to work out why it hasn't. Wireshark may help you on this front.
You have to connect your client socket to the remote ServerSocket. Instead of
Socket clientSocket = new Socket("localhost", 5000);
do
Socket clientSocket = new Socket(serverName, 5000);
The client must connect to serverName which should match the name or IP of the box on which your ServerSocket was instantiated (the name must be reachable from the client machine). BTW: It's not the name that is important, it's all about IP addresses...
I had the same problem, but running the Server before running the Client fixed it.
One point that I would like to add to the answers above is my experience-
"I hosted on my server on localhost and was trying to connect to it through an android emulator by specifying proper URL like http://localhost/my_api/login.php . And I was getting connection refused error"
Point to note - When I just went to browser on the PC and use the same URL (http://localhost/my_api/login.php) I was getting correct response
so the Problem in my case was the term localhost which I replaced with the IP for my server (as your server is hosted on your machine) which made it reachable from my emulator on the same PC.
To get IP for your local machine, you can use ipconfig command on cmd
you will get IPv4 something like 192.68.xx.yy
Voila ..that's your machine's IP where you have your server hosted.
use it then instead of localhost
http://192.168.72.66/my_api/login.php
Note - you won't be able to reach this private IP from any node outside this computer. (In case you need ,you can use Ngnix for that)
I had the same problem with Mqtt broker called vernemq.but solved it by adding the following.
$ sudo vmq-admin listener show
to show the list o allowed ips and ports for vernemq
$ sudo vmq-admin listener start port=1885 -a 0.0.0.0 --mountpoint /appname --nr_of_acceptors=10 --max_connections=20000
to add any ip and your new port. now u should be able to connect without any problem.
Hope it solves your problem.
Hope my experience may be useful to someone. I faced the problem with the same exception stack trace and I couldn't understand what the issue was. The Database server which I was trying to connect was running and the port was open and was accepting connections.
The issue was with internet connection. The internet connection that I was using was not allowed to connect to the corresponding server. When I changed the connection details, the issue got resolved.
In my case, I gave the socket the name of the server (in my case "raspberrypi"), and instead an IPv4 address made it, or to specify, IPv6 was broken (the name resolved to an IPv6)
In my case, I had to put a check mark near Expose daemon on tcp://localhost:2375 without TLS in docker setting (on the right side of the task bar, right click on docker, select setting)
i got this error because I closed ServerSocket inside a for loop that try to accept number of clients inside it (I did not finished accepting all clints)
so be careful where to close your Socket
I had same problem and the problem was that I was not closing socket object.After using socket.close(); problem solved.
This code works for me.
ClientDemo.java
public class ClientDemo {
public static void main(String[] args) throws UnknownHostException,
IOException {
Socket socket = new Socket("127.0.0.1", 55286);
OutputStreamWriter os = new OutputStreamWriter(socket.getOutputStream());
os.write("Santosh Karna");
os.flush();
socket.close();
}
}
and
ServerDemo.java
public class ServerDemo {
public static void main(String[] args) throws IOException {
System.out.println("server is started");
ServerSocket serverSocket= new ServerSocket(55286);
System.out.println("server is waiting");
Socket socket=serverSocket.accept();
System.out.println("Client connected");
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=reader.readLine();
System.out.println("Client data: "+str);
socket.close();
serverSocket.close();
}
}
I changed my DNS network and it fixed the problem
You probably didn't initialize the server or client is trying to connect to wrong ip/port.
Change local host to your ip address
localhost
//to you local ip
192.168.xxxx
I saw the same error message ""java.net.ConnectException: Connection refused" in SQuirreLSQL when it was trying to connect to a postgresql database through an ssh tunnel.
Example of opening tunel:
Example of error in Squirrel with Postgresql:
It was trying to connect to the wrong port. After entering the correct port, the process execution was successful.
See more options to fix this error at: https://stackoverflow.com/a/6876306/5857023
In my case, with server written in c# and client written in Java, I resolved it by specifying hostname as 'localhost' in the server, and '[::1]' in the client. I don't know why that is, but specifying 'localhost' in the client did not work.
Supposedly these are synonyms in many ways, but apparently, not not a 100% match. Hope it helps someone avoid a headache.
For those who are experiencing the same problem and use Spring framework, I would suggest to check an http connection provider configuration. I mean RestTemplate, WebClient, etc.
In my case there was a problem with configured RestTemplate (it's just an example):
public RestTemplate localRestTemplate() {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", <some port>));
SimpleClientHttpRequestFactory clientHttpReq = new SimpleClientHttpRequestFactory();
clientHttpReq.setProxy(proxy);
return new RestTemplate(clientHttpReq);
}
I just simplified configuration to:
public RestTemplate restTemplate() {
return new RestTemplate(new SimpleClientHttpRequestFactory());
}
And it started to work properly.
There is a service called MySQL80 that should be running to connect to the database
for windows you can access it by searching for services than look for MySQL80 service and make sure it is running
It could be that there is a previous instance of the client still running and listening on port 5000.

How can a Java Socket read() take a long time when it returns -1 and is finished?

I am doing some benchmarks on a HTTP server. To avoid potential conflict with HTTP libraries, I open the connection directly using a Socket, with no HTTP persistent connections.
The Java code opens and connects an InputStream on a socket connected to a loaded HTTP server. It is running on Linux.
I see that that either of these could happen:
The socket connect (new Socket()) could take long. This makes sense if the server has a backlog in accepting new connections.
The socket connect is fast but the delay, up to 1500 milliseconds, is in the last read when read() returns -1 to signify the stream is at the end of "the file." This I do not understand.
The code follows the standard, with some timing code added:
final byte[] buffer = new byte[8192];
int size = inputStream.read(buffer);
while (size > 0) {
// Copy the buffer
size = inputStream.read(buffer);
}
You are calling read after you've already received all the data the server is going to send. This is a mistake. Once you've received the full reply (as determined by the HTTP protocol), you should stop calling read. You are waiting for the connection to timeout, which is silly.
The HTTP protocol tells you when you have received the full reply, not the TCP protocol. You are expecting the TCP stack to understand HTTP. It does not. It has no idea when you've received a full reply.
The socket connect is fast but the delay, up to 1500 milliseconds, is in the last read when read() returns -1 to signify the stream is at the end of "the file." This I do not understand.
Assuming that the scenario is exactly as you've explained; i.e. that you simply connected to port 80 and tried to read data:
The server is most likely waiting for your client code to send a request.
The server times out the read after 1.5 seconds, and then it closes both sides of its socket.
That results in the client-side socket seeing an EOF ... which is signalled to your code by returning -1 from the read(...) call.
Basically, your code has to send a well-formed HTTP request to the server if you expect the server to send you a response.
On the other hand, if your code did send a well-formed HTTP request, then the behaviour could be caused by your code trying to read more data that the server has to send ... combined with sending a Request that specified a persistent connection (see HTTP 1.1 spec, section 8.1).
I had a similar problem when receiving a response with a JSON body. After several experiments, I detected that program hanging while reading the body. So, I solved this, by reading Content-Length from the header, stopping after the blank line ( which between header and body ), and reading only necessary characters after that. Here is code:
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class TelegramSocketClient {
private final String CONTENT_LEN = "Content-Length:";
Socket clientSocket;
SSLSocket sslSocket;
PrintWriter out;
BufferedReader in;
public void startSSLConnection(String ip, int port) throws IOException {
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory
.getDefault();
sslSocket = (SSLSocket) factory.createSocket(ip, port);
out = new PrintWriter(sslSocket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(sslSocket.getInputStream(), StandardCharsets.UTF_8));
}
public String sendMessage(String msg) throws IOException {
out.println(msg);
String line = null;
int contentLen = 0;
while ((line = in.readLine()) != null && !line.isEmpty()) {
System.out.println(line);
if(line.startsWith(CONTENT_LEN)) {
contentLen = Integer.parseInt(line.substring(CONTENT_LEN.length() +1, line.length()));
}
}
char [] buff = new char[contentLen];
in.read(buff, 0, buff.length);
return new String(buff, 0, buff.length);
}
public void stopConnection() throws IOException {
in.close();
out.close();
clientSocket.close();
}
}

Add a timeout when creating a new Socket

I have a local network with DHCP and a few PCs. One of these should be my Server and get automatically connected to all others (clients). My idea was this:
First, I create a server on every client (CServer) that is listening for a client programm from the server (SClient). When the SClient connects to a CServer, the SClient sends the CServer his IP, so he knows there will be the server on this IP. Then after trying all IPs in his IP range (e.g. 192.168.1.xxx), he starts the real server and all the clients connect to the known server IP.
But when I try the following, the SClient just freezes at the first IP, when trying to connect to 192.168.1.0. How can i define a timeout or something similar that lets the SClient drop the unsuccessful connection and going on with 192.168.1.1?
import java.lang.*;
import java.io.*;
import java.net.*;
class SClient {
public SClient() {
for(int i = 120; i < 125; i++){
try{
InetAddress addr = InetAddress.getLocalHost();
String addrs = addr+"";
String ip = addrs.substring(addrs.indexOf("/")+1);
Socket s1 = new Socket("192.168.1." + i, 1254);
OutputStream s1out = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream (s1out);
dos.writeUTF(ip);
dos.close();
s1out.close();
s1.close();
}catch(IOException e){}
}
}
}
and
import java.lang.*;
import java.io.*;
import java.net.*;
class CServer {
public CServer() throws IOException{
ServerSocket s = new ServerSocket(1254);
while(true){
Socket s1=s.accept();
InputStream s1In = s1.getInputStream();
DataInputStream dis = new DataInputStream(s1In);
String st = new String (dis.readUTF());
System.out.println(st);
dis.close();
s1In.close();
s1.close();
}
}
}
I've found a solution for my problem. It was just initializing the Socket not with
Socket s1 = new Socket("192.168.1." + i, 1254);
but with
Socket s1 = new Socket();
s1.setSoTimeout(200);
s1.connect(new InetSocketAddress("192.168.1." + i, 1254), 200);
Thanks anyway!
It's much easier to do this with UDP. The general logic would be:
Identify a well known port for 'discovery'
Any machine that starts up sends out a 'Query Master Server' message
If a response is not received to that message within a time frame
you define, then the machine that sent it automatically designates
itself as being the server.
Henceforth, any machine that sends out a 'Query Master Server'
message will get a response back from the master, with its IP
address and a 'communication port'
Connect from the new machine to the server on the communication port
and start sending messages.
You might run into situations where more than one server thinks it is the master in this scenario, and then you would need a conflict resolution process, but the outline should give you a general idea of a process that will work for you.

Connect to FTPS server

I write FTPS server, and I have problems with ssl connection after AUTH TLS command.
Simple example:
try
{
int ServerPort = 21;
ServerSocket FtpExServer = new ServerSocket(ServerPort);
while(true)
{
Socket S = FtpExServer.accept();
InputStreamReader ISR = new InputStreamReader(S.getInputStream());
OutputStreamWriter OSW = new OutputStreamWriter(S.getOutputStream());
BufferedReader ClientSocketReader = new BufferedReader(ISR);
PrintWriter ClientSocketWriter = new PrintWriter(OSW, true);
ClientSocketWriter.println("220 Welcome to FTP server.");
print(ClientSocketReader.readLine());
ClientSocketWriter.println("234 AUTH TLS successful");
char[] passphrase = "pass".toCharArray();
char[] cpassphrase = "cpass".toCharArray();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream("keystore.jks"), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, cpassphrase);
SSLContext context = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = kmf.getKeyManagers();
context.init(keyManagers, null, null);
SSLServerSocketFactory ssf = context.getServerSocketFactory();
SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(990);
ss.setSoTimeout(2000);
SSLSocket s = (SSLSocket)ss.accept();
ISR = new InputStreamReader(s.getInputStream());
OSW = new OutputStreamWriter(s.getOutputStream());
ClientSocketReader = new BufferedReader(ISR);
ClientSocketWriter = new PrintWriter(OSW, true);
ClientSocketWriter.println("234 AUTH TLS successful");
print(ClientSocketReader.readLine());
ClientSocketWriter.println("331 Password required for smie");
print(ClientSocketReader.readLine());
ClientSocketWriter.println("230 User smie logged in");
print(ClientSocketReader.readLine());
ClientSocketWriter.println("215 UNIX Type: L8");
print(ClientSocketReader.readLine());
ClientSocketWriter.println("550 Command not suported.");
}
}
catch(Exception e)
{
print(e);
}
Description: FTP client(for example MoveITFreely) connect to server on port 21. After send command "AUTH TLS", server send "234 AUTH TLS successful". Now client must to connect to server on port 990(?), but client dont connect and get timeout exception.
What i do wrong?
There exist two methods to add SSL to FTP.
First method is called implicit SSL. It means that the server is listening on port 990 and when the client connects to it, first SSL/TLS negotiation is performed, and then the established connection is used as a command channel for communication (for data channel SSL handshake is also performed in a similar manner).
Second method is what you attempt to use. It's called explicit SSL. The client connects on port 21, sends AUTH TLS and starts SSL negotiation on existing connection. Data channel can be secured or not secured depending on how you want it (you specify this using PROT command).
You mixed the methods. I suggest that you read detailed explanation in Wikipedia before going further. Then read RFC for explicit TLS.
Update: Also you'd need SSLClientSocket, not SSLServerSocket.

Categories