Nous avons écrit une application cliente sous Android qui se connecte à des serveurs https à l'aide d'apis HttpsUrlConnection. En raison de la vulnérabilité de Poodle, nous devons désactiver SSLv3 de la liste des protocoles activés lors de l'appel de toute demande.
Nous avons suivi les instructions capturées par Oracle sous la forme http://www.Oracle.com/technetwork/Java/javase/documentation/cve-2014-3566-2342133.html
et ajouté la ligne suivante avant d'appeler la connexion url
Java.lang.System.setProperty("https.protocols", "TLSv1");
Cette solution fonctionne bien avec un programme Java normal. Nous avons eu SSLHandShakeException lorsque nous avons essayé de nous connecter avec un serveur qui ne fonctionne que sur le protocole SSLv3.
Mais l'inquiétude est: le même correctif ne fonctionne pas pour Android. Est-ce que je manque quelque chose ou devrais-je essayer une autre approche pour Android? Veuillez suggérer.
J'ai trouvé la solution en analysant les paquets de données à l'aide de Wireshark. Ce que j’ai trouvé, c’est que lorsqu’il établissait une connexion sécurisée, Android retombait sur SSLv3 de TLSv1. Il s’agit d’un bogue dans les versions Android <4.4, et il peut être résolu en supprimant le protocole SSLv3 de la liste des protocoles activés. J'ai créé une classe socketFactory personnalisée appelée NoSSLv3SocketFactory.Java. Utilisez ceci pour créer une socketfactory.
/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.Apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.OutputStream;
import Java.net.InetAddress;
import Java.net.Socket;
import Java.net.SocketAddress;
import Java.net.SocketException;
import Java.nio.channels.SocketChannel;
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory{
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
@Override
public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, Host, port, autoClose));
}
@Override
public Socket createSocket(String Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress Host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(Host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
@Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
@Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
@Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
@Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
@Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
@Override
public SSLSession getSession() {
return delegate.getSession();
}
@Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
@Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
@Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
@Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
@Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
@Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
@Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
@Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
@Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
@Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
@Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
@Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
@Override
public synchronized void close() throws IOException {
delegate.close();
}
@Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
@Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
@Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
@Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
@Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
@Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
@Override
public int getLocalPort() {
return delegate.getLocalPort();
}
@Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
@Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
@Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
@Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
@Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
@Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
@Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
@Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
@Override
public boolean isBound() {
return delegate.isBound();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public boolean isConnected() {
return delegate.isConnected();
}
@Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
@Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
@Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
@Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
@Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
@Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
@Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
@Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
@Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
Utilisez cette classe comme ceci lors de la connexion:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null,
null,
null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();
METTRE À JOUR :
Maintenant, la solution correcte serait d'installer un fournisseur de sécurité plus récent en utilisant Services Google Play :
ProviderInstaller.installIfNeeded(getApplicationContext());
Cela donne effectivement à votre application un accès à une version plus récente d'OpenSSL et du fournisseur de sécurité Java, qui inclut la prise en charge de TLSv1.2 dans SSLEngine. Une fois le nouveau fournisseur installé, vous pouvez créer un SSLEngine prenant en charge SSLv3, TLSv1, TLSv1.1 et TLSv1.2 de la manière habituelle:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
Ou vous pouvez restreindre les protocoles activés à l'aide de engine.setEnabledProtocols
.
N'oubliez pas d'ajouter la dépendance suivante ( dernière version trouvée ici ):
compile 'com.google.Android.gms:play-services-auth:11.8.0'
Pour plus d'informations, consultez ce link .
Inspiré par answer de Bhavit S. Sengar, il a intégré cette technique dans un appel de méthode simple mort. Vous pouvez utiliser la bibliothèque NetCipher pour obtenir une configuration TLS moderne lorsque vous utilisez la variable HttpsURLConnection
d'Android. NetCipher configure l'instance HttpsURLConnection
pour qu'elle utilise la meilleure version TLS prise en charge, supprime la prise en charge de SSLv3 et configure la meilleure suite de chiffrements pour cette version TLS. Tout d’abord, ajoutez-le à votre build.gradle:
compile 'info.guardianproject.netcipher:netcipher:1.2'
Ou vous pouvez télécharger le fichier netcipher-1.2.jar et l'inclure directement dans votre application. Alors au lieu d'appeler:
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
Appelez ceci:
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
Au début, j'ai essayé répondre de Bhavit S. Sengar et cela a fonctionné dans la plupart des cas. Mais il y avait parfois des problèmes même lorsque le protocole SSLv3 était supprimé des protocoles activés sur un appareil Android 4.4.4. Ainsi, la bibliothèque NetCipher de Hans-Christoph Steiner est parfaite pour résoudre ce problème dans la mesure où je pouvais le tester.
Nous utilisons jsoup pour créer un tas de pages Web sur différents serveurs, nous ne pouvons donc pas définir HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
. Je suppose que c'est le même problème si vous utilisez OkHttp.
La meilleure solution à laquelle nous sommes arrivés consiste à définir le info.guardianproject.netcipher.client.TlsOnlySocketFactory
de NetCipher sur DefaultSSLSocketFactory
dans un bloc statique. Donc, il est défini pour toute la durée d'exécution de notre application:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);
Si vous souhaitez inspecter tous les détails (avec trustAllCertificates
), vous pouvez le faire ici .
utilisez cet extrait de code. Si le serveur est activé SSLv3, il échouera.
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("Host-name", 443);
socket.setEnabledProtocols(new String[] { "TLSv1"});
socket.startHandshake();
Se connecte avec un serveur https, nous avons besoin d’un certificat en négociation du côté client. Il y a 1 an, j'ai résolu un problème similaire en utilisant un certificat d'auto-signature de la manière suivante:
import Java.security.KeyManagementException;
import Java.security.NoSuchAlgorithmException;
import Java.security.SecureRandom;
import Java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
@Override
public void checkClientTrusted(
Java.security.cert.X509Certificate[] x509Certificates, String s)
throws Java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
Java.security.cert.X509Certificate[] x509Certificates, String s)
throws Java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
Utilisation côté client avant HttpsUrlConnection
HttpsTrustManager.allowAllSSL();
j'espère que ça va marcher :)
En fait, nous n'avons pas besoin de désactiver SSLV3 ou TLSV1.0. Ce qu'il nous faut juste pour activer TLSV1.1 ou TLSv1.2 sur les appareils Android <5.
Le problème est que TLSv1.1 et TLSv1.2 ne sont pas activés sur Android <5 par défaut et pour se connecter à l'aide de ce dernier protocole sécurisé, vous devez obligatoirement activer sur Android <5.
Cette solution a résolu mon problème: https://stackoverflow.com/a/45853669/3448003
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(null, null, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
httpURLConnection.setSSLSocketFactory(socketFactory);
HttpsURLConnection en utilisant TSL créer une sécurité a échoué, l'implémentation Android va revenir à SSLV3 à la connexion.
Veuillez vous référer à cette http://code.google.com/p/Android/issues/detail?id=78431
Utilisation de Bibliothèques clientes de PlayService publisher s'exécutant sur Android J'ai rencontré le même problème lors de l'exécution de sample .
Correction du problème avec la grille de @ bhavit-s-sengar ci-dessus. Devait aussi changer AndroidPublisherHelper.newTrustedTransport()
en ceci:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
// NoSSLv3SocketFactory is @bhavit-s-sengar's http://stackoverflow.com/a/29946540/8524
SSLSocketFactory noSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
NetHttpTransport.Builder netTransportBuilder = new NetHttpTransport.Builder();
netTransportBuilder.setSslSocketFactory(noSSLv3Factory);
HTTP_TRANSPORT = netTransportBuilder.build();