SSLSocket of HttpsURLConnection - java

I have a JEditorPane which loads a website over SSL/TLS. My goal is to get (with reflection) specific information of the SSL connection like the finished message of the SSL handshake. The only thing I get from JEditorPane is the URL and thus the HttpsURLConnection.
But how do I get the SSLSocket of the HttpsURLConnection? Any ideas?

There is no direct way to get it. But you can control the SSL socket used by the HttpsURLConnection. You can set the socket factory in the httppsURLConnection.setSSLSocketFactory().
You can create a custom socket factory which creates your owns decorated sslsocket and return it. This will be the socket used by your HttpsURLConnection.
Note that u need to do this before the connection is established.
SSLSocketFactory sslSktFactory = SSLContext.getInstance("TLS").getSocketFactory();
httpsUrlConnection.setSSLSocketFactory(new CustomSSLSocketFactory(sslSktFactory ));
A sample custom SSL socket factory is below
class CustomSSLSocketFactory extends SSLSocketFactory {
SSLSocketFactory factory = null;
CustomSSLSocketFactory(SSLSocketFactory factory) {
this.factory = factory;
}
#Override
public Socket createSocket(Socket s, String host, int port,
boolean autoClose) throws IOException {
Socket skt = factory.createSocket(s, host, port, autoClose);
return customizeSSLSocket(skt);
}
#Override
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(String host, int port) throws IOException,
UnknownHostException {
Socket skt = factory.createSocket(host, port);
return customizeSSLSocket(skt);
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket skt = factory.createSocket(host, port);
return customizeSSLSocket(skt);
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
Socket skt = factory.createSocket(host, port, localHost, localPort);
return customizeSSLSocket(skt);
}
#Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
Socket skt = factory.createSocket(address, port, localAddress, localPort);
return customizeSSLSocket(skt);
}
private Socket customizeSSLSocket(Socket skt) throws SocketException {
((SSLSocket)skt).addHandshakeCompletedListener(
new HandshakeCompletedListener() {
public void handshakeCompleted(
HandshakeCompletedEvent event) {
System.out.println("Handshake finished!");
System.out.println(
"\t CipherSuite:" + event.getCipherSuite());
System.out.println(
"\t SessionId " + event.getSession());
System.out.println(
"\t PeerHost " + event.getSession().getPeerHost());
System.out.println(
"\t PeerHost " + event.getSession().getProtocol());
}
}
);
return skt;
}

Related

Java: Start a socket server class with the port as an argument

So i have a load balancer and when a server is full of clients i want to create a new multithread server programmatically by passing the server port as an argument.
This is how im trying to start a new server instance
int newport = 4001
SMTPserver server = new SMTPserver();
server.SMTPserver(port);
this is my server
public class SMTPserver {
public static Socket connsock = null;
public static int port;
// SMTPserver(int port) {
// this.port = port;
// }
public static void main(String args[]) throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server is running on port " + port);
while (true) {
try {
// accepting client socket
connsock = serverSocket.accept();
}
}
}
}
my question is how to start this server with the giver port argument? is this code correct?
You are passing 0 to the ServerSocket constructor, so it will choose an available port. You need to pass a non zero port number if you want to use a specific port.
You could do it like this:
public class SMTPserver {
public Socket connsock = null;
public int port;
public SMTPserver(int port) {
this.port = port;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server is running on port " + port);
while (true) {
try {
// accepting client socket
connsock = serverSocket.accept();
}
}
}
}
Notice that I'm assigning the port parameter to the port field, and then passing it to the ServerSocket constructor.

Very simple UDP server/client on Android wont work

