Android平台上经常有使用https的需求,对于https服务器使用的根证书是受信任的证书的话,实现https是非常简单的,直接用httpclient库就行了,与使用http几乎没有区别。但是在大多数情况下,服务器所使用的根证书是自签名的,或者签名机构不在设备的信任证书列表中,这样使用httpclient进行https连接就会失败。解决这个问题的办法有两种,一是在发起https连接之前将服务器证书加到httpclient的信任证书列表中,这个相对来说比较复杂一些,很容易出错;另一种办法是让httpclient信任所有的服务器证书,这种办法相对来说简单很多,但安全性则差一些,但在某些场合下有一定的应用场景。这里要举例说明的就是后一种方法:实例化HttpClinet对象时要进行一些处理主要是绑定https连接所使用的端口号,这里绑定了443和8443:
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("https",
- new EasySSLSocketFactory(), 443));
- schemeRegistry.register(new Scheme("https",
- new EasySSLSocketFactory(), 8443));
- ClientConnectionManager connManager = new ThreadSafeClientConnManager(params, schemeRegistry);
- HttpClient httpClient = new DefaultHttpClient(connManager, params);
上面的EasySSLSocketFactory类是我们自定义的,主要目的就是让httpclient接受所有的服务器证书,能够正常的进行https数据读取。相关代码如下:
- public class EasySSLSocketFactory implements SocketFactory,
- LayeredSocketFactory {
- private SSLContext sslcontext = null;
- private static SSLContext createEasySSLContext() throws IOException {
- try {
- SSLContext context = SSLContext.getInstance("TLS");
- context.init(null, new TrustManager[] { new EasyX509TrustManager(
- null) }, null);
- return context;
- } catch (Exception e) {
- throw new IOException(e.getMessage());
- }
- }
- private SSLContext getSSLContext() throws IOException {
- if (this.sslcontext == null) {
- this.sslcontext = createEasySSLContext();
- }
- return this.sslcontext;
- }
- public Socket connectSocket(Socket sock, String host, int port,
- InetAddress localAddress, int localPort, HttpParams params)
- throws IOException, UnknownHostException, ConnectTimeoutException {
- int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
- int soTimeout = HttpConnectionParams.getSoTimeout(params);
- InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
- SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());
- if ((localAddress != null) || (localPort > 0)) {
- // we need to bind explicitly
- if (localPort < 0) {
- localPort = 0; // indicates "any"
- }
- InetSocketAddress isa = new InetSocketAddress(localAddress,
- localPort);
- sslsock.bind(isa);
- }
- sslsock.connect(remoteAddress, connTimeout);
- sslsock.setSoTimeout(soTimeout);
- return sslsock;
- }
- public Socket createSocket() throws IOException {
- return getSSLContext().getSocketFactory().createSocket();
- }
- public boolean isSecure(Socket socket) throws IllegalArgumentException {
- return true;
- }
- public Socket createSocket(Socket socket, String host, int port,
- boolean autoClose) throws IOException, UnknownHostException {
- return getSSLContext().getSocketFactory().createSocket(socket, host,
- port, autoClose);
- }
- // -------------------------------------------------------------------
- // javadoc in org.apache.http.conn.scheme.SocketFactory says :
- // Both Object.equals() and Object.hashCode() must be overridden
- // for the correct operation of some connection managers
- // -------------------------------------------------------------------
- public boolean equals(Object obj) {
- return ((obj != null) && obj.getClass().equals(
- EasySSLSocketFactory.class));
- }
- public int hashCode() {
- return EasySSLSocketFactory.class.hashCode();
- }
- }
- public class EasyX509TrustManager implements X509TrustManager {
- private X509TrustManager standardTrustManager = null;
- public EasyX509TrustManager(KeyStore keystore)
- throws NoSuchAlgorithmException, KeyStoreException {
- super();
- TrustManagerFactory factory = TrustManagerFactory
- .getInstance(TrustManagerFactory.getDefaultAlgorithm());
- factory.init(keystore);
- TrustManager[] trustmanagers = factory.getTrustManagers();
- if (trustmanagers.length == 0) {
- throw new NoSuchAlgorithmException("no trust manager found");
- }
- this.standardTrustManager = (X509TrustManager) trustmanagers[0];
- }
- public void checkClientTrusted(X509Certificate[] certificates,
- String authType) throws CertificateException {
- standardTrustManager.checkClientTrusted(certificates, authType);
- }
- public void checkServerTrusted(X509Certificate[] certificates,
- String authType) throws CertificateException {
- if ((certificates != null) && (certificates.length == 1)) {
- certificates[0].checkValidity();
- } else {
- standardTrustManager.checkServerTrusted(certificates, authType);
- }
- }
- public X509Certificate[] getAcceptedIssuers() {
- return this.standardTrustManager.getAcceptedIssuers();
- }
- }
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("http", PlainSocketFactory
- .getSocketFactory(), 80));
- SSLSocketFactory sf = SSLSocketFactory.getSocketFactory();
- try {
- KeyStore trustStore = KeyStore.getInstance(KeyStore
- .getDefaultType());
- trustStore.load(null, null);
- sf = new SSLSocketFactoryEx(trustStore);
- sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
- // 允许所有主机的验证
- } catch (Exception e) {
- Log.e("erro", "SSLSocketFactory Error");
- }
- schemeRegistry.register(new Scheme("https", sf, 443));
- ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(
- httpParams, schemeRegistry);
- DefaultHttpClient httpClient = new DefaultHttpClient(cm, httpParams);
上面的 SSLSocketFactoryEx 类主要目的就是让httpclient接受所有的服务器证书,能够正常的进行https数据读取。相关代码如下:
- class SSLSocketFactoryEx extends SSLSocketFactory {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- public SSLSocketFactoryEx(KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException,
- KeyStoreException, UnrecoverableKeyException {
- super(truststore);
- TrustManager tm = new X509TrustManager() {
- @Override
- 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();
- }
- }
添加http头信息token认证
有时候服务器端需要传递token来验证请求来源是否是受信任的,以增强安全性:
httppost.addHeader("Authorization", "your token"); //token认证
重点是有些服务器要求将token转化成Base64编码。
于是 String token ="Basic " + Base64.encodeToString("your token".getBytes(), Base64.NO_WRAP);
注意,参数是 Base64.NO_WRAP,而不是Base64.DEFAULT 。而否则会返回 “400 Bad Request”,而得不到数据。