diff --git a/app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettings.java b/app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettings.java index 319b63a3..3f7e105a 100644 --- a/app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettings.java +++ b/app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettings.java @@ -54,7 +54,7 @@ public class SeedstuffSettings extends SeedboxSettingsImpl implements SeedboxSet null, 443, true, - true, + false, null, "/user/" + user, true, diff --git a/app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettings.java b/app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettings.java index 0f9787ee..e818ab6d 100644 --- a/app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettings.java +++ b/app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettings.java @@ -55,7 +55,7 @@ public class XirvikDediSettings extends SeedboxSettingsImpl implements SeedboxSe null, type == Daemon.uTorrent? 5010: 443, type == Daemon.uTorrent? false: true, - type == Daemon.uTorrent? false: true, + false, null, type == Daemon.Deluge? "/deluge": null, true, diff --git a/app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettings.java b/app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettings.java index 7de3d8e2..cb22c229 100644 --- a/app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettings.java +++ b/app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettings.java @@ -54,7 +54,7 @@ public class XirvikSemiSettings extends SeedboxSettingsImpl implements SeedboxSe null, 443, true, - true, + false, null, "/RPC2", true, diff --git a/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettings.java b/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettings.java index 31e12196..bd65cdc5 100644 --- a/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettings.java +++ b/app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettings.java @@ -55,7 +55,7 @@ public class XirvikSharedSettings extends SeedboxSettingsImpl implements Seedbox null, 443, true, - true, + false, null, rpc, true, diff --git a/app/src/main/java/org/transdroid/daemon/Vuze/VuzeXmlOverHttpClient.java b/app/src/main/java/org/transdroid/daemon/Vuze/VuzeXmlOverHttpClient.java index f6cb06bd..67eec402 100644 --- a/app/src/main/java/org/transdroid/daemon/Vuze/VuzeXmlOverHttpClient.java +++ b/app/src/main/java/org/transdroid/daemon/Vuze/VuzeXmlOverHttpClient.java @@ -47,7 +47,7 @@ import org.base64.android.Base64; import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.DaemonSettings; import org.transdroid.daemon.DaemonException.ExceptionType; -import org.transdroid.daemon.util.FakeSocketFactory; +import org.transdroid.daemon.util.TlsSniSocketFactory; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; @@ -114,12 +114,16 @@ public class VuzeXmlOverHttpClient { HttpConnectionParams.setSoTimeout(httpParams, settings.getTimeoutInMilliseconds()); SchemeRegistry registry = new SchemeRegistry(); + SocketFactory httpsSocketFactory; + if (settings.getSslTrustKey() != null) { + httpsSocketFactory = new TlsSniSocketFactory(settings.getSslTrustKey()); + } else if (settings.getSslTrustAll()) { + httpsSocketFactory = new TlsSniSocketFactory(true); + } else { + httpsSocketFactory = new TlsSniSocketFactory(); + } registry.register(new Scheme("http", new PlainSocketFactory(), 80)); - SocketFactory https_socket = - settings.getSslTrustAll() ? new FakeSocketFactory() - : settings.getSslTrustKey() != null ? new FakeSocketFactory(settings.getSslTrustKey()) - : SSLSocketFactory.getSocketFactory(); - registry.register(new Scheme("https", https_socket, 443)); + registry.register(new Scheme("https", httpsSocketFactory, 443)); client = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams, registry), httpParams); if (settings.shouldUseAuthentication()) { @@ -128,7 +132,7 @@ public class VuzeXmlOverHttpClient { } else { username = settings.getUsername(); password = settings.getPassword(); - ((DefaultHttpClient) client).getCredentialsProvider().setCredentials( + client.getCredentialsProvider().setCredentials( new AuthScope(postMethod.getURI().getHost(), postMethod.getURI().getPort(), AuthScope.ANY_REALM), new UsernamePasswordCredentials(username, password)); } @@ -141,7 +145,7 @@ public class VuzeXmlOverHttpClient { /** * Convenience constructor. Creates new instance based on server String address * @param settings The server connection settings - * @param uri The URI of the XML RPC to connect to + * @param url The URL of the XML RPC to connect to * @throws DaemonException Thrown when settings are missing or conflicting */ public VuzeXmlOverHttpClient(DaemonSettings settings, String url) throws DaemonException { diff --git a/app/src/main/java/org/transdroid/daemon/util/FakeSocketFactory.java b/app/src/main/java/org/transdroid/daemon/util/FakeSocketFactory.java deleted file mode 100644 index 83404669..00000000 --- a/app/src/main/java/org/transdroid/daemon/util/FakeSocketFactory.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.transdroid.daemon.util; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; - -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.conn.scheme.LayeredSocketFactory; -import org.apache.http.conn.scheme.SocketFactory; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -public class FakeSocketFactory implements SocketFactory, LayeredSocketFactory { - private String certKey = null; - private SSLContext sslcontext = null; - - public FakeSocketFactory(String certKey){ - this.certKey = certKey; - } - public FakeSocketFactory() { } - - private static SSLContext createEasySSLContext(String certKey) throws IOException { - try { - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, new TrustManager[] { new FakeTrustManager(certKey) }, null); - return context; - } catch (Exception e) { - throw new IOException(e.getMessage()); - } - } - - private SSLContext getSSLContext() throws IOException { - if (this.sslcontext == null) { - this.sslcontext = createEasySSLContext(this.certKey); - } - return this.sslcontext; - } - - @Override - public Socket connectSocket(Socket sock, String host, int port, - InetAddress localAddress, int localPort, HttpParams params) throws IOException, - UnknownHostException, ConnectTimeoutException { - int connTimeout = HttpConnectionParams.getConnectionTimeout(params); - int soTimeout = HttpConnectionParams.getSoTimeout(params); - - InetSocketAddress remoteAddress = new InetSocketAddress(host, port); - SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); - - if ((localAddress != null) || (localPort > 0)) { - // we need to bind explicitly - if (localPort < 0) { - localPort = 0; // indicates "any" - } - InetSocketAddress isa = new InetSocketAddress(localAddress, - localPort); - sslsock.bind(isa); - } - - sslsock.connect(remoteAddress, connTimeout); - sslsock.setSoTimeout(soTimeout); - return sslsock; - } - - @Override - public Socket createSocket() throws IOException { - return getSSLContext().getSocketFactory().createSocket(); - } - - @Override - public boolean isSecure(Socket arg0) throws IllegalArgumentException { - return true; - } - - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) - throws IOException, UnknownHostException { - return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); - } - -} diff --git a/app/src/main/java/org/transdroid/daemon/util/FakeTrustManager.java b/app/src/main/java/org/transdroid/daemon/util/FakeTrustManager.java deleted file mode 100644 index 6b82eade..00000000 --- a/app/src/main/java/org/transdroid/daemon/util/FakeTrustManager.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.transdroid.daemon.util; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.X509TrustManager; - -public class FakeTrustManager implements X509TrustManager { - private String certKey = null; - private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {}; - private static final String LOG_NAME = "TrustManager"; - - public FakeTrustManager(String certKey){ - super(); - this.certKey = certKey; - } - FakeTrustManager(){ - super(); - } - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if( this.certKey == null ){ - // This is the Accept All certificates case. - return; - } - - // Otherwise, we have a certKey defined. We should now examine the one we got from the server. - // They match? All is good. They don't, throw an exception. - String our_key = this.certKey.replaceAll("[^a-fA-F0-9]+", ""); - try { - //Assume self-signed root is okay? - X509Certificate ss_cert = chain[0]; - String thumbprint = FakeTrustManager.getThumbPrint(ss_cert); - DLog.d(LOG_NAME, thumbprint); - if( our_key.equalsIgnoreCase(thumbprint) ){ - return; - } - else { - throw new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value."); - } - } catch (NoSuchAlgorithmException e) { - throw new CertificateException("Unable to check self-signed cert, unknown algorithm. " + e.toString()); - } - - } - - public boolean isClientTrusted(X509Certificate[] chain) { - return true; - } - - public boolean isServerTrusted(X509Certificate[] chain) { - return true; - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return _AcceptedIssuers; - } - - // Thank you: http://stackoverflow.com/questions/1270703/how-to-retrieve-compute-an-x509-certificates-thumbprint-in-java - private static String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmException, CertificateEncodingException { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - byte[] der = cert.getEncoded(); - md.update(der); - byte[] digest = md.digest(); - return hexify(digest); - } - - private static String hexify (byte bytes[]) { - char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - StringBuffer buf = new StringBuffer(bytes.length * 2); - - for (int i = 0; i < bytes.length; ++i) { - buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); - buf.append(hexDigits[bytes[i] & 0x0f]); - } - - return buf.toString(); - } -} diff --git a/app/src/main/java/org/transdroid/daemon/util/HttpHelper.java b/app/src/main/java/org/transdroid/daemon/util/HttpHelper.java index 9161a8a9..bee9acd5 100644 --- a/app/src/main/java/org/transdroid/daemon/util/HttpHelper.java +++ b/app/src/main/java/org/transdroid/daemon/util/HttpHelper.java @@ -41,7 +41,6 @@ import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.scheme.SocketFactory; -import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.HttpEntityWrapper; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; @@ -91,7 +90,7 @@ public class HttpHelper { * Creates a standard Apache HttpClient that is thread safe, supports different SSL auth methods and basic * authentication * @param sslTrustAll Whether to trust all SSL certificates - * @param sslTrustkey A specific SSL key to accept exclusively + * @param sslTrustKey A specific SSL key to accept exclusively * @param timeout The connection timeout for all requests * @param authAddress The authentication domain address * @param authPort The authentication domain port number @@ -99,15 +98,21 @@ public class HttpHelper { * @throws DaemonException Thrown when information (such as username/password) is missing */ public static DefaultHttpClient createStandardHttpClient(boolean userBasicAuth, String username, String password, - boolean sslTrustAll, String sslTrustkey, int timeout, String authAddress, int authPort) + boolean sslTrustAll, String sslTrustKey, int timeout, String authAddress, int authPort) throws DaemonException { // Register http and https sockets SchemeRegistry registry = new SchemeRegistry(); + SocketFactory httpsSocketFactory; + if (sslTrustKey != null) { + httpsSocketFactory = new TlsSniSocketFactory(sslTrustKey); + } else if (sslTrustAll) { + httpsSocketFactory = new TlsSniSocketFactory(true); + } else { + httpsSocketFactory = new TlsSniSocketFactory(); + } registry.register(new Scheme("http", new PlainSocketFactory(), 80)); - SocketFactory https_socket = sslTrustAll ? new FakeSocketFactory() - : sslTrustkey != null ? new FakeSocketFactory(sslTrustkey) : SSLSocketFactory.getSocketFactory(); - registry.register(new Scheme("https", https_socket, 443)); + registry.register(new Scheme("https", httpsSocketFactory, 443)); // Standard parameters HttpParams httpparams = new BasicHttpParams(); @@ -124,7 +129,7 @@ public class HttpHelper { if (userBasicAuth) { if (username == null || password == null) { throw new DaemonException(ExceptionType.AuthenticationFailure, - "No username or password was provided while we hadauthentication enabled"); + "No username or password was provided while we had authentication enabled"); } httpclient.getCredentialsProvider().setCredentials( new AuthScope(authAddress, authPort, AuthScope.ANY_REALM), diff --git a/app/src/main/java/org/transdroid/daemon/util/IgnoreSSLTrustManager.java b/app/src/main/java/org/transdroid/daemon/util/IgnoreSSLTrustManager.java new file mode 100644 index 00000000..25d2b033 --- /dev/null +++ b/app/src/main/java/org/transdroid/daemon/util/IgnoreSSLTrustManager.java @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2013 Eric Kok et al. + * + * Transdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Transdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transdroid. If not, see . + */ +package org.transdroid.daemon.util; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +public class IgnoreSSLTrustManager implements X509TrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // Perform no check whatsoever on the validity of the SSL certificate + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // Perform no check whatsoever on the validity of the SSL certificate + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + +} diff --git a/app/src/main/java/org/transdroid/daemon/util/SelfSignedTrustManager.java b/app/src/main/java/org/transdroid/daemon/util/SelfSignedTrustManager.java new file mode 100644 index 00000000..41bf196a --- /dev/null +++ b/app/src/main/java/org/transdroid/daemon/util/SelfSignedTrustManager.java @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2013 Eric Kok et al. + * + * Transdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Transdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transdroid. If not, see . + */ +package org.transdroid.daemon.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +public class SelfSignedTrustManager implements X509TrustManager { + + private static final X509Certificate[] acceptedIssuers = new X509Certificate[]{}; + private static final String LOG_NAME = "TrustManager"; + + private String certKey = null; + + public SelfSignedTrustManager(String certKey) { + super(); + this.certKey = certKey; + } + + // Thank you: http://stackoverflow.com/questions/1270703/how-to-retrieve-compute-an-x509-certificates-thumbprint-in-java + private static String getThumbPrint(X509Certificate cert) + throws NoSuchAlgorithmException, CertificateEncodingException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] der = cert.getEncoded(); + md.update(der); + byte[] digest = md.digest(); + return hexify(digest); + } + + private static String hexify(byte bytes[]) { + + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + StringBuffer buf = new StringBuffer(bytes.length * 2); + for (int i = 0; i < bytes.length; ++i) { + buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]); + buf.append(hexDigits[bytes[i] & 0x0f]); + } + return buf.toString(); + + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (this.certKey == null) { + throw new CertificateException("Requires a non-null certificate key in SHA-1 format to match."); + } + + // Qe have a certKey defined. We should now examine the one we got from the server. + // They match? All is good. They don't, throw an exception. + String ourKey = this.certKey.replaceAll("[^a-fA-F0-9]+", ""); + try { + // Assume self-signed root is okay? + X509Certificate sslCert = chain[0]; + String thumbprint = SelfSignedTrustManager.getThumbPrint(sslCert); + DLog.d(LOG_NAME, thumbprint); + if (ourKey.equalsIgnoreCase(thumbprint)) { + return; + } else { + CertificateException certificateException = + new CertificateException("Certificate key [" + thumbprint + "] doesn't match expected value."); + DLog.e(SelfSignedTrustManager.class.getSimpleName(), certificateException.toString()); + throw certificateException; + } + } catch (NoSuchAlgorithmException e) { + throw new CertificateException("Unable to check self-signed cert, unknown algorithm. " + e.toString()); + } + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return acceptedIssuers; + } + +} diff --git a/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java b/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java new file mode 100644 index 00000000..cafb45ed --- /dev/null +++ b/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java @@ -0,0 +1,148 @@ +/* + * Copyright 2010-2013 Eric Kok et al. + * + * Transdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Transdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transdroid. If not, see . + */ +package org.transdroid.daemon.util; + +import android.net.SSLCertificateSocketFactory; +import android.os.Build; + +import org.apache.http.conn.scheme.LayeredSocketFactory; +import org.apache.http.conn.ssl.StrictHostnameVerifier; +import org.apache.http.params.HttpParams; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +/** + * Implements an HttpClient socket factory with extensive support for SSL. Many thanks to + * http://blog.dev001.net/post/67082904181/android-using-sni-and-tlsv1-2-with-apache-httpclient for the base + * implementation. + *

+ * Firstly, all SSL protocols that a particular Android version support will be enabled (according to + * http://developer.android.com/reference/javax/net/ssl/SSLSocket.html). This currently includes SSL v3 and TLSv1.0, + * v1.1 and v1.2. + *

+ * Second, SNI is supported for host name verification. For Android 4.2+, which supports it natively, the default + * (strict) hostname verifier is used. For Android 4.1 and earlier it is possibly supported through reflexion on the + * same methods. + *

+ * Third, self-signed certificates are supported through the checking of the received certificate key with a given SHA-1 + * encoded hex of the self-signed certificate key. When a key is given but not a correct match, the thumbprint of the + * server certificate is given, such that the correct SHA-1 hash to use can be foudn in the log. + *

+ * Finally, the ignoring of all SSL certificates (and hostname) is possible (which is obviously very insecure!). + */ +public class TlsSniSocketFactory implements LayeredSocketFactory { + + private final static HostnameVerifier hostnameVerifier = new StrictHostnameVerifier(); + + private final boolean acceptAllCertificates; + private final String selfSignedCertificateKey; + + public TlsSniSocketFactory() { + this.acceptAllCertificates = false; + this.selfSignedCertificateKey = null; + } + + public TlsSniSocketFactory(String certKey) { + this.acceptAllCertificates = false; + this.selfSignedCertificateKey = certKey; + } + + public TlsSniSocketFactory(boolean acceptAllCertificates) { + this.acceptAllCertificates = acceptAllCertificates; + this.selfSignedCertificateKey = null; + } + + // Plain TCP/IP (layer below TLS) + + @Override + public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, + HttpParams params) throws IOException { + return null; + } + + @Override + public Socket createSocket() throws IOException { + return null; + } + + @Override + public boolean isSecure(Socket s) throws IllegalArgumentException { + if (s instanceof SSLSocket) { + return s.isConnected(); + } + return false; + } + + // TLS layer + + @Override + public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException { + if (autoClose) { + // we don't need the plainSocket + plainSocket.close(); + } + + SSLCertificateSocketFactory sslSocketFactory = + (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0); + + // For self-signed certificates use a custom trust manager + if (acceptAllCertificates) { + sslSocketFactory.setTrustManagers(new TrustManager[]{new IgnoreSSLTrustManager()}); + } else if (selfSignedCertificateKey != null) { + sslSocketFactory.setTrustManagers(new TrustManager[]{new SelfSignedTrustManager(selfSignedCertificateKey)}); + } + + // create and connect SSL socket, but don't do hostname/certificate verification yet + SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName(host), port); + + // enable TLSv1.1/1.2 if available + ssl.setEnabledProtocols(ssl.getSupportedProtocols()); + + // set up SNI before the handshake + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + sslSocketFactory.setHostname(ssl, host); + } else { + try { + java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); + setHostnameMethod.invoke(ssl, host); + } catch (Exception e) { + DLog.d(TlsSniSocketFactory.class.getSimpleName(), "SNI not usable: " + e); + } + } + + // verify hostname and certificate + SSLSession session = ssl.getSession(); + if (!(acceptAllCertificates || selfSignedCertificateKey != null) && !hostnameVerifier.verify(host, session)) { + throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host); + } + + DLog.d(TlsSniSocketFactory.class.getSimpleName(), + "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + + " using " + session.getCipherSuite()); + + return ssl; + } + +} diff --git a/app/src/main/res/values/changelog.xml b/app/src/main/res/values/changelog.xml index 0f2adcce..afc5f824 100644 --- a/app/src/main/res/values/changelog.xml +++ b/app/src/main/res/values/changelog.xml @@ -19,6 +19,7 @@ Transdroid 2.3.0\n - Aria2 support\n +- Improved SSL support with TLS 1.1/1.2 and SNI hostname verification\n - Fixed server checker when one is unavailable\n - Fixed Deluge magnet links from Chrome\n - Enable RSS notifications per feed\n