我正在尝试将我的Android(4.2)HTTP客户端连接到Jetty(9.1.3),使用TLS 1.2并交换可信证书 - 服务器在客户端受信任,客户端在服务器端受信任。我正在接收握手,客户端会读取数据,但最多需要6秒钟。有些类来自Apache HttpClient for Android 4.3.5(GA)。
读取密钥库(在扩展Application的Program类中):
private javax.net.ssl.SSLContext sslContext;
private org.apache.http.client.CredentialsProvider credentialsProvider;
public void createSSLContext(String pin) {
InputStream store = null;
try {
String password = getPassword();
store = getResources().openRawResource(R.raw.key_store);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(store, password.toCharArray());
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, password.toCharArray());
trustFactory.init(keyStore);
String cn = null;
List<String> aliases = Collections.list(keyStore.aliases());
for (String alias : aliases) {
if (!alias.equalsIgnoreCase("server")) {
cn = alias;
break;
}
}
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyFactory.getKeyManagers(), trustFactory.getTrustManagers(), null);
credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(String.format("%s:%s", cn, pin)));
} catch (Throwable t) {
Log.e(Program.TAG, t.getMessage(), t);
throw new RuntimeException(t.getMessage(), t);
} finally {
if (store != null) {
try {
store.close();
} catch (IOException e) {
Log.e(Program.TAG, e.getMessage(), e);
}
}
}
}
public SSLContext getSSLContext() {
return sslContext;
}
public HttpContext getHttpContext() {
HttpClientContext httpContext = HttpClientContext.create();
httpContext.setCredentialsProvider(credentialsProvider);
return httpContext;
}
客户类:
public final class Client {
private Context context;
public Client(Context context) {
this.context = context;
}
public void execute() {
Program program = (Program) context.getApplicationContext();
CloseableHttpClient client =
HttpClients.custom()
.setSSLSocketFactory(
new SSLConnectionSocketFactory(program.getSSLContext(), new AllowAllHostnameVerifier()))
.build();
HttpContext httpContext = program.getHttpContext();
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
HttpGet get = new HttpGet("https://10.1.5.195:8088/application/Application");
response = client.execute(get, httpContext);
entity = response.getEntity();
String r = EntityUtils.toString(entity);
System.out.println(r);
} catch (Throwable t) {
Log.e(Program.TAG, t.getMessage(), t);
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
Log.e(Program.TAG, e.getMessage(), e);
}
}
if (entity != null) {
try {
entity.consumeContent();
} catch (IOException e) {
Log.e(Program.TAG, e.getMessage(), e);
}
}
try {
client.close();
} catch (IOException e) {
Log.e(Program.TAG, e.getMessage(), e);
}
}
}
}
Android应用程序在移动设备上运行(Samsung Galaxy S3,Cyanogenmod 11)。移动设备和服务器都在同一个网络中(在Wi-Fi上工作)。
在AVD上进行测试时,一切正常。
我该怎么做才能让它更快?我试图设置http版本1.1参数,启用tcp没有延迟,但它没有改变任何东西。
在Java SE客户端中使用几乎相同的类时,接收数据大约需要400毫秒。
答案 0 :(得分:0)
如果其他人会遇到类似的问题,请阅读一些Jetty文档:
SslContextFactory.html#newSSLEngine
如果getNeedClientAuth()为true,则主机名将传递给newSSLEngine(String,int),这可能会导致反向DNS查找,这需要时间并且可能会挂起选择器(因为此方法通常由选择器调用)线程)。
因此解决方案是在服务器端SslContextFactory.html#setWantClientAuth而不是SslContextFactory.html#setNeedClientAuth上使用。