Okhttp android Websocket和Jetty Server连接SSL

我有下面给出的okhttpclient android websocket和jetty独立服务器。 我必须为android客户端和jetty服务器之间的通信启用强大的安全性。 我在客户端面临问题,如下所示。

服务器端

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; } } 

服务器端日志

 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 oejsServletContextHandler@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 

客户端

 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; } } 

客户端错误

 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] 

不知道如何解决这个问题。 过去一周打破了我的脑袋。 任何帮助

 new SslConnectionFactory(contextFactory, org.eclipse.jetty.http.HttpVersion.HTTP_2.toString()); 

WebSocket over HTTP / 2(尚未)存在。

https://daniel.haxx.se/blog/2016/06/15/no-websockets-over-http2/

WebSocket over HTTP / 2的概念是全新的,草案规范在过去几个月才被讨论过。

https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets-01