diff options
author | Koushik Dutta <koushd@gmail.com> | 2014-07-13 18:06:22 -0700 |
---|---|---|
committer | Koushik Dutta <koushd@gmail.com> | 2014-07-13 18:06:22 -0700 |
commit | d8a5060ca43c7efc5ff7b2b253791805cb928114 (patch) | |
tree | 2c347298ef8c7cf54ebeef20568e526dd4467367 /AndroidAsync | |
parent | eb633ea9125ec9ddfbe99d04301b61cbbac3a507 (diff) | |
download | AndroidAsync-d8a5060ca43c7efc5ff7b2b253791805cb928114.tar.gz AndroidAsync-d8a5060ca43c7efc5ff7b2b253791805cb928114.tar.bz2 AndroidAsync-d8a5060ca43c7efc5ff7b2b253791805cb928114.zip |
AsyncSSLSocketWrapper: Perform handshake before allowing read/write.
Diffstat (limited to 'AndroidAsync')
3 files changed, 85 insertions, 158 deletions
diff --git a/AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocketWrapper.java b/AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocketWrapper.java index 93c2865..bb6a167 100644 --- a/AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocketWrapper.java +++ b/AndroidAsync/src/com/koushikdutta/async/AsyncSSLSocketWrapper.java @@ -28,129 +28,28 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket { + public interface HandshakeCallback { + public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket); + } + static SSLContext defaultSSLContext; AsyncSocket mSocket; BufferedDataEmitter mEmitter; BufferedDataSink mSink; - boolean mUnwrapping = false; + boolean mUnwrapping; + SSLEngine engine; + boolean finishedHandshake; + private int mPort; + private String mHost; + private boolean mWrapping; HostnameVerifier hostnameVerifier; - - /* - private static void initTLS_1_2() { - try { - defaultSSLContext = SSLContext.getInstance("TLSv1.2"); - } - catch (NoSuchAlgorithmException e) { - } - } - - private static void initTLS_1_1() { - try { - defaultSSLContext = SSLContext.getInstance("TLSv1.1"); - } - catch (NoSuchAlgorithmException e) { - } - } - - private static void initTLS() { - try { - defaultSSLContext = SSLContext.getInstance("TLS"); - } - catch (NoSuchAlgorithmException e) { - } - } - - static { - try { - initTLS_1_2(); - if (defaultSSLContext == null) - initTLS_1_1(); - if (defaultSSLContext == null) - initTLS(); - if (defaultSSLContext == null) - defaultSSLContext = SSLContext.getInstance("SSL"); - // critical extension 2.5.29.15 is implemented improperly prior to 4.0.3. - // https://code.google.com/p/android/issues/detail?id=9307 - // https://groups.google.com/forum/?fromgroups=#!topic/netty/UCfqPPk5O4s - // certs that use this extension will throw in Cipher.java. - // fallback is to use a custom SSLContext, and hack around the x509 extension. - TrustManager[] trustManagers = null; - if (Build.VERSION.SDK_INT <= 15) { - trustManagers = new TrustManager[] { new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - for (X509Certificate cert : certs) { - if (cert != null && cert.getCriticalExtensionOIDs() != null) - cert.getCriticalExtensionOIDs().remove("2.5.29.15"); - } - } - } }; - } - defaultSSLContext.init(null, trustManagers, null); - } - catch (Exception ex) { - ex.printStackTrace(); - } - } - - // android SSL cipher suites were downgraded (!!) for some derpy reason. - // Paranoid people would be wise to enable the original/secure suites. - // http://op-co.de/blog/posts/android_ssl_downgrade/ - public static final String RECOMMENDED_CIPHERS[] = { - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_RC4_128_SHA", - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_CBC_SHA", - "SSL_RSA_WITH_3DES_EDE_CBC_SHA", - "SSL_RSA_WITH_RC4_128_SHA", - "SSL_RSA_WITH_RC4_128_MD5", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - }; - - public static final String RECOMMENDED_PROTOCOLS[] = { - "TLSv1" - }; - - public static void setupRecommendedEngineSecurity(SSLEngine engine) { - LinkedHashSet<String> ciphers = new LinkedHashSet<String>(Arrays.asList(engine.getSupportedCipherSuites())); - ciphers.addAll(Arrays.asList(engine.getSupportedCipherSuites())); - LinkedHashSet<String> protocols = new LinkedHashSet<String>(); - protocols.addAll(Arrays.asList(engine.getSupportedProtocols())); - - ArrayList<String> enabledCiphers = new ArrayList<String>(); - for (String cipher: RECOMMENDED_CIPHERS) { - if (ciphers.contains(cipher)) - enabledCiphers.add(cipher); - } - - ArrayList<String> enabledProtocols = new ArrayList<String>(); - for (String protocol: RECOMMENDED_PROTOCOLS) { - if (protocols.contains(protocol)) - enabledProtocols.add(protocol); - } - - enabledCiphers.addAll(Arrays.asList(engine.getEnabledCipherSuites())); - enabledProtocols.addAll(Arrays.asList(engine.getEnabledProtocols())); -// engine.setEnabledCipherSuites(enabledCiphers.toArray(new String[enabledCiphers.size()])); -// engine.setEnabledProtocols(enabledProtocols.toArray(new String[enabledProtocols.size()])); -// engine.setEnabledCipherSuites(RECOMMENDED_CIPHERS); - engine.setEnabledProtocols(new String[] {"SSL"}); - } - */ + HandshakeCallback handshakeCallback; + X509Certificate[] peerCertificates; + WritableCallback mWriteableCallback; + DataCallback mDataCallback; + TrustManager[] trustManagers; + boolean clientMode; static { // following is the "trust the system" certs setup @@ -195,19 +94,25 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket return defaultSSLContext.createSSLEngine(); } - @Override - public void end() { - mSocket.end(); - } - - public AsyncSSLSocketWrapper(AsyncSocket socket, String host, int port) { - this(socket, host, port, createDefaultSSLEngine(), null, null, true); + public static void handshake(AsyncSocket socket, + String host, int port, + SSLEngine sslEngine, + TrustManager[] trustManagers, HostnameVerifier verifier, boolean clientMode, + HandshakeCallback callback) { + AsyncSSLSocketWrapper wrapper = new AsyncSSLSocketWrapper(socket, host, port, sslEngine, trustManagers, verifier, clientMode); + wrapper.handshakeCallback = callback; + try { + wrapper.engine.beginHandshake(); + wrapper.handleHandshakeStatus(wrapper.engine.getHandshakeStatus()); + } catch (SSLException e) { + wrapper.report(e); + } } - TrustManager[] trustManagers; - boolean clientMode; - - public AsyncSSLSocketWrapper(AsyncSocket socket, String host, int port, SSLEngine sslEngine, TrustManager[] trustManagers, HostnameVerifier verifier, boolean clientMode) { + private AsyncSSLSocketWrapper(AsyncSocket socket, + String host, int port, + SSLEngine sslEngine, + TrustManager[] trustManagers, HostnameVerifier verifier, boolean clientMode) { mSocket = socket; hostnameVerifier = verifier; this.clientMode = clientMode; @@ -277,7 +182,7 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket bb.addFirst(b); b = ByteBufferList.EMPTY_BYTEBUFFER; } - handleResult(res); + handleHandshakeStatus(res.getHandshakeStatus()); if (b.remaining() == remaining && before == transformed.remaining()) { bb.addFirst(b); break; @@ -308,32 +213,30 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket } - SSLEngine engine; - boolean finishedHandshake = false; - - private String mHost; + @Override + public void end() { + mSocket.end(); + } public String getHost() { return mHost; } - private int mPort; - public int getPort() { return mPort; } - private void handleResult(SSLEngineResult res) { - if (res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + private void handleHandshakeStatus(HandshakeStatus status) { + if (status == HandshakeStatus.NEED_TASK) { final Runnable task = engine.getDelegatedTask(); task.run(); } - if (res.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + if (status == HandshakeStatus.NEED_WRAP) { write(ByteBufferList.EMPTY_BYTEBUFFER); } - if (res.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { + if (status == HandshakeStatus.NEED_UNWRAP) { mEmitter.onDataAvailable(); } @@ -380,6 +283,11 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket throw e; } } + else { + finishedHandshake = true; + } + handshakeCallback.onHandshakeCompleted(null, this); + handshakeCallback = null; if (mWriteableCallback != null) mWriteableCallback.onWriteable(); mEmitter.onDataAvailable(); @@ -403,7 +311,6 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket assert !mWriteTmp.hasRemaining(); } - private boolean mWrapping = false; int calculateAlloc(int remaining) { // alloc 50% more than we need for writing @@ -445,7 +352,7 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket else { mWriteTmp = ByteBufferList.obtain(calculateAlloc(bb.remaining())); } - handleResult(res); + handleHandshakeStatus(res.getHandshakeStatus()); } catch (SSLException e) { report(e); @@ -489,7 +396,7 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket } else { mWriteTmp = ByteBufferList.obtain(calculateAlloc(bb.remaining())); - handleResult(res); + handleHandshakeStatus(res.getHandshakeStatus()); } } catch (SSLException e) { @@ -501,8 +408,6 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket mWrapping = false; } - WritableCallback mWriteableCallback; - @Override public void setWriteableCallback(WritableCallback handler) { mWriteableCallback = handler; @@ -514,13 +419,21 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket } private void report(Exception e) { + final HandshakeCallback hs = handshakeCallback; + if (hs != null) { + handshakeCallback = null; + mSocket.setDataCallback(new NullDataCallback()); + mSocket.end(); + mSocket.close(); + hs.onHandshakeCompleted(e, null); + return; + } + CompletedCallback cb = getEndCallback(); if (cb != null) cb.onCompleted(e); } - DataCallback mDataCallback; - @Override public void setDataCallback(DataCallback callback) { mDataCallback = callback; @@ -596,8 +509,6 @@ public class AsyncSSLSocketWrapper implements AsyncSocketWrapper, AsyncSSLSocket return mSocket; } - X509Certificate[] peerCertificates; - @Override public X509Certificate[] getPeerCertificates() { return peerCertificates; diff --git a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLSocketMiddleware.java b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLSocketMiddleware.java index 4f3fff7..868afc6 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLSocketMiddleware.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/AsyncSSLSocketMiddleware.java @@ -4,6 +4,7 @@ import android.net.Uri; import android.os.Build; import android.text.TextUtils; +import com.koushikdutta.async.AsyncSSLSocket; import com.koushikdutta.async.AsyncSSLSocketWrapper; import com.koushikdutta.async.AsyncSocket; import com.koushikdutta.async.LineEmitter; @@ -70,6 +71,17 @@ public class AsyncSSLSocketMiddleware extends AsyncSocketMiddleware { return sslEngine; } + protected void tryHandshake(final ConnectCallback callback, AsyncSocket socket, final Uri uri, final int port) { + AsyncSSLSocketWrapper.handshake(socket, uri.getHost(), port, + createConfiguredSSLEngine(uri.getHost(), port), + trustManagers, hostnameVerifier, true, new AsyncSSLSocketWrapper.HandshakeCallback() { + @Override + public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) { + callback.onConnectCompleted(e, socket); + } + }); + } + @Override protected ConnectCallback wrapCallback(final ConnectCallback callback, final Uri uri, final int port, final boolean proxied) { return new ConnectCallback() { @@ -77,10 +89,7 @@ public class AsyncSSLSocketMiddleware extends AsyncSocketMiddleware { public void onConnectCompleted(Exception ex, final AsyncSocket socket) { if (ex == null) { if (!proxied) { - callback.onConnectCompleted(null, - new AsyncSSLSocketWrapper(socket, uri.getHost(), port, - createConfiguredSSLEngine(uri.getHost(), port), - trustManagers, hostnameVerifier, true)); + tryHandshake(callback, socket, uri, port); } else { // this SSL connection is proxied, must issue a CONNECT request to the proxy server @@ -112,10 +121,7 @@ public class AsyncSSLSocketMiddleware extends AsyncSocketMiddleware { socket.setDataCallback(null); socket.setEndCallback(null); if (TextUtils.isEmpty(s.trim())) { - callback.onConnectCompleted(null, - new AsyncSSLSocketWrapper(socket, uri.getHost(), port, - createConfiguredSSLEngine(uri.getHost(), port), - trustManagers, hostnameVerifier, true)); + tryHandshake(callback, socket, uri, port); } else { callback.onConnectCompleted(new IOException("unknown second status line"), socket); diff --git a/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServer.java b/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServer.java index 8d139ef..b1b393d 100644 --- a/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServer.java +++ b/AndroidAsync/src/com/koushikdutta/async/http/server/AsyncHttpServer.java @@ -1,9 +1,12 @@ package com.koushikdutta.async.http.server; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.AssetManager; +import android.os.Build; import android.text.TextUtils; +import com.koushikdutta.async.AsyncSSLSocket; import com.koushikdutta.async.AsyncSSLSocketWrapper; import com.koushikdutta.async.AsyncServer; import com.koushikdutta.async.AsyncServerSocket; @@ -39,6 +42,7 @@ import java.util.regex.Pattern; import javax.net.ssl.SSLContext; +@TargetApi(Build.VERSION_CODES.ECLAIR) public class AsyncHttpServer { ArrayList<AsyncServerSocket> mListeners = new ArrayList<AsyncServerSocket>(); public void stop() { @@ -223,8 +227,14 @@ public class AsyncHttpServer { AsyncServer.getDefault().listen(null, port, new ListenCallback() { @Override public void onAccepted(AsyncSocket socket) { - AsyncSSLSocketWrapper sslSocket = new AsyncSSLSocketWrapper(socket, null, port, sslContext.createSSLEngine(), null, null, false); - mListenCallback.onAccepted(sslSocket); + AsyncSSLSocketWrapper.handshake(socket, null, port, sslContext.createSSLEngine(), null, null, false, + new AsyncSSLSocketWrapper.HandshakeCallback() { + @Override + public void onHandshakeCompleted(Exception e, AsyncSSLSocket socket) { + if (socket != null) + mListenCallback.onAccepted(socket); + } + }); } @Override |