From f3eac35cd386f2fd1d97797bcb0fd2db4741cda1 Mon Sep 17 00:00:00 2001 From: "David A. Velasco" Date: Mon, 14 Apr 2014 13:10:05 +0200 Subject: [PATCH] Test project for library accepts server with a self-signed SSL certificate --- .../SelfSignedConfidentSslSocketFactory.java | 212 ++++++++++++++++++ .../lib/test_project/TestActivity.java | 33 ++- 2 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 test_client/src/com/owncloud/android/lib/test_project/SelfSignedConfidentSslSocketFactory.java diff --git a/test_client/src/com/owncloud/android/lib/test_project/SelfSignedConfidentSslSocketFactory.java b/test_client/src/com/owncloud/android/lib/test_project/SelfSignedConfidentSslSocketFactory.java new file mode 100644 index 00000000..3626e0b8 --- /dev/null +++ b/test_client/src/com/owncloud/android/lib/test_project/SelfSignedConfidentSslSocketFactory.java @@ -0,0 +1,212 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.test_project; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertStoreException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; + +import com.owncloud.android.lib.common.network.ServerNameIndicator; + + +/** + * SelfSignedConfidentSslSocketFactory allows to create SSL {@link Socket}s + * that accepts self-signed server certificates. + * + * WARNING: this SHOULD NOT be used in productive environments. + * + * @author David A. Velasco + */ + +public class SelfSignedConfidentSslSocketFactory implements SecureProtocolSocketFactory { + + + private SSLContext mSslContext = null; + + + /** + * Constructor for SelfSignedConfidentSslSocketFactory. + * @throws GeneralSecurityException + */ + public SelfSignedConfidentSslSocketFactory() throws GeneralSecurityException { + mSslContext = createSslContext(); + } + + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) + */ + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return mSslContext.getSocketFactory().createSocket(host, port); + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) + */ + @Override + public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) + throws IOException, UnknownHostException { + return mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort); + } + + /** + * Attempts to get a new socket connection to the given host within the given time limit. + * + * @param host The host name/IP + * @param port The port on the host + * @param clientHost The local host name/IP to bind the socket to + * @param clientPort The port on the local machine + * @param params {@link HttpConnectionParams} HTTP connection parameters. + * + * @return Socket A new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be determined + */ + @Override + public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, + HttpConnectionParams params) throws IOException, UnknownHostException, + ConnectTimeoutException { + + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + int timeout = params.getConnectionTimeout(); + SocketFactory socketfactory = mSslContext.getSocketFactory(); + Socket socket = socketfactory.createSocket(); + SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); + SocketAddress remoteaddr = new InetSocketAddress(host, port); + socket.setSoTimeout(params.getSoTimeout()); + socket.bind(localaddr); + ServerNameIndicator.setServerNameIndication(host, (SSLSocket)socket); + socket.connect(remoteaddr, timeout); + return socket; + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) + */ + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) + throws IOException, UnknownHostException { + return mSslContext.getSocketFactory().createSocket(socket, host, port, autoClose); + } + + + + private static SSLContext createSslContext() throws GeneralSecurityException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init( + null, + new TrustManager[] {new SelfSignedConfidentX509TrustManager()}, + null); + return context; + } + + public static class SelfSignedConfidentX509TrustManager implements X509TrustManager { + + private X509TrustManager mStandardTrustManager = null; + + public SelfSignedConfidentX509TrustManager() + throws NoSuchAlgorithmException, KeyStoreException, CertStoreException { + super(); + TrustManagerFactory factory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init((KeyStore)null); + mStandardTrustManager = findX509TrustManager(factory); + } + + /** + * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType) + */ + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + mStandardTrustManager.checkClientTrusted(chain, authType); + } + + /** + * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], + * String authType) + */ + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + if (chain != null && chain.length == 1) { + chain[0].checkValidity(); + } else { + mStandardTrustManager.checkServerTrusted(chain, authType); + } + } + + /** + * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() + */ + public X509Certificate[] getAcceptedIssuers() { + return mStandardTrustManager.getAcceptedIssuers(); + } + + /** + * Locates the first X509TrustManager provided by a given TrustManagerFactory + * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager + * @return The first X509TrustManager found in factory. + * @throws CertStoreException When no X509TrustManager instance was found in factory + */ + private X509TrustManager findX509TrustManager(TrustManagerFactory factory) + throws CertStoreException { + TrustManager tms[] = factory.getTrustManagers(); + for (int i = 0; i < tms.length; i++) { + if (tms[i] instanceof X509TrustManager) { + return (X509TrustManager) tms[i]; + } + } + return null; + } + } + + +} diff --git a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java index be243a73..593f64b0 100644 --- a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java +++ b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java @@ -25,10 +25,15 @@ package com.owncloud.android.lib.test_project; import java.io.File; +import java.security.GeneralSecurityException; + +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.resources.files.RemoteFile; +import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation; import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; @@ -47,6 +52,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.app.Activity; +import android.util.Log; import android.view.Menu; /** @@ -57,6 +63,7 @@ import android.view.Menu; public class TestActivity extends Activity { + private static final String TAG = null; // This account must exists on the server side private String mServerUri; private String mWebdavPath; @@ -78,10 +85,28 @@ public class TestActivity extends Activity { mPass = getString(R.string.password); mChunked = getResources().getBoolean(R.bool.chunked); - Uri uri = Uri.parse(mServerUri + mWebdavPath); - mClient = OwnCloudClientFactory.createOwnCloudClient(uri ,getApplicationContext(), true); - mClient.setBasicCredentials(mUser, mPass); - mClient.setBaseUri(Uri.parse(mServerUri)); + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + Log.e(TAG, "Self-signed confident SSL context could not be loaded"); + } + } + + Uri uri = Uri.parse(mServerUri + mWebdavPath); + mClient = new OwnCloudClient(NetworkUtils.getMultiThreadedConnManager()); + mClient.setDefaultTimeouts( + OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT, + OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT); + mClient.setWebdavUri(uri); + mClient.setFollowRedirects(true); + mClient.setBasicCredentials(mUser, mPass); + mClient.setBaseUri(Uri.parse(mServerUri)); }