Saltar al contenido

Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: protocolo de enlace SSL cancelado: falla en la biblioteca SSL, generalmente un error de protocolo

Luego de mucho luchar hemos encontrado la respuesta de este atasco que agunos usuarios de nuestra web han tenido. Si tienes algo que aportar no dudes en dejar tu comentario.

Solución:

Encontré la solución analizando los paquetes de datos usando wirehark. Lo que encontré es que mientras hacía una conexión segura, Android estaba volviendo a SSLv3 de TLSv1 . Es un error en las versiones de Android <4.4 y se puede solucionar eliminando el protocolo SSLv3 de la lista de Protocolos habilitados. Hice una clase socketFactory personalizada llamada NoSSLv3SocketFactory.java. Use esto para hacer un 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 enabledProtocols = new ArrayList(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);
    


Use esta clase así mientras se conecta:

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();

ACTUALIZAR :

Ahora, la solución correcta sería instalar un proveedor de seguridad más nuevo utilizando Google Play Services:

    ProviderInstaller.installIfNeeded(getApplicationContext());

Esto efectivamente le da a su aplicación acceso a una versión más reciente de OpenSSL y Java Security Provider, que incluye soporte para TLSv1.2 en SSLEngine. Una vez instalado el nuevo proveedor, puede crear un SSLEngine que admita SSLv3, TLSv1, TLSv1.1 y TLSv1.2 de la forma habitual:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

O puede restringir los protocolos habilitados usando engine.setEnabledProtocols.

No olvide agregar la siguiente dependencia (verifique la última versión aquí):

implementation 'com.google.android.gms:play-services-auth:17.0.0'

Para obtener más información, consulte este enlace.

Guión

Recibía excepciones de SSLHandshake en dispositivos que ejecutan versiones de Android anteriores a Android 5.0. En mi caso de uso, también quería crear un TrustManager para confiar en el certificado de mi cliente.

Implementé NoSSLv3SocketFactory y NoSSLv3Factory para eliminar SSLv3 de la lista de protocolos compatibles de mi cliente, pero no pude hacer que ninguna de estas soluciones funcionara.

Algunas cosas que aprendí:

  • En dispositivos anteriores a Android 5.0, los protocolos TLSv1.1 y TLSv1.2 no están habilitados de forma predeterminada.
  • El protocolo SSLv3 no está deshabilitado de forma predeterminada en dispositivos anteriores a Android 5.0.
  • SSLv3 no es un protocolo seguro y, por lo tanto, es conveniente eliminarlo de la lista de protocolos admitidos de nuestro cliente antes de realizar una conexión.

Que funciono para mi

Permitir la seguridad de Android Provider para actualizar al iniciar su aplicación.

El proveedor predeterminado anterior a 5.0+ no deshabilita SSLv3. Siempre que tenga acceso a los servicios de Google Play, es relativamente sencillo parchear el proveedor de seguridad de Android desde su aplicación.

private void updateAndroidSecurityProvider(Activity callingActivity) 
    try 
        ProviderInstaller.installIfNeeded(this);
     catch (GooglePlayServicesRepairableException e) 
        // Thrown when Google Play Services is not installed, up-to-date, or enabled
        // Show dialog to allow users to install, update, or otherwise enable Google Play services.
        GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
     catch (GooglePlayServicesNotAvailableException e) 
        Log.e("SecurityException", "Google Play Services not available.");
    

Si ahora crea su OkHttpClient o HttpURLConnection, TLSv1.1 y TLSv1.2 deberían estar disponibles como protocolos y SSLv3 debería eliminarse. Si el cliente / conexión (o más específicamente es SSLContext) se inicializó antes de llamar ProviderInstaller.installIfNeeded(...) entonces tendrá que ser recreado.

No olvide agregar la siguiente dependencia (la última versión se encuentra aquí):

compile 'com.google.android.gms:play-services-auth:16.0.1'

Fuentes:

  • Aplicación de parches al proveedor de seguridad con ProviderInstaller Provider
  • Hacer que SSLEngine use TLSv1.2 en Android (4.4.2)

Aparte

No necesitaba establecer explícitamente qué algoritmos de cifrado debería usar mi cliente, pero encontré una publicación SO que recomendaba los que se consideraban más seguros en el momento de escribir este artículo: ¿Qué suites de cifrado habilitar para SSL Socket?

Además, debe saber que puede forzar TLS v1.2 para dispositivos con Android 4.0 que no lo tienen habilitado de forma predeterminada:

Coloque este código en onCreate () de su archivo de aplicación:

try 
        ProviderInstaller.installIfNeeded(getApplicationContext());
        SSLContext sslContext;
        sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, null);
        sslContext.createSSLEngine();
     catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
            | NoSuchAlgorithmException | KeyManagementException e) 
        e.printStackTrace();
    

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *