我正在开发一款使用SSLSocket连接服务器的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.");
}
问题是在下面一行中进行TLS握手需要大约5秒或更多事件:
SSLSession session = socket.getSession();
我已经制作了类似的iPhone应用程序,握手只需1秒钟,所以我认为问题不在我正在连接的服务器中,它可能在上面的代码中。连接本身足够快,只是TLS握手很慢。
有人知道Android中是否正常,或者不是,如何让它更快?
谢谢。
于21.01.11编辑:
我发现,当我连接到另一台服务器时握手很快,例如 paypal.com:443 。
但我以前连接过另一台服务器 - 我写的一个.NET服务。正如我之前所说,我不认为问题出现在那台服务器上,因为如果我用我的iPhone App连接它,握手很快。现在我不知道为什么它在iPhone上速度快,在Android上速度慢。建立连接后,我在.NET服务器中唯一做的就是:
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.");
答案 0 :(得分:6)
早期版本的Android SDK存在错误。显然,它正在进行不必要的DNS反向查找。你需要防止这种情况发生。这是一个对我有用的解决方法。过去需要15秒,现在需要0-1秒。希望它有所帮助。
以下是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 :(得分:2)
您每个连接使用新的SecureRandom
,而不是使用单个静态预初始化SecureRandom
。每当你创建一个新的SecureRandom()时,你需要收集熵用于播种(一个缓慢的过程)。
SecureRandom在首次使用之前不会自行播种,这就是为什么在调用getSession()
之前不会发生延迟
答案 2 :(得分:0)
我做了类似的事情,它比不安全的连接慢。虽然我的情况是https vs http,但SSL / TLS因素会增加交易的速度。
我有两个相同的应用程序,使用相同的协议与同一服务器通信,一个在Android中,一个在iPhone中,都使用https。当我在http中测试它们时,我会看到或多或少相同的响应时间,在https iOS中我的情况稍微快一些,但不是非常糟糕。
答案 3 :(得分:-1)
问题很可能在于设备验证服务器证书的方式。验证可能涉及联系第三方以获取CRL和OCSP响应。如果发生这种情况,需要时间。 iPhone可能只是不这样做(至少在默认情况下)这是一个安全漏洞BTW。