我正在尝试使用简单的HttpGet读取网页。在iOS上,我已经可以通过http实现,但在Android上无法通过https实现。
该url是一个内部网络IP和自定义端口,所以我可以像这样使用http读取:http://ipaddress:port/MyPage.html
HttpClient httpclient = new DefaultHttpClient(httpParameters);
HttpResponse response;
String responseString = null;
try {
// Try connection
HttpGet get = new HttpGet(params[0].path);
get.addHeader("Authorization",
"Basic "
+ Base64.encodeBytes(new String(params[0].username + ":" + params[0].password)
.getBytes()));
response = httpclient.execute(get);
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else {
// Closes the connection.
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
Log.e(TAG, "ClientProtocolException");
this.e = e;
} catch (IOException e) {
Log.e(TAG, "IOException");
this.e = e;
}
return responseString;
当我尝试使用https时,会出现No peer certificate
错误。因此,我已经尝试使用以下代码:HttpClient httpclient = new DefaultHttpClient(httpParameters);
private HttpClient createHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true);
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schReg.register(new Scheme("https", sf, 8080));
ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
return new DefaultHttpClient(conMgr, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
但是这给了我一个"连接被对等方关闭"的错误。
我做错了什么?我可以安全地忽略证书,因为它是一个带自签名证书的内部网络,但是我无法控制vert和我的应用程序用户可能有不同的证书,所以我真的需要自动接受或绕过它。
谢谢
编辑 ------------------------------
尝试下面My-Name-Is的答案后:
我创建了一个CustomX509TrustManager类,然后使用它创建一个自定义HttpClient,如下所示:
private HttpClient sslClient(HttpClient client) {
try {
CustomX509TrustManager tm = new CustomX509TrustManager();
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new MySSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = client.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 8080));
return new DefaultHttpClient(ccm, client.getParams());
} catch (Exception ex) {
return null;
}
}
最后,像这样使用HttpClient:
private class httpGETTask extends AsyncTask<GetParams, Void, String> {
private Exception e = null;
@Override
protected String doInBackground(GetParams... params) {
// Set connection parameters
HttpParams httpParameters = new BasicHttpParams();
int timeoutConnection = 15000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
int timeoutSocket = 15000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
Log.v(TAG, params[0].path);
HttpClient httpclient = new DefaultHttpClient(httpParameters);
httpclient = sslClient(httpclient);
HttpResponse response;
String responseString = null;
try {
// Try connection
HttpGet get = new HttpGet(params[0].path);
get.addHeader("Authorization",
"Basic "
+ Base64.encodeBytes(new String(params[0].username + ":" + params[0].password)
.getBytes()));
response = httpclient.execute(get);
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else {
// Closes the connection.
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
Log.e(TAG, "ClientProtocolException");
this.e = e;
} catch (IOException e) {
Log.e(TAG, "IOException");
this.e = e;
}
return responseString;
记录的路径格式为https://ipaddress:8080/Page.html
,但我遇到了一个连接被对等方关闭
的错误:
05-24 08:20:32.500:E/ConnectionHelper(1129):IOException 05-24 08:20:32.550:E/ConnectionHelper(1129):加载内容时出现异常 05-24 08:20:32.550:E/ConnectionHelper(1129): javax.net.ssl.SSLException:连接被对等方关闭 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:410) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.(OpenSSLSocketImpl.java:643) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:614) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.io.SocketInputBuffer.(SocketInputBuffer.java:70) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:172) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) 05-24 08:20:32.550:E/ConnectionHelper(1129): 在 com.d_apps.my_app.connection_helpers.ConnectionHelper$httpGETTask.doInBackground(ConnectionHelper.java:114)
cert.verify(ca.getPublicKey());
我添加了这段源代码,只是为了展示如何实现“checkServerTrusted”方法的一种可能性。对于大多数应用程序来说,没有必要进行此检查。 - My-Name-IsKeyStore
,但是你可以调整你的SSLSocketFactory
来支持这个功能。上面的解决方案使用了一个自定义的X509TrustManager
,它根据证书的公钥决定应用程序是否被允许连接到特定的服务器。控制这种行为的checkServerTrusted
的自定义实现已经被注释掉了,因为它可能对其他环境没有用处。 - My-Name-Is