我在 Android 设备(使用 Android 6.0 的三星 Galaxy)和使用 com.sun.net.httpserver.HttpsServer 的简单 Java 应用程序之间通过 SSL (TSL) 进行通信时遇到问题。我不得不说,在其他装有 Android 4.4 的设备上一切正常。
当 Android 6.0 尝试进行握手时,服务器端完成通信,在 android 上看到下面的堆栈跟踪
W/System.err: javax.net.ssl.SSLHandshakeException: Connection closed by peer
W/System.err: at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:353)
W/System.err: at com.android.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:212)
W/System.err: at com.android.okhttp.Connection.connect(Connection.java:1322)
W/System.err: at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:1410)
W/System.err: at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:466)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:447)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:353)
W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:468) 08-28 06:47:21.322 3676-5603/pl.com.szb.gateopener W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:118) 08-28 06:47:21.322 3676-5603/pl.com.szb.gateopener W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:249)
W/System.err: at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
W/System.err: at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:25)
W/System.err: at org.ksoap2.transport.HttpsServiceConnectionSE.openOutputStream(HttpsServiceConnectionSE.java:127)
W/System.err: at org.ksoap2.transport.HttpTransportSE.sendData(HttpTransportSE.java:292)
W/System.err: at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:184)
W/System.err: at org.ksoap2.transport.HttpTransportSE.call(HttpTransportSE.java:118)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding.sendRequest(PSBGOImplPortBinding.java:99)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding.execute(PSBGOImplPortBinding.java:284)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding.getGates(PSBGOImplPortBinding.java:142)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding$2.Func(PSBGOImplPortBinding.java:165)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding$2.Func(PSBGOImplPortBinding.java:163)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding$7.doInBackground(PSBGOImplPortBinding.java:318)
W/System.err: at pl.com.szb.gateopener.client.PSBGOImplPortBinding$7.doInBackground(PSBGOImplPortBinding.java:308)
W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:295)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
W/System.err: at java.lang.Thread.run(Thread.java:818)
用于 SSL 启动的 Android 代码
`private void initSSL() throws Exception {
Log.d(TAG,"initSSL");
TrustManager tm = new CustomX509TrustManager();
SSLContext customSSLContext = SSLContext.getInstance("TLSv1.1");
customSSLContext.init(null , new TrustManager[] { tm } , null);
sslSocketFactory = customSSLContext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
Log.d(TAG, String.format("Verifying %s , sessionId: %s", hostname, Arrays.toString(session.getId())));
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
public class CustomX509TrustManager implements X509TrustManager {
private String TAG = "CustomX509TrustManager";
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
Log.d(TAG, "checkClientTrusted");
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws CertificateException
{
Log.d(TAG, "checkServerTrusted");
// Here you can verify the servers certificate. (e.g. against one which is stored on mobile device)
InputStream inStream = null;
try {
inStream = getResources().openRawResource(R.raw.goserver_10y);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
for (X509Certificate cert : certs) {
// Verifing by public key
cert.verify(ca.getPublicKey());
}
} catch (Exception e) {
Log.w(TAG, e.getMessage());
throw new CertificateException(e);
} finally {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public X509Certificate[] getAcceptedIssuers() {
Log.d(TAG, "getAcceptedIssuers");
return null;
}
}`
和一个服务器代码:
`private void initServer() throws Exception{
Config config = getContext().system().settings().config();
host = getConfigString("pl.com.szb.GOImpl.host", config, "0.0.0.0");
port = getConfigInt("port", config, 10000);
useSSL = getConfigBoolean("use_ssl", config, true);
endpointLocation = getConfigString("endpointLocation", config, "/go");
String fullURL = String.format("%s://%s:%d%s",(useSSL ? "https" : "http"), host, port, endpointLocation);
log.info(String.format("WSDL : %s?wsdl", fullURL));
if (useSSL) {
keystorePass = getConfigString("keystorePass", config, "secret");
keystoreName = getConfigString("keystoreName", config, "second/server.bks");
char[] pass = keystorePass.toCharArray();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream is = classLoader.getResourceAsStream(keystoreName);
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(is, pass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, pass);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), null, null);
server = HttpsServer.create(new InetSocketAddress(host, port), 0);
((HttpsServer)server).setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
log.debug(String.format("Got client: %s, wantAuth: %b, needAuth: %b",
params.getClientAddress().toString(),
params.getWantClientAuth(),
params.getNeedClientAuth()
));
params.setWantClientAuth(false);
params.setNeedClientAuth(false);
//what to do more??
} catch (Exception ex) {
log.warn(String.format("Failed to create HTTPS port, cause " + ex.getMessage()));
}
}
});
} else {
server = HttpServer.create(new InetSocketAddress(host, port), 0);
}
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
server.start();
HttpContext httpContext = server.createContext(endpointLocation);
endpoint = Endpoint.create(SOAPBinding.SOAP11HTTP_BINDING,this);
endpoint.publish(httpContext);
}
`
我检查过的内容:
- 输入标题后 google 和 stackoverflow 建议的每个链接(特别是链接Https 连接在 android 5.0 lollipop 中被对等方关闭- 但没有运气)
- 两个设备上的 Cipher siute 理论上都应该能够完成握手。有相应的算法。
- 我检查了与wireshark的通信。我发现即使我 delcare(在 Android 中)TLSv1.2 版本 TLS 1.0 在 SSL 标头中发送(请参阅 附加的 wireshark 转储- 使用“解码为”-> SSL)在第 140 行我们看到来自 Android 6.0 的通信,Bellow(在第 145 行,我们看到来自 Android 4.4 的工作通信)10.0.0.203 是 Android 6.0,10.0.0.99 是服务器,10.0.0.200 是 Android 4.4
- 我读到“连接被对等方关闭”并且在许多主题中人们都在谈论错误的证书,但就我而言,我认为 SSL 在协议协商期间崩溃,而不是在证书交换期间(稍后出现)。
服务器站点有点旧(它是一个 SOAP 服务),但我认为服务器提供什么并不重要。
请帮忙,这是我的第一篇文章。