-
Notifications
You must be signed in to change notification settings - Fork 9.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support enabling TLSv1.2 on Android 4.1-4.4. #2372
Comments
As a quick fix, try renaming the |
No action for us to take here. |
Had the same issue on Android < 5.0 (16 <= API < 20). Thanks to your posts, I was able to make this work, so for anyone who gets here, this is the out-of-the-box solution. At the time of this writing, I'm using Edit: Add import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* Enables TLS v1.2 when creating SSLSockets.
* <p/>
* For some reason, android supports TLS v1.2 from API 16, but enables it by
* default only from API 20.
* @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
* @see SSLSocketFactory
*/
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_V12_ONLY = {"TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory base) {
this.delegate = base;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
}
return s;
}
} Then, add this method somewhere in your code: public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
// a more robust version is to pass a custom X509TrustManager
// as the second parameter and make checkServerTrusted to accept your server.
// Credits: https://github.com/square/okhttp/issues/2372#issuecomment-1774955225
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
client.connectionSpecs(specs);
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
return client;
} And when you create your private OkHttpClient getNewHttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cache(null)
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
} Also, be sure to use well known CAs for your server side certificates when targeting older Androids. Credits to @StuStirling and @techiebrij: To check your server side certificates: Or: For anyone else that may be struggling with this, the thing that fixed mine was to install the latest security fixes that are bundled with Google Play Services.
After doing this, the solution for enabling TLS1.2 worked. |
@gotev thanks for sharing, any particular reason you've added the |
It's like that to be as generic as possibile. You could always remove them from the client if you only use TLS 1.2 |
Seems like client.sslSocketFactory(sslSocketFactory) is deprecated and the documentation recommends using client.sslSocketFactory(sslSocketFactory, X509TrustManager trustManager). How do i get an X509TrustManager object? just create new ? |
SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); |
Slightly unrelated. But once I make the changes, how do I actually confirm that app and server are indeed using TLS 1.2 (on my server TLS 1.0, 1.1 and 1.2 all are enabled). Is there a hint in the HTTP packet ? |
Check the Handshake object on the Response. |
Has anyone actually come up with a working solution for KitKat? |
This is an intermediate step, and an experiment on the way to supporting TLS1.2 on Android API<=21 I can't override the trust manager in OkApacheClient though, and it's deprecated anyway, so we need to move to the full OkHttp Request/Response implementation, and alter the OkHttpClient builder invocation like so: square/okhttp#2372 (comment)
This is an intermediate step, and an experiment on the way to supporting TLS1.2 on Android API<=21 I can't override the trust manager in OkApacheClient though, and it's deprecated anyway, so we need to move to the full OkHttp Request/Response implementation, and alter the OkHttpClient builder invocation like so: square/okhttp#2372 (comment)
Thanks to gotev's solution (on Sep 5th 2016), allowed me to make https work on Android 4.x up-to 5.0. That said, it didn't work out of the box because this solution doesn't support servers having self-signed certificates. I had to change the init into this:
Also, the connectionSpecs() call turned out to be useless. |
@3c71 instead of self signed, use https://letsencrypt.org/ |
@gotev, thanks, however I don't use okhttp to connect to my own site, which I have a valid certificate for. I'm obviously referring to all those sites with self-signed certificates, like all those routers or NAS out there which can't actually be accessed with HTTPs unless certificate is either trusted automatically or manually confirmed. In both cases, it's mandatory to change the init() call. |
Ehrm.. I don't want to be that guy, but why did you decide to drop older android versions altogether? (rather than enabling this) And I mean, it's not even like the market for this wouldn't be there given the number of referenced issues above. |
Square doesn't ship software on Android 4.4 and I don’t want to do the work to test & support a platform I don’t use. In addition to 200 lines of code, there’s also code to integrate Conscrypt. Conscrypt is native code and there’s cost to making that work reliably everywhere. I expect that if anyone really cared enough, they’d maintain an unofficial backport! @mirh is that you? |
We have slammed into this problem as well because we are providing a free healthcare app to a poor part of the world where many suffering users can only afford a Samsung J1 Ace that is stuck on an non-upgradeable Android 4.4. So lack of support for older Androids turns out to be a rest-of-the-world-problem rather than a first-world-problem. |
@c4augustus some options to consider:
|
Quick question: how do I configure Conscrypt? |
@c4augustus the test for this was failing with
|
@gotev for me to work (on a specific device without Google Play Services), I had to call |
Our lawyers and security consultants claim that for PCI compliance*, we must disable TLS 1.0 and 1.1 on our servers. For some confusing reason, Android has supported TLS 1.2 since API 16 (android 4.1) but enabled it by default only since API 20 (android "4.4W").
With okhttp 2.6, we were able to force use of TLS 1.2 with:
where
Tls12SocketFactory
is this.However, okhttp 3.1 uses some kind of reflection on internal implementation details of the
SSLSocketFactory
, so the above implementation no longer works. And, indeed, it's a bit silly to make callers write so much code anyway. SpecifyingTLS_1_2
in theConnectionSpec
should be enough to get TLSv1.2 whenever it is supported.As far as I can tell, the only reason why the custom socket factory is needed in the first place is that
ConnectionSpec.supportedSpec()
callsSSLSocket.getEnabledProtocols()
to learn the list of protocols supported by the system, so on Android 4.x where TLS 1.2 is supported but not enabled by default, OkHttp thinks 1.2 is not supported at all.Sorry for this long bug report: I think the fix is as simple as changing
getEnabledProtocols()
above togetSupportedProtocols()
but wanted to submit this bug for discussion before making a PR with such a change, in case there is some affirmative reason why it's the other way now.* Originally I understood the PCI compliance deadline to be June 2016; however, it seems like it has since been changed to be June 2018. Regardless, OkHttp should support this change for users that want it.
The text was updated successfully, but these errors were encountered: