2011-01-19 19 views
7

Estoy desarrollando una aplicación para Android que usa SSLSocket para conectarse a un servidor. Este es el código que estoy usando:La conexión TLS usando SSLSocket es lenta en Android OS

// Connect 
if (socket == null || socket.isClosed() || !socket.isConnected()) { 
    if (socket != null && !socket.isClosed()) 
     socket.close(); 
    Log.i(getClass().toString(), "Connecting..."); 
    if (sslContext == null) { 
     sslContext = SSLContext.getInstance("TLS"); 
     sslContext.init(null, trustAllCerts, new SecureRandom()); 
    } 
    SSLSocketFactory socketFactory = sslContext.getSocketFactory(); 
    socket = (SSLSocket)socketFactory.createSocket(host, port); 
    socket.setSoTimeout(20000); 
    socket.setUseClientMode(true); 
    connected = true; 
    Log.i(getClass().toString(), "Connected."); 
} 

// Secure 
if (connected) { 
    Log.i(getClass().toString(), "Securing..."); 
    SSLSession session = socket.getSession(); 
    secured = session.isValid(); 
    if (secured) { 
     Log.i(getClass().toString(), "Secured."); 
    } 
    else 
     Log.i(getClass().toString(), "Securing failed."); 
} 

El problema es que se tarda unos 5 segundos o evento más que ver el apretón de manos TLS en la línea de abajo:

SSLSession session = socket.getSession(); 

he hecho un similares aplicación de iPhone, el apretón de manos lleva solo 1 segundo allí, así que creo que el problema no está en el servidor al que me estoy conectando, tal vez en el código anterior. La conexión en sí es lo suficientemente rápida, solo el saludo de TLS es lento.

¿Alguien sabe si es normal en Android, o si no lo es, cómo hacerlo más rápido?

Gracias.

Editado el 21.01.11:

he descubierto, que el apretón de manos es rápido cuando me conecto a otro servidor, por ejemplo paypal.com:443.

Pero me había estado conectando a otro servidor antes - un servicio .NET escrito por mí. Como había dicho antes, no pensé que el problema estaba en ese servidor porque si me conecto a él con mi aplicación de iPhone, el apretón de manos es rápido. Ahora no sé por qué es rápido en iPhone y lento en Android. Una vez establecida la conexión, lo único que hago en el servidor .NET es:

Console.WriteLine("New client connected."); 
this.sslStream = new SslStream(tcpClient.GetStream(), true); 
this.sslStream.ReadTimeout = 15000; 
this.sslStream.WriteTimeout = 15000; 

Console.WriteLine("Beginning TLS handshake..."); 
this.sslStream.AuthenticateAsServer(connection.ServerCertificate, false, SslProtocols.Tls, false); 
Console.WriteLine("TLS handshake completed."); 

Respuesta

0

he hecho algo similar a esto y es más lento que una conexión no segura. De acuerdo mi caso era https vs http y es un poco diferente, el factor SSL/TLS agregará lentitud al trato.

Tengo dos aplicaciones idénticas que se comunican con el mismo protocolo al mismo servidor, una en Android y otra en iPhone, ambas usando https. Cuando los probé, tanto en http que vería más o menos el mismo tiempo de respuesta, en https iOS fue un poco más rápido en mi caso, pero no terriblemente.

+0

Sí Sé que TLS es generalmente más lento que una conexión pura, pero es lo suficientemente rápido. Sin embargo, el apretón de manos TLS es muy lento, toma 10 segundos, no 5 como dije antes. – Arthur

2

Está utilizando un nuevo SecureRandom por conexión, en lugar de utilizar un solo pre-inicializado estático SecureRandom. Cada vez que creas un nuevo SecureRandom(), necesitas reunir la entropía para la siembra (un proceso lento).

SecureRandom no se auto-semilla hasta que se utilizó por primera vez, por lo que el retraso no se produce hasta que la llamada a getSession()

+0

Gracias por su respuesta Jumbogram, intenté con lo que dijo, ahora estoy usando solo una instancia de SecureRandom que se inicializa en el constructor. Pero no importa la frecuencia con la que me conecto y desconecto, toma aproximadamente 10 segundos ejecutar socket.getSession(); – Arthur

+0

Si fuera yo, trataría de obtener una captura de paquetes y ver dónde está la ralentización. ¿Es 1 segundo entre cada paquete? ¿Hay una larga demora entre el paquete (n) y (n + 1), pero todo lo demás es rápido? Cosas como esta – Jumbogram

+0

