X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/blobdiff_plain/48f13c8adc5c4b9bc4ca96bf13939a7d7cfae562..5fc7cd13e7e561ef528e12d2fa088b58e35e00d0:/src/com/owncloud/android/network/AdvancedSslSocketFactory.java diff --git a/src/com/owncloud/android/network/AdvancedSslSocketFactory.java b/src/com/owncloud/android/network/AdvancedSslSocketFactory.java index 3c1d68e1..755888f7 100644 --- a/src/com/owncloud/android/network/AdvancedSslSocketFactory.java +++ b/src/com/owncloud/android/network/AdvancedSslSocketFactory.java @@ -24,15 +24,19 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; +import java.security.cert.X509Certificate; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; -import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; import android.util.Log; @@ -49,24 +53,32 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory { private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName(); private SSLContext mSslContext = null; - private X509HostnameVerifier mHostnameVerifier; + private AdvancedX509TrustManager mTrustManager = null; + private X509HostnameVerifier mHostnameVerifier = null; + public SSLContext getSslContext() { + return mSslContext; + } + /** * Constructor for AdvancedSSLProtocolSocketFactory. */ - public AdvancedSslSocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) { + public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) { if (sslContext == null) throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext"); + if (trustManager == null) + throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager"); mSslContext = sslContext; + mTrustManager = trustManager; mHostnameVerifier = hostnameVerifier; } /** - * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) + * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) */ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort); - verifyHostname(host, socket); + verifyPeerIdentity(host, port, socket); return socket; } @@ -94,7 +106,7 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory { Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params); if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); - } + } int timeout = params.getConnectionTimeout(); SocketFactory socketfactory = mSslContext.getSocketFactory(); Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout()); @@ -104,31 +116,21 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory { socket.setSoTimeout(params.getSoTimeout()); socket.bind(localaddr); socket.connect(remoteaddr, timeout); - verifyHostname(host, socket); + verifyPeerIdentity(host, port, socket); return socket; } /** - * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) + * @see ProtocolSocketFactory#createSocket(java.lang.String,int) */ public Socket createSocket(String host, int port) throws IOException, UnknownHostException { Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port); Socket socket = mSslContext.getSocketFactory().createSocket(host, port); - verifyHostname(host, socket); + verifyPeerIdentity(host, port, socket); return socket; } - /** - * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) - */ - /*public Socket createSocket(Socket socket, String host, int port, - boolean autoClose) throws IOException, UnknownHostException { - Log.d(TAG, "Creating SSL Socket from other shocket " + socket + " to remote " + host + ":" + port); - return getSSLContext().getSocketFactory().createSocket(socket, host, - port, autoClose); - }*/ - public boolean equals(Object obj) { return ((obj != null) && obj.getClass().equals( AdvancedSslSocketFactory.class)); @@ -149,20 +151,90 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory { } /** - * Verifies the host name with the content of the server certificate using the current host name verifier, if some + * Verifies the identity of the server. + * + * The server certificate is verified first. + * + * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any. * @param socket */ - private void verifyHostname(String host, Socket socket) throws IOException { - if (mHostnameVerifier != null) { + private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException { + try { + CertificateCombinedException failInHandshake = null; + /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager) try { - mHostnameVerifier.verify(host, (SSLSocket) socket); - } catch (IOException iox) { - try { - socket.close(); - } catch (Exception x) {} - throw iox; + SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect" + sock.startHandshake(); + + } catch (RuntimeException e) { + + if (e instanceof CertificateCombinedException) { + failInHandshake = (CertificateCombinedException) e; + } else { + Throwable cause = e.getCause(); + Throwable previousCause = null; + while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) { + previousCause = cause; + cause = cause.getCause(); + } + if (cause != null && cause instanceof CertificateCombinedException) { + failInHandshake = (CertificateCombinedException)cause; + } + } + if (failInHandshake == null) { + throw e; + } + failInHandshake.setHostInUrl(host); + } + + /// 2. VERIFY HOSTNAME + SSLSession newSession = null; + boolean verifiedHostname = true; + if (mHostnameVerifier != null) { + if (failInHandshake != null) { + /// 2.1 : a new SSLSession instance was NOT created in the handshake + X509Certificate serverCert = failInHandshake.getServerCertificate(); + try { + mHostnameVerifier.verify(host, serverCert); + } catch (SSLException e) { + verifiedHostname = false; + } + + } else { + /// 2.2 : a new SSLSession instance was created in the handshake + newSession = ((SSLSocket)socket).getSession(); + if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) { + verifiedHostname = mHostnameVerifier.verify(host, newSession); + } + } + } + + /// 3. Combine the exceptions to throw, if any + if (!verifiedHostname) { + SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL"); + if (failInHandshake == null) { + failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]); + failInHandshake.setHostInUrl(host); + } + failInHandshake.setSslPeerUnverifiedException(pue); + pue.initCause(failInHandshake); + throw pue; + + } else if (failInHandshake != null) { + SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified"); + hse.initCause(failInHandshake); + throw hse; + } + + } catch (IOException io) { + try { + socket.close(); + } catch (Exception x) { + // NOTHING - irrelevant exception for the caller + } + throw io; } } - -} \ No newline at end of file + +}