Selon les documents dans Android pour SSLSocket
et SSLContext
, les protocoles TLS v1.1 et v1.2 sont pris en charge par les API de niveau 16+, mais ne sont pas activés par défaut . http://developer.Android.com/ reference/javax/net/ssl/SSLSocket.htmlhttp://developer.Android.com/reference/javax/net/ssl/SSLContext.html
Comment l'activer sur un appareil fonctionnant sous Android 4.1 ou ultérieur (mais inférieur à 5.0)?
J'ai essayé de créer un SSLSocketFactory personnalisé qui active tous les protocoles pris en charge lors de la création de Socket
et utilise plus tard mon implémentation personnalisée en tant que:
HttpsURLConnection.setDefaultSSLSocketFactory (nouveau 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;
}
}
Mais il donne toujours une exception lorsqu’on essaie d’établir une connexion avec un serveur sur lequel Only TLS 1.2 est activé.
Voici l'exception que je reçois:
03-09 09: 21: 38.427: W/System.err (2496): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: négociation SSL interrompue: ssl = 0xb7fa0620: Echec dans la bibliothèque SSL, généralement une erreur de protocole
03-09 09: 21: 38.427: W/System.err (2496): erreur: 14077410: SSL routines: SSL23_GET_SERVER_HELLO: échec de la négociation de l'alerte sslv3 (external/openssl/ssl/s23_clnt.c: 741 0xa90e6990: 0x00000000)
2 façons d'activer TLSv1.1 et TLSv1.2:
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), port));
J'ai résolu ce problème en suivant les indications de l'article http://blog.dev-area.net/2015/08/13/Android-4-1-enable-tls-1-1-and-tls-1 -2/ avec de petits changements.
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);
Voici le code de la TLSSocketFactory personnalisée:
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;
}
}
Edit: Merci à ademar111190 pour l'implémentation de 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 }
}
Tu devrais utiliser
SSLContext.getInstance("TLSv1.2");
pour une version de protocole spécifique.
La deuxième exception s'est produite parce que socketFactory par défaut utilisait le protocole de remplacement SSLv3 pour les échecs.
Vous pouvez utiliser NoSSLFactory de la réponse principale ici pour sa suppression Comment désactiver SSLv3 dans Android pour HttpsUrlConnection?
De plus, vous devez initialiser SSLContext avec tous vos certificats (clients et certificats de confiance si vous en avez besoin)
Mais tout cela est inutile sans utiliser
ProviderInstaller.installIfNeeded(getContext())
Voici plus d'informations avec le scénario d'utilisation approprié https://developer.Android.com/training/articles/security-gms-provider.html
J'espère que ça aide.
Comme l'OP l'a indiqué, les protocoles TLS v1.1 et v1.2 sont pris en charge par les API de niveaux 16 et supérieur, mais ne sont pas activés par défaut. Nous devons simplement l'activer.
Exemple ici utilise HttpsUrlConnection
, pas HttpUrlConnection
. Suivez https://blog.dev-area.net/2015/08/13/Android-4-1-enable-tls-1-1-and-tls-1-2/ , nous pouvons créer un usine
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;
}
}
Quelle que soit la bibliothèque de mise en réseau que vous utilisez, assurez-vous que ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
est appelé pour que le Socket active les protocoles TLS.
Maintenant, vous pouvez utiliser cela dans 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();
}
}
Par exemple
new MyHttpRequestTask().execute(myUrl);
De plus, n'oubliez pas de déplacer minSdkVersion
dans build.gradle to 16
minSdkVersion 16
Ajouter la bibliothèque play-services-safetynet
dans Android build.gradle
:
implementation 'com.google.Android.gms:play-services-safetynet:+'
et ajoutez ce code à votre 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 - merci d'avoir posté ceci. Vous y êtes presque - vous devez ajouter deux paramètres supplémentaires à la méthode SSLContext.init ().
TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() };
sc.init(null, trustManagers, new SecureRandom());
ça va commencer à fonctionner. Encore une fois, merci beaucoup d'avoir posté ceci. J'ai résolu ce problème/mon problème avec votre code.