2011-01-19 32 views
7

Tôi đang phát triển một ứng dụng Android sử dụng SSLSocket để kết nối với máy chủ. Đây là mã Tôi đang sử dụng:Kết nối TLS sử dụng SSLSocket chậm trong Hệ điều hành Android

// 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."); 
} 

Vấn đề là phải mất khoảng 5 giây hoặc sự kiện hơn để làm những cái bắt tay TLS trong dòng dưới đây:

SSLSession session = socket.getSession(); 

Tôi đã thực hiện một tương tự Ứng dụng iPhone, cái bắt tay chỉ mất 1 giây ở đó, vì vậy tôi nghĩ vấn đề không nằm trong máy chủ mà tôi đang kết nối đến, nó có thể nằm trong đoạn mã trên. Kết nối chính nó là đủ nhanh, chỉ cần bắt tay TLS là chậm.

Có ai biết nếu nó bình thường trong Android, hoặc nếu không, làm thế nào để làm cho nó nhanh hơn?

Cảm ơn bạn.

edited on 21.01.11:

Tôi đã phát hiện ra, rằng cái bắt tay là nhanh khi kết nối với máy chủ khác, ví dụ paypal.com:443.

Nhưng trước đó tôi đã kết nối với một máy chủ khác - dịch vụ .NET do tôi viết. Như tôi đã nói trước đây, tôi đã không nghĩ rằng vấn đề là trong máy chủ đó bởi vì nếu tôi kết nối với nó với iPhone App của tôi bắt tay là nhanh. Bây giờ tôi không biết tại sao nó nhanh trên iPhone và chạy chậm trên Android. Sau khi kết nối được thiết lập, điều duy nhất tôi làm trong máy chủ .NET là:

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."); 

Trả lời

0

Tôi đã làm điều gì đó tương tự như vậy và chậm hơn kết nối không an toàn. Cấp trường hợp của tôi là https so với http và có một chút khác biệt với yếu tố SSL/TLS sẽ làm chậm thêm sự thỏa thuận.

Tôi có hai ứng dụng giống hệt nhau mà giao tiếp với cùng một giao thức cho cùng một máy chủ, một trong Android và một trong iPhone, cả hai đều sử dụng https. Khi tôi thử nghiệm cả hai trong http tôi sẽ thấy nhiều hơn hoặc ít hơn thời gian phản ứng tương tự, trong https iOS là hơi nhanh hơn trong trường hợp của tôi, nhưng không khủng khiếp.

+0

Có Tôi biết rằng TLS thường chậm hơn kết nối thuần túy nhưng đủ nhanh. Việc bắt tay TLS tuy nhiên rất chậm, phải mất 10 giây, không phải 5 như tôi đã nói trước đây. – Arthur

2

Bạn đang sử dụng SecureRandom kết nối mới cho mỗi kết nối, thay vì sử dụng một đơn lẻ được khởi tạo trước SecureRandom. Mỗi khi bạn tạo một SecureRandom mới(), bạn cần thu thập entropy để tạo hạt giống (một tiến trình chậm).

SecureRandom không tự hạt giống cho đến khi nó được sử dụng đầu tiên, đó là lý do sự chậm trễ không xảy ra cho đến khi cuộc gọi đến getSession()

+0

Cảm ơn bạn đã trả lời Jumbogram, tôi đã thử những gì bạn nói, bây giờ tôi chỉ sử dụng một thể hiện của SecureRandom được khởi tạo trong hàm dựng. Nhưng dù tôi có kết nối và ngắt kết nối thường xuyên như thế nào đi chăng nữa, nó luôn mất khoảng 10 giây để thực thi socket.getSession(); – Arthur

+0

Nếu đó là tôi, tôi sẽ cố gắng để có được một gói chụp và nhìn vào nơi chậm lại là. Là nó 1 giây giữa mỗi gói? Có một sự chậm trễ lâu dài giữa gói (n) và (n + 1), nhưng mọi thứ khác nhanh chóng? Những thứ như thế này. – Jumbogram

+0

Ý tưởng hay, tôi sẽ thử nó. Tôi đã phát hiện ra, rằng cái bắt tay nhanh khi tôi kết nối với một máy chủ khác, ví dụ paypal.com:443. Tôi đã chỉnh sửa bài viết của tôi bây giờ, tôi nghĩ rằng tôi phải thay đổi một cái gì đó trong máy chủ NET. Nhưng tôi không biết những gì, có thể vấn đề là trong giấy chứng nhận của máy chủ, không có ý tưởng .. – Arthur

-1

Vấn đề là có khả năng nhất trong cách thiết bị xác nhận chứng chỉ máy chủ. Việc xác thực có thể liên quan đến việc liên hệ với bên thứ ba về các phản hồi CRL và OCSP. Nếu điều này xảy ra, phải mất thời gian. iPhone có lẽ chỉ không làm điều này (ít nhất là theo mặc định) mà là một lỗ hổng bảo mật BTW.

6

Đã xảy ra lỗi trên các phiên bản trước của SDK Android. Rõ ràng, nó đang thực hiện tra cứu ngược DNS không cần thiết. Bạn cần phải ngăn chặn điều này xảy ra. Đây là một cách giải quyết có hiệu quả đối với tôi. Nó được sử dụng để mất 15 giây, bây giờ phải mất 0-1 giây. Hy vọng nó giúp.

Đây là liên kết tới 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

Tôi đã tìm kiếm ở khắp mọi nơi cho một cách để làm điều này (thậm chí không liên quan đến android, chỉ cần thiết để vô hiệu hóa tra cứu DNS ngược trong một dự án java). Tôi đã phải sửa đổi nó cho JDK mới nhất - họ đặt cấp độ 'hostName' sâu hơn bên trong một lớp trình bao bọc' InetAddressHolder' (cũng không thể tiếp cận được, cần phản ánh nhiều hơn để duyệt qua nó). Tuy nhiên - cảm ơn bạn. –

+1

Cảm ơn bạn Yuyo. vấn đề đã gây rắc rối cho tôi trong một thời gian dài. – SalutonMondo

+0

Thực hiện yêu cầu phụ trợ của tôi nhanh hơn nhiều trong Android 6, có vẻ như lỗi không may vẫn tồn tại trong Android SDK. –

Các vấn đề liên quan