Android httpClient 支持HTTPS的访问方式

jopen 10年前

项目中Android https请求地址遇到了这个异常(无终端认证):
 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

是SSL协议中没有终端认证。

没有遇到过的问题,于是无奈的去找度娘。。。。。。。

看了不少大神的博客后得到的解决方案如下:

    /**       * Post请求连接Https服务       * @param serverURL  请求地址       * @param jsonStr    请求报文       * @return       * @throws Exception       */      public static synchronized String doHttpsPost(String serverURL, String jsonStr)throws Exception {          // 参数          HttpParams httpParameters = new BasicHttpParams();          // 设置连接超时          HttpConnectionParams.setConnectionTimeout(httpParameters, 3000);          // 设置socket超时          HttpConnectionParams.setSoTimeout(httpParameters, 3000);          // 获取HttpClient对象 (认证)          HttpClient hc = initHttpClient(httpParameters);          HttpPost post = new HttpPost(serverURL);          // 发送数据类型          post.addHeader("Content-Type", "application/json;charset=utf-8");          // 接受数据类型          post.addHeader("Accept", "application/json");          // 请求报文          StringEntity entity = new StringEntity(jsonStr, "UTF-8");          post.setEntity(entity);          post.setParams(httpParameters);          HttpResponse response = null;          try {              response = hc.execute(post);          } catch (UnknownHostException e) {              throw new Exception("Unable to access " + e.getLocalizedMessage());          } catch (SocketException e) {              e.printStackTrace();          }          int sCode = response.getStatusLine().getStatusCode();          if (sCode == HttpStatus.SC_OK) {              return EntityUtils.toString(response.getEntity());          } else              throw new Exception("StatusCode is " + sCode);      }        private static HttpClient client = null;      /**       * 初始化HttpClient对象       * @param params       * @return       */      public static synchronized HttpClient initHttpClient(HttpParams params) {          if(client == null){              try {                  KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());                  trustStore.load(null, null);                                    SSLSocketFactory sf = new SSLSocketFactoryImp(trustStore);                  //允许所有主机的验证                  sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);                                    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);                  HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);                  // 设置http和https支持                  SchemeRegistry registry = new SchemeRegistry();                  registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));                  registry.register(new Scheme("https", sf, 443));                                    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);                                    return new DefaultHttpClient(ccm, params);              } catch (Exception e) {                  e.printStackTrace();                  return new DefaultHttpClient(params);              }          }          return client;      }       public static class SSLSocketFactoryImp extends SSLSocketFactory {          final SSLContext sslContext = SSLContext.getInstance("TLS");            public SSLSocketFactoryImp(KeyStore truststore)                  throws NoSuchAlgorithmException, KeyManagementException,                  KeyStoreException, UnrecoverableKeyException {              super(truststore);                TrustManager tm = new X509TrustManager() {                  public java.security.cert.X509Certificate[] getAcceptedIssuers() {                      return null;                  }                    @Override                  public void checkClientTrusted(                          java.security.cert.X509Certificate[] chain,                          String authType)                          throws java.security.cert.CertificateException {                  }                    @Override                  public void checkServerTrusted(                          java.security.cert.X509Certificate[] chain,                          String authType)                          throws java.security.cert.CertificateException {                  }              };              sslContext.init(null, new TrustManager[] { tm }, null);          }            @Override          public Socket createSocket(Socket socket, String host, int port,                  boolean autoClose) throws IOException, UnknownHostException {              return sslContext.getSocketFactory().createSocket(socket, host,                      port, autoClose);          }            @Override          public Socket createSocket() throws IOException {              return sslContext.getSocketFactory().createSocket();          }      }
run下,小手发抖的点到测试按钮,深吸口气,咦?没反应。。。马蛋的,工作线程忘记start(),唉,再次run下,终于的有点反应了,神奇的竟然没有报之前的  javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 的异常了。服务端的数据正常返回了。,狂喜中…</span>

分析问题:
HTTPS:超文本安全传输协议,和HTTP相比,多了一个SSL/TSL的认证过程,端口为443。

1.peer终端发送一个request,https服务端把支持的加密算法等以证书的形式返回一个身份信息(包含ca颁发机构和加密公钥等)。</p>

2.获取证书之后,验证证书合法性。

3.随机产生一个密钥,并以证书当中的公钥加密。

4.request https服务端,把用公钥加密过的密钥传送给https服务端。

5.https服务端用自己的密钥解密,获取随机值。

6.之后双方传送数据都用此密钥加密后通信。

HTTPS流程清楚后,问题也就明显了,验证证书时,无法验证

上面提供的解决方案就是添加默认信任全部证书。以此来通过接下来的通信。

但是,这样问题是解决了。但是觉得还是不带靠谱(信任全部证书有点危险)。继续噼噼啪啪的网上搜索一番。又找到了一种解决方案,其过程大致这样的:

1.浏览器访问https地址,保存提示的证书到本地,放到android项目中的assets目录。

2.导入证书,代码如下。

3.把证书添加为信任。

public static String requestHTTPSPage(Context context, String mUrl) {          InputStream ins = null;          String result = "";          try {              ins = context.getAssets().open("my.key"); // 下载的证书放到项目中的assets目录中              CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");              Certificate cer = cerFactory.generateCertificate(ins);              KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");              keyStore.load(null, null);              keyStore.setCertificateEntry("trust", cer);                SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore);              Scheme sch = new Scheme("https", socketFactory, 443);              HttpClient mHttpClient = new DefaultHttpClient();              mHttpClient.getConnectionManager().getSchemeRegistry().register(sch);                BufferedReader reader = null;              try {                  HttpGet request = new HttpGet();                  request.setURI(new URI(mUrl));                  HttpResponse response = mHttpClient.execute(request);                  if (response.getStatusLine().getStatusCode() != 200) {                      request.abort();                      return result;                  }                    reader = new BufferedReader(new InputStreamReader(response                          .getEntity().getContent()));                  StringBuffer buffer = new StringBuffer();                  String line = null;                  while ((line = reader.readLine()) != null) {                      buffer.append(line);                  }                  result = buffer.toString();              } catch (Exception e) {                  e.printStackTrace();              } finally {                  if (reader != null) {                      reader.close();                  }              }          } catch (Exception e) {              e.printStackTrace();          } finally {              try {                  if (ins != null)                      ins.close();              } catch (IOException e) {                  e.printStackTrace();              }          }          return result;      }
</span>来自:http://my.oschina.net/u/1251149/blog/299010