Buena idea, lo intentaré. Descubrí que el apretón de manos es rápido cuando conecto con otro servidor, por ejemplo, paypal.com:443. He editado mi publicación ahora, creo que tengo que cambiar algo en el servidor .NET, pero no sé qué, tal vez el problema esté en el certificado del servidor, ni idea ... – Arthur

-1

El problema es más probable en la forma en que el dispositivo valida certificados de servidor. La validación puede implicar contactar a un tercero para obtener CRL y respuestas OCSP. Si esto sucede, lleva tiempo. El iPhone probablemente no haga esto (al menos de manera predeterminada), que es un agujero de seguridad por cierto.

6

Hubo un error en las versiones anteriores de Android SDK. Aparentemente, está haciendo una búsqueda inversa de DNS innecesaria. Debes evitar que esto suceda. Aquí hay una solución que funcionó para mí. Solía ​​tomar 15 segundos, ahora toma 0-1 segundos. Espero eso ayude.

Aquí está el enlace a Google issue.

boolean connected = false; 
if (socket == null || socket.isClosed() || !socket.isConnected()) { 
    if (socket != null && !socket.isClosed()) { 
     socket.close(); 
    } 

    Log.i(getClass().toString(), "Connecting..."); 
    messages.getText().append("Connecting..."); 
    final KeyStore keyStore = KeyStore.getInstance("BKS"); 
    keyStore.load(getResources().openRawResource(R.raw.serverkey), null); 

    final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    keyManager.init(keyStore, null); 
    //keyManager.init(null, null); 

    final TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    trustFactory.init(keyStore); 

    sslContext = SSLContext.getInstance("TLS"); 
    sslContext.init(keyManager.getKeyManagers(), trustFactory.getTrustManagers(), rnd); 
    final SSLSocketFactory delegate = sslContext.getSocketFactory(); 
    SocketFactory factory = new SSLSocketFactory() { 
     @Override 
     public Socket createSocket(String host, int port) 
         throws IOException, UnknownHostException { 

      InetAddress addr = InetAddress.getByName(host); 
      injectHostname(addr, host); 
      return delegate.createSocket(addr, port); 
     } 
     @Override 
     public Socket createSocket(InetAddress host, int port) 
         throws IOException { 

      return delegate.createSocket(host, port); 
     } 
     @Override 
     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) 
         throws IOException, UnknownHostException { 

      return delegate.createSocket(host, port, localHost, localPort); 
     } 
     @Override 
     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) 
         throws IOException { 

      return delegate.createSocket(address, port, localAddress, localPort); 
     } 
     private void injectHostname(InetAddress address, String host) { 
      try { 
       Field field = InetAddress.class.getDeclaredField("hostName"); 
       field.setAccessible(true); 
       field.set(address, host); 
      } catch (Exception ignored) { 
      } 
     } 
     @Override 
     public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { 

      injectHostname(s.getInetAddress(), host); 
      return delegate.createSocket(s, host, port, autoClose); 
     } 
     @Override 
     public String[] getDefaultCipherSuites() { 
      return delegate.getDefaultCipherSuites(); 
     } 
     @Override 
     public String[] getSupportedCipherSuites() { 
      return delegate.getSupportedCipherSuites(); 
     } 
    }; 
    socket = (SSLSocket)factory.createSocket("192.168.197.133", 9999); 
    socket.setSoTimeout(20000); 
    socket.setUseClientMode(true); 
    connected = true; 
    Log.i(getClass().toString(), "Connected."); 
    messages.getText().append("Connected."); 
} 

// Secure 
if (connected) { 
    Log.i(getClass().toString(), "Securing..."); 
    messages.getText().append("Securing..."); 
    SSLSession session = socket.getSession(); 
    boolean secured = session.isValid(); 
    if (secured) { 
     Log.i(getClass().toString(), "Secured."); 
     messages.getText().append("Secured."); 
    } 
} 
+1

He estado buscando por todas partes una forma de hacerlo (ni siquiera relacionada con Android, solo se necesita deshabilitar las búsquedas DNS inversas en un proyecto de Java). Tuve que modificarlo para el último JDK - pusieron el 'hostName' un nivel más profundo dentro de una clase de contenedor' InetAddressHolder' (también inaccesible, necesitaba más reflexión para atravesar eso). Aún así, gracias. –

+1

Gracias Yuyo. el problema me ha preocupado por mucho tiempo. – SalutonMondo

+0

He hecho mis solicitudes de back-end mucho más rápido en Android 6, parece que el error lamentablemente todavía existe en Android SDK. –

Cuestiones relacionadas