I'm trying to do a very simple client on android to send an UDP message to a server on Android.
Here's my simple UDP client:
public class MyUDPClient {
private static String TAG = "MyUDPClient";
private DatagramSocket udpSocket;
private InetAddress serverAddress;
private int port;
private Scanner scanner;
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
public void send(String message) throws IOException {
DatagramPacket p = new DatagramPacket(
message.getBytes(), message.getBytes().length, serverAddress, port);
Log.d(TAG, "udp client send: " + message + " to serverAddress " + serverAddress);
this.udpSocket.send(p);
}
public void connect() {
Log.d(TAG, "UDP connection to port " + port);
}
}
And here's my simple UDP server:
public class MyUDPServer {
private String TAG = "MyUDPServer";
private int port;
private DatagramSocket udpSocket;
OnMessageCallback onMessageCallback;
public MyUDPServer(int port) throws SocketException {
this.udpSocket = new DatagramSocket(port);
this.port = port;
}
public interface OnMessageCallback {
public void on(String message);
}
private void listen() throws Exception {
Log.d(TAG, "listening UDP server on port " + port);
String msg;
while (true) {
byte[] buf = new byte[256];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// blocks until a packet is received
udpSocket.receive(packet);
msg = new String(packet.getData()).trim();
Log.d(TAG, "message received; " + msg);
onMessageCallback.on(msg);
}
}
public void start() throws Exception {
listen();
}
public void setMessageCallback(OnMessageCallback onMessageCallback) {
this.onMessageCallback = onMessageCallback;
}
}
The problem is that the server never receives any messages. I start the client like this:
MyUDPClient myUDPClient = new MyUDPClient("192.168.1.6", 8887);
Since it's on an emulator, I try to filter the IP 192.168.1.6 and even though I call myUDPClient.send("message"), on WireShark I receive nothing, which indicates that the message is not even leaving the computer where the emulator tuns.
What am I doing wrong?
I've created a WebSocket client/server on Android and it worked fine.
I have these 2 permissions:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
In your MyUDPClient-class simply change to avoid a "double" binding that causes the exception:
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
super();
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
to
public MyUDPClient(String destinationAddr, int port) throws SocketException, UnknownHostException {
super();
this.serverAddress = InetAddress.getByName(destinationAddr);
this.port = port;
udpSocket = new DatagramSocket();
//udpSocket = new DatagramSocket(this.port);
scanner = new Scanner(System.in);
}
Problem was that I was gatting NetworkOnMainThreadException but it was being logged to another part of the logcat that I wasn't filtering for.
Doing the myudpClient.send("myMessage") from a separate thread solved the problem. That's why I couldn't even see the output on Wireshark, it wasn't even sending.

How to let Jersey client use the defaultSSLSocketFactory set in HttpsURLConnection?

I have a project in which HttpsURLConnection is configed to use a customized TrustManager as following:
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new MyTrustManager()}, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
There is a REST API client in this project, it uses Jersey client to send HTTP/HTTPS request:
Client client = ClientBuilder.newClient();
However, the HTTPS connection initiated by this Jerset client does not use the defaultSSLSocketFactory I set in HttpsURLConnection and it fails to connect to untrusted HTTPS url.
I need to explicitly set the SslContext on this client to make it work with my TrustManager.
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new MyTrustManager()}, null);
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
Is there any way to solve this issue?
Thanks.
The solution I eventually found is to set the SSLSocketFactory provider property to a customized SSLSocketFactory. Hope this can help others who have similar issues.
Call this in beginning of the program:
Security.setProperty("ssl.SocketFactory.provider", MySSLSocketFactory.class.getCanonicalName());
Here is how MySSLSocketFactory looks like (it also sets connection timeout):
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLContext sslContext = SSLContext.getInstance(Const.Ssl.PROTOCOL_SSL);
public MySSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
this.sslContext.init(
null,
new TrustManager[] { new MyTrustManager(false) },
new SecureRandom());
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException {
socket.connect(new InetSocketAddress(host, port), Const.Ssl.CONNECT_TIMEOUT);
socket.setSoTimeout(Const.Ssl.DATA_TIMEOUT);
return this.sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
#Override
public String[] getDefaultCipherSuites() {
return this.sslContext.getSocketFactory().getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return this.sslContext.getSocketFactory().getSupportedCipherSuites();
}
#Override
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
return this.createSocket(new Socket(), host, port, true);
}
#Override
public Socket createSocket(InetAddress address, int port)
throws IOException {
return this.createSocket(new Socket(), address.getHostAddress(), port, true);
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return this.createSocket(new Socket(), host, port, true);
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return this.createSocket(new Socket(), address.getHostAddress(), port, true);
}

SSL Handshake failed Android 5.1

