I have my okhttpclient android websocket and jetty stand-alone server given below. I have to enable strong security for the communication between the android client and jetty server. I am facing issue on client side which is given below.
Server Side
package com.wss.okhttp;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.servlet.ServletException;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
public class JettySSLServer {
public static void main(String[] args) throws IOException {
JettyEndpoint endpoint = new JettyEndpoint();
Server webServer = new Server();
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
webServer.setHandler(context);
// --------------------SSL-Connection Start---------------------------//
KeyStore keyStore = null;
Certificate mPinnedCertificate = null;
try {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, new char[] {});
mPinnedCertificate = readPinnedCertificate();
keyStore.setCertificateEntry("ca", mPinnedCertificate);
} catch (KeyStoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SslContextFactory contextFactory = new SslContextFactory();
contextFactory.setIncludeProtocols("TLSv1.2");
contextFactory.setKeyStore(keyStore);
contextFactory.setKeyStorePassword("MIIEpAIBAAKCAQEAtzc9IK4U2YkfgASQ51v3IdjZUKABXw1RzUd+SxS8phI6O7Rb\r\n"
+ "RL+/KDQGPAtdrML2qDzaANIYa8rZ9jAyTnFAHpuykD8ByHf7RhogjPhJEvQDZkiX\r\n"
+ "r0hFS9A0ypqWn3fRWVXTREZTNGKgs0TQMNCY4Lm2H/lrgxNubaROn0KffLt+c5rK\r\n"
+ "7e3NXOcUUTP/tlkeC2JyHVIT8Cv2acaYJDD3PfHY5MSbvIORelVTp67eJkCSM+xF\r\n"
+ "spEi1SRuvRoBT+LMALNiIpi8nYBtNKlyDwmK2w38n11216g5DP3ipfZRHypk6048\r\n"
+ "vCO0qbgfwGfaep54twh94QJ4rjNi9X7f0F0qzCex7vmpJMpJ4gRl02mzni1DanOy\r\n"
+ "ExJB8ImpS3Il2jh2kVSbfLSg66UW33yAMKyCRCXypTSLgMGHetVDS+gHwcyFcE/M\r\n"
+ "nAY/k60CgYEA3ccY7AYSz10czJC0Y2ZPnw6NzESBNlWBgFIODQyKE5J2FKezJsR8\r\n"
+ "+LPRtEn+JeYI5+Q/jZZBR5qMXGaI+tprOlZKTSVcH4PQKOr7Ogd7v9leyH6zrfAe\r\n"
+ "k37acLaLtQE54tIyQVRLZW0dxzCiJ/tobJy+1f4TfWnpuRd4Y9xCnvMCgYEA03zT\r\n"
+ "aQLxW0ZBNbcz9ivDdbjy5kK2m1vA7Rq9LzawR0K9W05WOKUH7T1Ybp/idNTZfjKo\r\n"
+ "k+G2DV9ts/vQEL//3PthWo/FWZ8hsA5P1J+cT0RrwKKgWjCPNArp2l/T4vEdkGdM\r\n"
+ "GBbB6KZe5Wsn+HKPBszU35A8K2pD5PpebV0RGNcCgYBSEMmFFR5Cw2bTv7wwh/xw\r\n"
+ "lBcefj7+FxfrnvF6HKi/Y1P0grXFY7IG6atwtmyoI34qKQjnYLFZSLQlwP9xK/+/\r\n"
+ "v4yRDYEQXFtbuNAsAfbl4A61zES62X7G/4rfaH08Bm8gIr3b9NBNgNojCjkG6H4U\r\n"
+ "qs/nKbSWlOmaxzeSZD/2xwKBgQCJOlz/rc4ouLyFe1v3J0yMLbdHHBDbXD0iXRBW\r\n"
+ "+3iEtNSj03/0/3jWQtEH7y0FPDvoPDzQwEvd/4bym7nVtI/0txTjq5iV38D/OTop\r\n"
+ "sGu/r5jvhVbhTtMNJOu7LCUUA/p4Ad8JXnLyYEoBOXfVKZiPBAg5DKFOVoS5po/x\r\n"
+ "DMuUPwKBgQCa4cym/jJnK6r7h2xzE5bHLcniuud0F1DgCMkW/x026z4owpGtSCyK\r\n"
+ "BEQn/PY0rnSioRkcNjm5leGb1oOaFcT/QBgGhVpm09TyA/v8tj96pP631fYayzZh\r\n"
+ "lBEvszx6LOLEBbIioiXFtp1JhmWzkxvbuB114S3ChK+IKVrgZYTjvQ==");
contextFactory.setIncludeCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
contextFactory.setTrustAll(true);
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(contextFactory,
org.eclipse.jetty.http.HttpVersion.HTTP_1_1.toString());
ServerConnector sslConnector = new ServerConnector(webServer, sslConnectionFactory);
sslConnector.setPort(8443);
webServer.addConnector(sslConnector);
ServerConnector wsConnector = new ServerConnector(webServer);
wsConnector.setPort(50055);
webServer.addConnector(wsConnector);
// --------------------SSL-Connection End---------------------------//
ServerContainer container;
try {
container = WebSocketServerContainerInitializer.configureContext(context);
container.addEndpoint(endpoint.getClass());
WebSocketServerContainerInitializer.configureContext(context);
webServer.start();
} catch (ServletException servEx) {
System.out.println(servEx.getMessage());
} catch (DeploymentException depEx) {
System.out.println(depEx.getMessage());
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
private static Certificate readPinnedCertificate() throws CertificateException, IOException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new FileInputStream(System.getProperty("user.dir") + "/assets/va_cert.pem");
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
return ca;
}
}
Server Side Log
2018-04-26 18:33:51.049:INFO::main: Logging initialized @190ms
2018-04-26 18:33:51.229:INFO:oejs.Server:main: jetty-9.3.z-SNAPSHOT
2018-04-26 18:33:51.596:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@4e7dc304{/,null,AVAILABLE}
2018-04-26 18:33:51.606:INFO:oejus.SslContextFactory:main: x509=X509@396f6598(ca,h=[],w=[]) for SslContextFactory@394e1a0f(null,null)
2018-04-26 18:33:51.630:INFO:oejs.ServerConnector:main: Started ServerConnector@458c1321{SSL,[ssl]}{0.0.0.0:8443}
2018-04-26 18:33:51.635:INFO:oejs.ServerConnector:main: Started ServerConnector@11438d26{HTTP/1.1,[http/1.1]}{0.0.0.0:50055}
2018-04-26 18:33:51.636:INFO:oejs.Server:main: Started @777ms
Client Side
package com.example.myapplication;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.TlsVersion;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
public class MainActivity extends AppCompatActivity {
private Button start;
private TextView output;
private OkHttpClient client;
private Certificate mPinnedCertificate;
private final class EchoWebSocketListener extends WebSocketListener {
private static final int NORMAL_CLOSURE_STATUS = 1000;
@Override
public void onOpen(WebSocket webSocket, Response response) {
output("Sending----------");
webSocket.send("Hello, Friend");
webSocket.send("USA");
webSocket.send(ByteString.decodeHex("Hi"));
webSocket.close(NORMAL_CLOSURE_STATUS, "Goodbye !");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
output("Receiving : " + text);
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
output("Receiving bytes : " + bytes.hex());
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
output("Closing : " + code + " / " + reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
output("Error : " + t.getMessage());
Log.i("Connection Error ",t.getMessage());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
output = (TextView) findViewById(R.id.output);
prepareOkHttpClient();
// client = new OkHttpClient();
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
start();
}
});
}
private void start() {
Request request = new Request.Builder().url("wss://localhost:50055/sample").build();
EchoWebSocketListener listener = new EchoWebSocketListener();
WebSocket ws = client.newWebSocket(request, listener);
client.dispatcher().executorService().shutdown();
}
private void output(final String txt) {
runOnUiThread(new Runnable() {
@Override
public void run() {
output.setText(output.getText().toString() + "\n\n" + txt);
}
});
}
private void prepareOkHttpClient() {
try {
ConnectionSpec wssSpecs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
.build();
mPinnedCertificate = readPinnedCertificate("va_cert.der");
// Create a KeyStore containing our trusted CAs
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, new char[]{});
keyStore.setCertificateEntry("ca", mPinnedCertificate);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, tmf.getTrustManagers(), null);
Log.i("Protocol : ",sslContext.getProvider()+" **** " + sslContext.getProtocol());
TrustManager[] trustManagers = tmf.getTrustManagers();
client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(wssSpecs))
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.build();
} catch (NoSuchAlgorithmException | CertificateException
| KeyStoreException | KeyManagementException | IOException e) {
Log.i("SSL Exception ",e.getMessage());
}
}
/**
* Reads SSL certificate from App Assets folder.
*
* @param certAssetName File name of the SSL certificate.
* @return Certificate object.
* @throws CertificateException Certificate is invalid exception.
* @throws IOException File does not exist.
*/
private Certificate readPinnedCertificate(final String certAssetName)
throws CertificateException, IOException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
AssetManager assManager = this.getApplicationContext().getAssets();
InputStream caInput = assManager.open(certAssetName);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
return ca;
}
}
Client Side Error
Connection Error: Unable to find acceptable protocols. isFallback=false, modes=[ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256], tlsVersions=[TLS_1_2], supportsTlsExtensions=true)], supported protocols=[TLSv1, TLSv1.1, TLSv1.2]
Dont know how to fix this issue. Breaking my head for the past one week. Any help
server.setDumpAfterStart(true);
before you callserver.start();
. Look for the output related toSslContextFactory
and its selected/enabled report. Edit your question and include the details. – Absorb