I'm using gottox socket.io java client for an Android chat application. I could connect to both web-socket and Xhr transport in HTTP mode. But when i switch to HTTPS only Xhr mode is working. i used the default SSL Context as below
SocketIO.setDefaultSSLSocketFactory(SSLContext.getInstance("Default"));
This works fine in Xhr mode. But in websocket transport there are no responses or errors.
Update
It might be that with new versions IO.setDefaultSSLContext and IO. setDefaultHostnameVerifier methods are not available. Instead now we can create our own OkHttpClient and set the hostname verifier and ssl socket factory etc on it as mentioned on socket.io-client-java usage. Here is the sniplet from there:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.hostnameVerifier(myHostnameVerifier)
.sslSocketFactory(mySSLContext.getSocketFactory(), myX509TrustManager)
.build(); // default settings for all sockets
IO.setDefaultOkHttpWebSocketFactory(okHttpClient);
IO.setDefaultOkHttpCallFactory(okHttpClient);
Initial Answer:
I had the same issue with io.socket:socket.io-client:0.7.0 version of socket.io library on Android for long. It used to work fine for http protocol, however for https protocol it had trouble establishing connection giving xhr poll errors.
Following solution works for me without modifying the library itself:
// Socket connection
private Socket mSocket;
// Configure options
IO.Options options = new IO.Options();
// ... add more options
// End point https
String yourEndpoint = "https://whatever.yoururl.com"
String yourHostName = "yoururl.com"
// If https, explicitly tell set the sslContext.
if (yourEndpoint.startsWith("https://")) {
try {
// Default settings for all sockets
// Set default ssl context
IO.setDefaultSSLContext(SSLContext.getDefault());
// Set default hostname
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(yourHostName, session);
}
};
IO.setDefaultHostnameVerifier(hostnameVerifier);
// set as an option
options.sslContext = SSLContext.getDefault();
options.hostnameVerifier = hostnameVerifier;
options.secure = true;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
// Instantiate the socket
mSocket = IO.socket(mEndpoint, options);
Hope this helps.
It works but you have to do some modifications on io.socket library.
Instead of using the socketio.jar, import into src folder the io.socket library (You'll find inside socket.io-java-client package). There, you have to edit the WebsocketTransport class.
Here you have the solution
https://github.com/Gottox/socket.io-java-client/issues/60
public WebsocketTransport(URI uri, IOConnection connection) {
super(uri);
this.connection = connection;
SSLContext context = null;
try {
context = SSLContext.getInstance("TLS", "HarmonyJSSE");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
context.init(null, null, null);
} catch (KeyManagementException e) {
e.printStackTrace();
}
if("wss".equals(uri.getScheme()) && context != null) {
this.setWebSocketFactory(new DefaultSSLWebSocketClientFactory(context));
}
}
And remember to call the setDefaultSSLSocketFactory like this:
socket = new SocketIO();
socket.setDefaultSSLSocketFactory(SSLContext.getDefault());
socket.connect("https://www.myHttpsServer.com:443/");
Hope it helps someone ;)
Websocket with SSL working in AndroidAsync. Using that for now.
Related
I am using SSLSocket to connect to a server. I want to use ssl SNI extension. I can use SSLParameter.setServerNames in java. But it appears in android it is not available till sdk 24. What are my options here?
I had the same problem, solved by using reflection.
Connect the socket using:
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket(uri.getHost(), 443);
setSNIHost(sf, socket, uri.getHost());
SSLSession s = socket.getSession();
where setSNIHost is declared as:
public void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Log.i(TAG, "Setting SNI via SSLParameters");
SNIHostName sniHostName = new SNIHostName(hostname);
SSLParameters sslParameters = socket.getSSLParameters();
List<SNIServerName> sniHostNameList = new ArrayList<>(1);
sniHostNameList.add(sniHostName);
sslParameters.setServerNames(sniHostNameList);
socket.setSSLParameters(sslParameters);
} else if (factory instanceof android.net.SSLCertificateSocketFactory) {
Log.i(TAG, "Setting SNI via SSLCertificateSocketFactory");
((android.net.SSLCertificateSocketFactory)factory).setHostname(socket, hostname);
} else {
Log.i(TAG, "Setting SNI via reflection");
try {
socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
} catch (Throwable e) {
Log.e(TAG, "Could not call SSLSocket#setHostname(String) method ", e);
}
}
}
I don't really remember where I copied the code for the reflection, I simply added the support for API 24.
I am trying to connect to dialogic xms server using autobahn WebSockets:
As read I need to add a protocol rtcweb
This is my code :
String uri = "ws://myUrl:port";
WebSocketConnection ws = new WebSocketConnection();;
wsObserver = new WebSocketObserver();
String[] protocols = {"rtcweb"};
try {
ws.connect(new URI(uri), protocols, wsObserver, new WebSocketOptions() );
} catch (WebSocketException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
But when i add the protocol rtcweb I get this onClose; and the connection still didn't open :
Code: CONNECTION_LOST. Reason: WebSockets connection lost
I try establish a https server in android for other phones to connect,but
only iphone6 sometimes can connected , ipod ,android browser all failed to get the webserver content.
(the browser message is fail to establish safe connect)
I use nanohttpd's simpleServer Class to establish it.
my CA is here
http://www.mediafire.com/download/53f6e9uveb47kqv/ca.cer
// for client to download
http://www.mediafire.com/download/v9i58n38yb85co5/server.p12
//for server to load keystore
CA password both are singuler .
Here is my sslServerSocket Code
char[]kspass = KEYSTOREPASS.toCharArray();
char[]ctpass = KEYSTOREPASS.toCharArray();
try {
KeyStore ks = KeyStore.getInstance("PKCS12");
//ks.load(new FileInputStream("file:///android_asset/singuler.keystore"),kspass);
ks.load(getResources().getAssets().open("server.p12"),kspass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, ctpass);
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("X509");
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
//webServer.makeSecure(NanoHTTPD.makeSSLSocketFactory(ks, kmf.getKeyManagers()));
webServerSSL.makeSecure(sc.getServerSocketFactory());
} catch (Exception e) {
// TODO: handle exceptionser
Log.i("test", e.toString());
}
try {
webServer.start(15);
webServerSSL.start(15);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
webServer = null;
webServerSSL=null;
Log.i("test", e.toString());
}
Can any one help me ?
Thank you.
I found the answer
Whe start nanohttpd server , need set the timeout millonseconds for every connect socket,especial for https request.
Firstly,
I want to use session ticket in android, My code as follows:
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext cpmContext = SSLContext.getInstance("TLSv1.2");
cpmContext.init(null, null, null);
SSLSocket socket = (SSLSocket) cpmContext.getSocketFactory().createSocket(ip, port);
socket.setEnabledProtocols(socket.getEnabledProtocols());
socket.setEnabledCipherSuites(socket.getEnabledCipherSuites());
Class c = socket.getClass();
try {
Method m = c.getMethod("setUseSessionTickets",boolean.class);
m.invoke(socket,true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
SSLSession session = socket.getSession();
I capture the data blcok by tcpdump, the code can get "
TLSv1.2 224 New Session Ticket, Change Cipher Spec, Hello Request, Hello Request"
,so I think I get the session ticket, but when I reconnect to server, "session ticket "content of client hello is as follow:
"Extension:sessionTicket TLS
Type: SessionTicket TLS(0x0023)
length:0
Data:(0 bytes)"
it did not execute resume.
then I use SSLCertificateSocketFactory to create SSLSocket:
private Socket createSocketOnLine(final String ip, final int port) throws UnknownHostException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, KeyManagementException {
SSLCertificateSocketFactory sf = (SSLCertificateSocketFactory) SSLCertificateSocketFactory
.getDefault(30 * 1000);
SSLSocket socket = (SSLSocket) sf.createSocket(ip, port);
socket.setEnabledProtocols(socket.getEnabledProtocols());
socket.setEnabledCipherSuites(socket.getEnabledCipherSuites());
enableSessionTicket(sf, socket);
SSLSession session = socket.getSession();
return socket;
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void enableSessionTicket(SSLCertificateSocketFactory sf, Socket socket) {
if (VERSION.SDK_INT > 17) {
sf.setUseSessionTickets(socket, true);
}
}
this code donot even enable session and the version of tls is always TLSv1.0,who can tell me how to enable it and set version of tls to be tlsv1.2?
PS:I test it on android 4.4 and L
Android's SSLCertificateSocketFactory is broken for you on some/many devices. It does not provide an interface/method for you to specify the enabled protocols. Instead, it simply uses the system default for the enabledProtocols. On earlier Android devices, that would be TLS and SSL3 and not TLSv1.2.
Because of the way it is implemented, you can't subclass it and attempt to override its behavior. There is no way for you to set the TLS protocol you want, let alone to set the ciphersuites that you may need to control.
Unfortunately, some of the other features of SSLCertificateSocketFactory are lost as well. For example, you can't enable SNI or set alpns protocols "officially" without it.
Thus, your solution will need to subclass SSLSocketFactory to provide your own socketfactory to control the settings you need as you do in your first code example. You can use your reflection trick above to enable session tickets in your own createSocket() method implementations.
How can access to wss:// protocol in java ?
i use benkay / java-socket.io.client
but it's not support wss protocol.
i tried use SSLEngine. but it's very hard work.
how can connect to ssl in java ?
I tried change SocketChannel by SSLEngine. but it is not worked.
ssl channel is ok. but i can't wire this original websocket part.
this is source code.
client = SocketChannel.open(remote);
client.configureBlocking(false);
//client.connect(remote);
selector = Selector.open();
this.conn = new WebSocket(client, new LinkedBlockingQueue<ByteBuffer>(), this);
client.register(selector, SelectionKey.OP_READ);
try {
sslClient = new SSLClient(keyStore, storepass.toCharArray(), client);
sslClient.beginHandShake();
startClient()
} catch (Exception e) {
e.printStackTrace();
}
this point uncorret ?? i don't know .. not same the original websocket code.. may problem is this point. how can fix it ??
public void startClient()
{
try
{
while(true)
{
if(selector.select() <= 0)
{
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext())
{
SelectionKey key = (SelectionKey)it.next();
Log.e("key","key");
if(key.isReadable())
{
read(key);
}
it.remove();
}
}
}
catch(Exception e)
{
}
}
and SSLClient is http://rapidant.tistory.com/attachment/cfile25.uf#121346414D45B0960BD01B.zip
key store : change JKS to BKS, not problem.
how can wrap the SocketChannel ?
(Web browser it worked.)
You could check out my fork of the Autobahn WebSocket Library.
Secure WebSockets based upon Autobahn
You don't want to use SSLEngine on Android because it is broken.