Android Open Source - ACM_Pi_Cloud Advanced Ssl Socket Factory






From Project

Back to project page ACM_Pi_Cloud.

License

The source code is released under:

MIT License

If you think the Android project ACM_Pi_Cloud listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* ownCloud Android Library is available under MIT license
 *   Copyright (C) 2014 ownCloud Inc.//w  w w  .j  a v  a2 s . c  o m
 *   
 *   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.common.network;

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.Provider;
import java.security.cert.X509Certificate;
//import java.util.Enumeration;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
//import javax.net.ssl.SSLParameters;
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.http.conn.ssl.X509HostnameVerifier;

//import android.os.Build;
import android.util.Log;



/**
 * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with 
 * a custom SSLContext and an optional Hostname Verifier.
 * 
 * @author David A. Velasco
 */

public class AdvancedSslSocketFactory implements ProtocolSocketFactory {

    private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
    
    private SSLContext mSslContext = null;
    private AdvancedX509TrustManager mTrustManager = null;
    private X509HostnameVerifier mHostnameVerifier = null;

    public SSLContext getSslContext() {
        return mSslContext;
    }
    
    /**
     * Constructor for AdvancedSSLProtocolSocketFactory.
     */
    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 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);
        verifyPeerIdentity(host, port, socket);
        return socket;
    }

    /*
    private void logSslInfo() {
      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) {
        Log.v(TAG, "SUPPORTED SSL PARAMETERS");
        logSslParameters(mSslContext.getSupportedSSLParameters());
        Log.v(TAG, "DEFAULT SSL PARAMETERS");
        logSslParameters(mSslContext.getDefaultSSLParameters());
        Log.i(TAG, "CURRENT PARAMETERS");
        Log.i(TAG, "Protocol: " + mSslContext.getProtocol());
      }
      Log.i(TAG, "PROVIDER");
      logSecurityProvider(mSslContext.getProvider());
  }
    
    private void logSecurityProvider(Provider provider) {
      Log.i(TAG, "name: " + provider.getName());
      Log.i(TAG, "version: " + provider.getVersion());
      Log.i(TAG, "info: " + provider.getInfo());
      Enumeration<?> keys = provider.propertyNames();
      String key;
      while (keys.hasMoreElements()) {
        key = (String) keys.nextElement();
          Log.i(TAG, "  property " + key + " : " + provider.getProperty(key));
      }
  }

  private void logSslParameters(SSLParameters params) {
      Log.v(TAG, "Cipher suites: ");
      String [] elements = params.getCipherSuites();
      for (int i=0; i<elements.length ; i++) {
        Log.v(TAG, "  " + elements[i]);
      }
      Log.v(TAG, "Protocols: ");
      elements = params.getProtocols();
      for (int i=0; i<elements.length ; i++) {
        Log.v(TAG, "  " + elements[i]);
      }
  }
  */

  /**
     * 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
     */
    public Socket createSocket(final String host, final int port,
            final InetAddress localAddress, final int localPort,
            final HttpConnectionParams params) throws IOException,
            UnknownHostException, ConnectTimeoutException {
        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();
        
        //logSslInfo();
        
        SocketFactory socketfactory = mSslContext.getSocketFactory();
        Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
        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);
        verifyPeerIdentity(host, port, socket);
        return socket;
    }

  /**
     * @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);
        verifyPeerIdentity(host, port, socket);
        return socket; 
    }

    public boolean equals(Object obj) {
        return ((obj != null) && obj.getClass().equals(
                AdvancedSslSocketFactory.class));
    }

    public int hashCode() {
        return AdvancedSslSocketFactory.class.hashCode();
    }


    public X509HostnameVerifier getHostNameVerifier() {
        return mHostnameVerifier;
    }
    
    
    public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
        mHostnameVerifier = hostnameVerifier;
    }
    
    /**
     * 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 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 {
                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;
        }
    }
    
}




Java Source Code List

com.owncloud.android.lib.common.OwnCloudAccount.java
com.owncloud.android.lib.common.OwnCloudBasicCredentials.java
com.owncloud.android.lib.common.OwnCloudBearerCredentials.java
com.owncloud.android.lib.common.OwnCloudClientFactory.java
com.owncloud.android.lib.common.OwnCloudClientManagerFactory.java
com.owncloud.android.lib.common.OwnCloudClientManager.java
com.owncloud.android.lib.common.OwnCloudClient.java
com.owncloud.android.lib.common.OwnCloudCredentialsFactory.java
com.owncloud.android.lib.common.OwnCloudCredentials.java
com.owncloud.android.lib.common.OwnCloudSamlSsoCredentials.java
com.owncloud.android.lib.common.SimpleFactoryManager.java
com.owncloud.android.lib.common.SingleSessionManager.java
com.owncloud.android.lib.common.accounts.AccountTypeUtils.java
com.owncloud.android.lib.common.accounts.AccountUtils.java
com.owncloud.android.lib.common.network.AdvancedSslSocketFactory.java
com.owncloud.android.lib.common.network.AdvancedX509TrustManager.java
com.owncloud.android.lib.common.network.BearerAuthScheme.java
com.owncloud.android.lib.common.network.BearerCredentials.java
com.owncloud.android.lib.common.network.CertificateCombinedException.java
com.owncloud.android.lib.common.network.ChunkFromFileChannelRequestEntity.java
com.owncloud.android.lib.common.network.FileRequestEntity.java
com.owncloud.android.lib.common.network.NetworkUtils.java
com.owncloud.android.lib.common.network.OnDatatransferProgressListener.java
com.owncloud.android.lib.common.network.ProgressiveDataTransferer.java
com.owncloud.android.lib.common.network.ServerNameIndicator.java
com.owncloud.android.lib.common.network.WebdavEntry.java
com.owncloud.android.lib.common.network.WebdavUtils.java
com.owncloud.android.lib.common.operations.OnRemoteOperationListener.java
com.owncloud.android.lib.common.operations.OperationCancelledException.java
com.owncloud.android.lib.common.operations.RemoteOperationResult.java
com.owncloud.android.lib.common.operations.RemoteOperation.java
com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation.java
com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation.java
com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation.java
com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation.java
com.owncloud.android.lib.resources.files.FileUtils.java
com.owncloud.android.lib.resources.files.ReadRemoteFileOperation.java
com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation.java
com.owncloud.android.lib.resources.files.RemoteFile.java
com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation.java
com.owncloud.android.lib.resources.files.RenameRemoteFileOperation.java
com.owncloud.android.lib.resources.files.UploadRemoteFileOperation.java
com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation.java
com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation.java
com.owncloud.android.lib.resources.shares.GetRemoteSharesOperation.java
com.owncloud.android.lib.resources.shares.OCShare.java
com.owncloud.android.lib.resources.shares.RemoveRemoteShareOperation.java
com.owncloud.android.lib.resources.shares.ShareType.java
com.owncloud.android.lib.resources.shares.ShareUtils.java
com.owncloud.android.lib.resources.shares.ShareXMLParser.java
com.owncloud.android.lib.resources.status.GetRemoteStatusOperation.java
com.owncloud.android.lib.resources.status.OwnCloudVersion.java
com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation.java
com.owncloud.android.lib.sampleclient.FilesArrayAdapter.java
com.owncloud.android.lib.sampleclient.MainActivity.java
com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory.java
com.owncloud.android.lib.test_project.TestActivity.java