Since Android 5.0.2 or 5.1.1 on Samsung devices my Android app receive error message when connecting web interface of old router "FRITZ!Box 7170".
javax.net.ssl.SSLProtocolException: SSL handshake aborted:
ssl=0xaecc7e00: Failure in SSL library, usually a protocol error
error:14082174:SSL routines:SSL3_CHECK_CERT_AND_ALGORITHM:got Channel
ID before a ccs (external/openssl/ssl/s3_clnt.c:3632
0xaf0e1679:0x00000000)
If I connect same interface with Firefox Browser:
ssl_error_weak_server_ephemeral_dh_key
I think it is because of unsafe Diffie-Hellman key length?
How to avoid this? I am using HTTPClient to make the connection.
I have a same problem.
The reason was a samsung security update, which has change a default cipher suite array given by SSLSocketFactory. Apropos if you take a nexus device with android M on it, you will see this error message
ssl_error_weak_server_ephemeral_dh_key
On samsung devices it is a
SSL3_CHECK_CERT_AND_ALGORITHM
The solution for me was to override cipher suites array. Here is my SSLSocketFactory, which I use to create ssl sockets.
public class SpeedportSSLSocketFactory extends SSLSocketFactory {
private final static Logger logger = Logger.getLogger(SpeedportSSLSocketFactory.class);
/**
* the order of ciphers in this list is important here e.g. TLS_DHE_* must not stay above TLS_RSA_*
*/
private static final String[] APPROVED_CIPHER_SUITES = new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
};
private SSLSocketFactory factory;
public SpeedportSSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{
// accepts certs with valid but expired key chain (incl. root cert)
new ExpiredSpeedportTrustManager()}, new java.security.SecureRandom());
factory = sslcontext.getSocketFactory();
} catch (Exception ex) {
logger.error("Cannot create SpeedportSSLSocketFactory", ex);
}
}
// dirty
private void injectHostname(InetAddress address, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(address, host);
} catch (Exception ignored) {
logger.error("Cannot inject hostName");
}
}
public static SocketFactory getDefault() {
return new SpeedportSSLSocketFactory();
}
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return factory.createSocket(socket, host, port, autoClose);
}
public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(addr, port, localAddr, localPort);
}
public Socket createSocket(InetAddress inaddr, int i) throws IOException {
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(host, port, localAddr, localPort);
}
public Socket createSocket(String host, int port) throws IOException {
InetAddress addr = InetAddress.getByName(host);
injectHostname(addr, host);
Socket socket = factory.createSocket(addr, port);
((SSLSocket) socket).setEnabledCipherSuites(getSupportedCipherSuites());
return socket;
}
#Override
public String[] getDefaultCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
#Override
public String[] getSupportedCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
}
Last two methods override default cipher suite. I am not sure, what you need override both.
The order in cipher suites array is also very important

HTTPS POST using HttpClient API opening 2 sockets

