De acordo com os documentos no Android para SSLSocket
e SSLContext
, os protocolos TLS v1.1 e v1.2 são suportados no nível de API 16+, mas não são ativados por padrão. http://developer.Android. com/reference/javax/net/ssl/SSLSocket.htmlhttp://developer.Android.com/reference/javax/net/ssl/SSLContext.html
Como posso ativá-lo em um dispositivo com Android 4.1 ou posterior (mas abaixo de 5.0)?
Eu tentei criar um personalizado SSLSocketFactory que permite todos os protocolos suportados quando Socket
são criados e depois usar minha implementação personalizada como:
HttpsURLConnection.setDefaultSSLSocketFactory (new MySSLSocketFactory ());
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLContext sc;
private SSLSocketFactory ssf;
public MySSLSocketFactory() {
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, null, null);
ssf = sc.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose)
throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(s, Host, port, autoClose);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public String[] getDefaultCipherSuites() {
return ssf.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return ssf.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
SSLSocket ss = (SSLSocket) ssf.createSocket(Host, port);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(Host, port);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
SSLSocket ss = (SSLSocket) ssf.createSocket(Host, port, localHost, localPort);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(address, port, localAddress, localPort);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
}
Mas ainda dá uma exceção ao tentar estabelecer uma conexão com um servidor no qual somente o TLS 1.2 está habilitado.
Aqui está a exceção que recebo:
09/09 09: 21: 38.427: W/System.err (2496): Javax.net.ssl.SSLHandshakeException: Javax.net.ssl.SSLProtocolException: SSL handshake cancelado: ssl = 0xb7fa0620: Falha na biblioteca SSL, geralmente um erro de protocolo
03-09 09: 21: 38.427: W/System.err (2496): erro: 14077410: SSL Rotinas: SSL23_GET_SERVER_HELLO: falha de handshake de alerta sslv3 (Externo/openssl/ssl/s23_clnt. c: 741 0xa90e6990: 0x00000000)
2 maneiras de ativar o TLSv1.1 e o TLSv1.2:
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), port));
Eu resolvi este problema seguindo a indicação fornecida no artigo http://blog.dev-area.net/2015/08/13/Android-4-1-enable-tls-1-1and-tls-1 -2/ com pequenas mudanças.
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
SSLSocketFactory noSSLv3Factory = null;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KitKat) {
noSSLv3Factory = new TLSSocketFactory(sslContext.getSocketFactory());
} else {
noSSLv3Factory = sslContext.getSocketFactory();
}
connection.setSSLSocketFactory(noSSLv3Factory);
Este é o código do TLSSocketFactory personalizado:
public static class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory(SSLSocketFactory delegate) throws KeyManagementException, NoSuchAlgorithmException {
internalSSLSocketFactory = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
/*
* Utility methods
*/
private static Socket enableTLSOnSocket(Socket socket) {
if (socket != null && (socket instanceof SSLSocket)
&& isTLSServerEnabled((SSLSocket) socket)) { // skip the fix if server doesn't provide there TLS version
((SSLSocket) socket).setEnabledProtocols(new String[]{TLS_v1_1, TLS_v1_2});
}
return socket;
}
private static boolean isTLSServerEnabled(SSLSocket sslSocket) {
System.out.println("__prova__ :: " + sslSocket.getSupportedProtocols().toString());
for (String protocol : sslSocket.getSupportedProtocols()) {
if (protocol.equals(TLS_v1_1) || protocol.equals(TLS_v1_2)) {
return true;
}
}
return false;
}
}
Editar: Obrigado a ademar111190 para a implementação do kotlin ( link )
class TLSSocketFactory constructor(
private val internalSSLSocketFactory: SSLSocketFactory
) : SSLSocketFactory() {
private val protocols = arrayOf("TLSv1.2", "TLSv1.1")
override fun getDefaultCipherSuites(): Array<String> = internalSSLSocketFactory.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String> = internalSSLSocketFactory.supportedCipherSuites
override fun createSocket(s: Socket, Host: String, port: Int, autoClose: Boolean) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, Host, port, autoClose))
override fun createSocket(Host: String, port: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port))
override fun createSocket(Host: String, port: Int, localHost: InetAddress, localPort: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port, localHost, localPort))
override fun createSocket(Host: InetAddress, port: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port))
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))
private fun enableTLSOnSocket(socket: Socket?) = socket?.apply {
if (this is SSLSocket && isTLSServerEnabled(this)) {
enabledProtocols = protocols
}
}
private fun isTLSServerEnabled(sslSocket: SSLSocket) = sslSocket.supportedProtocols.any { it in protocols }
}
Eu tenho algumas adições às respostas acima mencionadas É de facto um hack mencionado por Jesse Wilson de okhttp, square here . De acordo com esse hack, eu tive que renomear minha variável SSLSocketFactory para
private SSLSocketFactory delegate;
Esta é minha classe TLSSocketFactory
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(delegate.createSocket());
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
e é assim que eu usei com okhttp e retrofit
OkHttpClient client=new OkHttpClient();
try {
client = new OkHttpClient.Builder()
.sslSocketFactory(new TLSSocketFactory())
.build();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
Você deveria usar
SSLContext.getInstance("TLSv1.2");
para versão específica do protocolo.
A segunda exceção ocorreu porque o padrão socketFactory utilizava o protocolo SSLv3 de fallback para falhas.
Você pode usar NoSSLFactory da resposta principal aqui para sua supressão Como desativar o SSLv3 no Android para HttpsUrlConnection?
Além disso, você deve iniciar o SSLContext com todos os seus certificados (cliente e confiáveis, se precisar)
Mas tudo isso é inútil sem usar
ProviderInstaller.installIfNeeded(getContext())
Veja mais informações com o cenário de uso adequado https://developer.Android.com/training/articles/security-gms-provider.html
Espero que ajude.
Como o OP disse, os protocolos TLS v1.1 e v1.2 são suportados no nível de API 16+, mas não são ativados por padrão, só precisamos ativá-lo.
Exemplo aqui usa HttpsUrlConnection
, não HttpUrlConnection
. Siga https://blog.dev-area.net/2015/08/13/Android-4-1-enable-tls-1-1and-tls-1-2/ , podemos criar um fábrica
class MyFactory extends SSLSocketFactory {
private javax.net.ssl.SSLSocketFactory internalSSLSocketFactory;
public MyFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
Não importa qual biblioteca de rede você usa, certifique-se de que ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
seja chamado para que o Socket habilite os protocolos TLS.
Agora, você pode usar isso em HttpsUrlConnection
class MyHttpRequestTask extends AsyncTask<String,Integer,String> {
@Override
protected String doInBackground(String... params) {
String my_url = params[0];
try {
URL url = new URL(my_url);
HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection();
httpURLConnection.setSSLSocketFactory(new MyFactory());
// setting the Request Method Type
httpURLConnection.setRequestMethod("GET");
// adding the headers for request
httpURLConnection.setRequestProperty("Content-Type", "application/json");
String result = readStream(httpURLConnection.getInputStream());
Log.e("My Networking", "We have data" + result.toString());
}catch (Exception e){
e.printStackTrace();
Log.e("My Networking", "Oh no, error occurred " + e.toString());
}
return null;
}
private static String readStream(InputStream is) throws IOException {
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("US-ASCII")));
StringBuilder total = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
total.append(line);
}
if (reader != null) {
reader.close();
}
return total.toString();
}
}
Por exemplo
new MyHttpRequestTask().execute(myUrl);
Além disso, lembre-se de usar o minSdkVersion
no build.gradle para 16
minSdkVersion 16
Adicione a biblioteca play-services-safetynet
no Android build.gradle
:
implementation 'com.google.Android.gms:play-services-safetynet:+'
e adicione este código ao seu MainApplication.Java
:
@Override
public void onCreate() {
super.onCreate();
upgradeSecurityProvider();
SoLoader.init(this, /* native exopackage */ false);
}
private void upgradeSecurityProvider() {
ProviderInstaller.installIfNeededAsync(this, new ProviderInstallListener() {
@Override
public void onProviderInstalled() {
}
@Override
public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
// GooglePlayServicesUtil.showErrorNotification(errorCode, MainApplication.this);
GoogleApiAvailability.getInstance().showErrorNotification(MainApplication.this, errorCode);
}
});
}
@Inherently Curious - obrigado por postar isso. Você está quase lá - você tem que adicionar mais dois parâmetros ao método SSLContext.init ().
TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() };
sc.init(null, trustManagers, new SecureRandom());
vai começar a funcionar. Mais uma vez muito obrigado por postar isso. Eu resolvi esse problema com o seu código.