我在使用OpenSSL
对象连接到简单的HttpsUrlConnection
服务器时遇到了麻烦(我已经通过StackOverflow和一堆在线教程进行了梳理,并且几乎按照示例进行了操作对于我来说,当我使用本地信任库时,我仍然无法弄清楚为什么我的东西会被打破。)
我目前有一个Android活动试图连接到一个简单的OpenSSL server
(我可以使用OpenSSL客户端连接到我的服务器),一旦HttpsUrlConnection.connect()
被调用,我收到一个“{{1也许我正在设置我的样本服务器错误?
注意事项:
javax.net.ssl.SSLException: Connection closed by peer" error during the SSL handshake.
。 我已经尝试过的事情:
Android 4.1 API 16
127.0.0.1 and 10.0.2.2
SecureRandom() with the SSLContext.init()
'URL u = new URL("https", "10.0.2.2", 443, "/");'
代替“X509”
TrustManagerFactory.getDefaultAlgorithms()
而不是“由同行关闭的连接”提前感谢您抽出时间审核我的问题!
使用命令启动简单服务器:
"Unexpected response code error 503"
使用命令测试客户端连接:
$ sudo openssl s_server -accept 443 -cert server-cert.pem -key server-key.pem -pass file:passphrase.txt -state -www -verify 0
Android活动代码(编辑后删除完整的运行代码以简化 - 如果需要更多代码,请告诉我) - 错误输出低于代码。
$ openssl s_client -connect 127.0.0.1:443
正确连接到https://www.google.com时的输出:
try {
TrustManagerFactory tmf;
// local trust store
tmf = TrustManagerFactory.getInstance("X509");
tmf.init(loadLocalKeyStore(getApplicationContext()));
// default trust store - works for https://www.google.com
// tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// tmf.init((KeyStore) null);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
URL u = new URL("https://10.0.2.2");
HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setHostnameVerifier(hostnameVerifier);
urlConnection.connect();
System.out.println("Response Code: " + urlConnection.getResponseCode());
System.out.println("Response Code: " + urlConnection.getCipherSuite());
}
...
private KeyStore loadLocalKeyStore(Context context) {
InputStream in = context.getResources().openRawResource(R.raw.newserverkeystore);
KeyStore trusted = null;
try {
trusted = KeyStore.getInstance("BKS");
trusted.load(in, "thisisasecret".toCharArray());
} finally {
in.close();
}
return trusted;
}
尝试使用自签名证书连接到我的服务器时的输出:
09-09 21:58:09.947: I/System.out(669): Response Code: 200
09-09 21:58:09.947: I/System.out(669): Response Code: TLS_ECDHE_RSA_WITH_RC4_128_SHA
再次感谢!!
答案 0 :(得分:6)
我解决了我的问题 - 我需要使用10.0.2.2作为通用名称(CN)的证书,因此它与10.0.2.2的Android localhost ip地址匹配,而不是'localhost'或'127.0.0.1'。
编辑:您可以创建一个证书,其中localhost为CN,'127.0.0.1'和'10 .0.2.2'为主题备用名称(SAN)。
创建10.0.2.2 cert和私钥pem文件后,我能够使用以下命令运行我的服务器:
openssl s_server -accept 8888 -cert 10.0.2.2-cert.pem -key 10.0.2.2-key.pem -state -www
如果要强制客户端提供证书(虽然不会检查),请将标志-Verify 1
添加到上面的命令中。
要在命令行测试服务器,您可以使用以下内容(注意openssl能够通过127.0.0.1连接):
openssl s_client -connect 127.0.0.1:8888
如果服务器需要,请添加客户端证书,添加标记-cert client-cert.pem -key client-key.pem
在我的Android客户端中,我使用以下代码进行连接(删除了错误检查):
// use local trust store (CA)
TrustManagerFactory tmf;
KeyStore trustedStore = null;
InputStream in = context.getResources().openRawResource(R.raw.mycatruststore); // BKS in res/raw
trustedStore = KeyStore.getInstance("BKS");
trustedStore.load(in, "insertBksPasswordHere".toCharArray());
tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustedStore);
// load client certificate
KeyStore clientKeyStore = loadClientKeyStore(getApplicationContext());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(clientKeyStore, "insertPasswordHere".toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
// provide client cert - if server requires client cert this will pass
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
// connect to url
URL u = new URL("https://10.0.2.2:8888/");
HttpsURLConnection urlConnection = (HttpsURLConnection) u.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setHostnameVerifier(hostnameVerifier);
urlConnection.connect();
System.out.println("Response Code: " + urlConnection.getResponseCode());
您应该获得200的响应代码,并且可以从那里剖析响应。
以下是加载客户端凭据的代码,与加载服务器密钥库但使用不同的资源文件名和密码相同:
private KeyStore loadClientKeyStore(Context context) {
InputStream in = context.getResources().openRawResource(R.yourKeyStoreFile);
KeyStore trusted = null;
trusted = KeyStore.getInstance("BKS");
trusted.load(in, "yourClientPassword".toCharArray());
in.close();
return trusted;
}
答案 1 :(得分:2)
我浪费了6到7个小时修复了这个问题,最后它与
一起工作了public void URLConnection(String webUrl) throws IOException, NoSuchAlgorithmException, KeyManagementException {
//TLSSocketFactory objTlsSocketFactory = new TLSSocketFactory();
URL url = new URL(webUrl);
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setRequestMethod("GET");
//urlConnection.setSSLSocketFactory(objTlsSocketFactory);
int responseCode = urlConnection.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
//print result
System.out.println(response.toString());
}
它有效!!!!!!