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.
Related
I am stuck in a serious place..., I am trying to create a secure server. So I had to modify my code to be able to send in a secure way, the problem is the following.
It displays me the error: javax.net.ssl.SSLException: Connection has closed: javax.net.ssl.SSLException: Unsupported or unrecognized SSL message
I already made a certificate via keytool but I don't have the impression that it works
I have looked everywhere but I can't find a solution to my problem...
Here are my codes:
Server :
System.setProperty("javax.net.ssl.keyStore", "test.store");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
ServerSocketFactory serverSocketFactory =
ServerSocketFactory.getDefault();
ServerSocket serverSocket =
serverSocketFactory.createServerSocket(4444);
System.out.println("Started...");
while(true){
new ServerThread(serverSocket.accept()).start();
}
Client
System.setProperty("javax.net.ssl.trustStore", "test.store");
SocketFactory socketFactory = SSLSocketFactory.getDefault();
Socket socket = socketFactory.createSocket("localhost", 4444);
BufferedReader bf1 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("enter username");
writer.println(bf.readLine());
String message = null;
while(true){
System.out.println("enter message send to server");
message = bf.readLine();
if(message.equals("quit")){
socket.close();
break;
}
writer.println(message);
System.out.println("message from server : " + bf1.readLine());
}
I don't thing it is important to give you the serverThread cause it is not usefull.
Thank and have nice day :)
You server is not listening in SSL as you are calling the wrong factory. You would need SSLServerSocketFactory instead. Try
ServerSocketFactory serverSocketFactory = SSLServerSocketFactory.getDefault();
I have HttpsURLConnection opened.
How can i "connect" my ssl socket to this connection, means not to do another ssl handshake and don't change my ciphers.
URL url = new URL ("https://example.com:8080");
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.connect();
SSLSocketFactory ssf = connection.getSSLSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket("example.com", 8080);
// and here i want to send message with socket which will be in the same ssl session as https connection.
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
out.write("Hello");
out.flush();
out.close();
Is it really to do such thing in Java?
It should happen that way by default, as long as the session remains valid, and provided you haven't messed around with SSLContexts.
AFAIK, you cannot attach a new socket to the existing SSL session. If you want to send/read data on the original SSL session, use the original socket connection created by the URLConnection object. You can use the URLConnection.getInputStream() and URLConnection.getOutputStream() methods to get the input/output streams for it:
URL url = new URL ("https://example.com:8080");
HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.connect();
...
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
out.write("Hello");
out.flush();
out.close();
To create the certificates(pfx for C#, jks for Java) I followed this guide
C# client:
X509Certificate cert = new X509Certificate2(certPath, "pass");
TStreamTransport socket = new TTLSSocket(host, port, cert, (o, c, chain, errors) => true, null);
var transport = new TBufferedTransport(socket);
TProtocol protocol = new TBinaryProtocol(transport);
_client = new HfmConnectorService.Client(protocol);
_client.InputProtocol.Transport.Open();
Java Server:
TSSLTransportFactory.TSSLTransportParameters params = new TSSLTransportFactory.TSSLTransportParameters();
File keystoreFile = new File(System.getProperty("user.dir") + "\\keystore\\hfmcon.jks");
if(!keystoreFile.exists())
throw new IOException("Keystore file missing");
params.setKeyStore(keystoreFile.getPath(), "pass");
TServerSocket serverTransport = TSSLTransportFactory.getServerSocket(port, 10000, InetAddress.getByName("localhost"), params);
The client times out on this line
_client.InputProtocol.Transport.Open();
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.
My app is based on Sockets, but regular Sockets, not SSLSockets. Do I have to change all of them to SSL sockets to be able connect to https server?
You must change how the socket is created, by using the SSLSocketFactory, but you don't have to change all your datatypes from Socket to SSLSocket.
Yes i think you have to change for SSL, you can try that :
System.setProperty("javax.net.ssl.trustStore", "clienttrust");
SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory.getDefault();
Socket s = ssf.createSocket("127.0.0.1", 8888);
OutputStream outs = s.getOutputStream();
PrintStream out = new PrintStream(outs);
InputStream ins = s.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(ins));
out.println("Hi,How are u!");
out.println("");
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
out.close();
it's from : http://www.java2s.com/Tutorial/Java/0490__Security/HttpsSocketClient.htm
Hope it helps !