After a lot of R&D and googling, not able to troubleshoot my problem.
Environment Setup
Web Server (Tomcat 6.0.20) --> Proxy Server (Windows Server 2007) --> Thirdy part host
We have application, which does online payment transaction, after completion of this transaction, we want to send status of transaction to third party server. So posting data to third part server from our web server is opening 2 sockets for one transaction at proxy server, but when we check at web server it has created only one socket. SO why 2 socket at proxy server.
Below is my sample code
import javax.net.ssl.*;
import javax.net.SocketFactory;
import java.net.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Hashtable;
import java.math.BigInteger;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.protocol.*;
public class HTTPPostDemo {
private String privateKey;
private String host;
private int port;
private String userName;
private Header[] headers = null;
public class MySSLSocketFactory implements SecureProtocolSocketFactory {
private TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
return trustAllCerts;
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
return socketFactory.createSocket(host, port);
} catch (Exception ex) {
throw new UnknownHostException("Problems to connect " + host + ex.toString());
}
}
public Socket createSocket(Socket socket, String host, int port, boolean flag) throws IOException, UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
return socketFactory.createSocket(host, port);
} catch (Exception ex) {
throw new UnknownHostException("Problems to connect " + host + ex.toString());
}
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
return socketFactory.createSocket(host, port, clientHost, clientPort);
} catch (Exception ex) {
throw new UnknownHostException("Problems to connect " + host + ex.toString());
}
}
}
public SslClient(String host, int port, String userName, String privateKey) {
this.host = host;
this.port = port;
this.userName = userName;
this.privateKey = privateKey;
}
protected String md5Sum(String str) {
String sum = new String();
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
sum = String.format("%032x", new BigInteger(1, md5.digest(str.getBytes())));
} catch (Exception ex) {
}
return sum;
}
public String getSignature(String xml) {
return md5Sum(md5Sum(xml + privateKey) + privateKey);
}
public String sendRequest(String xml) throws Exception {
HttpClient client = new HttpClient();
client.setConnectionTimeout(60000);
client.setTimeout(60000);
String response = new String();
String portStr = String.valueOf(port);
Protocol.registerProtocol("https", new Protocol("https", new MySSLSocketFactory(), port));
String signature = getSignature(xml);
String uri = "https://" + host + ":" + portStr + "/";
PostMethod postRequest = new PostMethod(uri);
postRequest.addRequestHeader("Content-Length", String.valueOf(xml.length()));
postRequest.addRequestHeader("Content-Type", "text/xml");
postRequest.addRequestHeader("X-Signature", signature);
postRequest.addRequestHeader("X-Username", userName);
postRequest.setRequestBody(xml);
System.out.println("Sending https request....." + postRequest.toString());
try {
client.executeMethod(postRequest);
} catch (Exception ex) {
throw new TaskExecuteException("Sending post got exception ", ex);
}
response = postRequest.getResponseBodyAsString();
headers = postRequest.getRequestHeaders();
return response;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Header[] getHeaders() {
return headers;
}
public void setHeaders(Header[] headers) {
this.headers = headers;
}
public static void main(String[] args) {
String privateKey = "your_private_key";
String userName = "your_user_name";
String host = "demo.site.net";
int port = 55443;
String xml =
"<?xml version='1.0' encoding='UTF-8' standalone='no' ?>"
+ "<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>"
+ "<OPS_envelope>"
+ "<header>"
+ "<version>0.9</version>"
+ "<msg_id>2.21765911726198</msg_id>"
+ "<msg_type>standard</msg_type>"
+ "</header>"
+ "<body>"
+ "<data_block>"
+ "<dt_assoc>"
+ "<item key='attributes'>"
+ "<dt_assoc>"
+ "<item key='domain'>test-1061911771844.com</item>"
+ "<item key='pre-reg'>0</item>"
+ "</dt_assoc>"
+ "</item>"
+ "<item key='object'>DOMAIN</item>"
+ "<item key='action'>LOOKUP</item>"
+ "<item key='protocol'>XCP</item>"
+ "</dt_assoc>"
+ "</data_block>"
+ "</body>"
+ "</OPS_envelope>";
SslClient sslclient = new SslClient(host, port, userName, privateKey);
try {
String response = sslclient.sendRequest(xml);
System.out.println("\nResponse is:\n" + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
As in a day we are processing 10,000 + transactions, so number of socket at proxy are getting increased, so after 2-3 days, we need to do hard reboot of web server to free all the open sockets with proxy server.
Does HTTPClient, opens one socket for SSL Handshake and another for actual data post ? I don't think so. Then it should be at Web server and not at Proxy Server
For checking sockets and open ports at web server we are using netstat command.
For checking sockets and open ports at proxy server we are using proxy tool
when we check at web server it has created only one socket.
Because there is only one inbound connection to it.
SO why 2 socket at proxy server.
Because you are connecting to two different servers via the proxy server?
number of socket at proxy are getting increased, so after 2-3 days, we need to do hard reboot of web server to free all the open sockets with proxy server.
That doesn't make sense. It's the proxy server that has the dual connections, not the web server. You said that above. If the web server is running out of sockets, somebody isn't closing their connections correctly: the client, the proxy server, or the web server. Possibly your socket factory needs to override equals() and maybe hashCode() too, to enable whatever connection pooling HttpClient may do, I'm not an expert on that.
BUT your TrustManager is radically insecure. If you have this deployed in production, you have already committed a major security breach. This is currently a much bigger problem that running out of sockets every few days.
When socket ports run out, transaction timeouts occur. The solution to this problem is to tune the TIMEWAIT-related Windows registry parameters:
TcpTimedWaitDelay
MaxUserPort
StrictTimeWaitSeqCheck
The TIMEWAIT-related Windows registry parameters control how long a socket port remains unavailable after it is closed and how many ports are available for use.
By setting these windows registry parameters, I have solved this problem, but don't know, weather it is a correct solution to implement or not.

Categories

Resources