Android Https请求详解

转至:http://itindex.net/detail/51666-android-https-demo


   Android Https详细请求全方案实现,包括HttpUrlConnection及HttpClient方式实现指定证书及信任所有的实现,不多说了,以下代码都经过详细测试,可以直接使用。

    

package com.example.httpstest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

/**
 * HTTPS测试 测试地址:https://certs.cac.washington.edu/CAtest/
 * 测试证书:https://www.washington.edu/itconnect/security/ca/load-der.crt
 * 
 * 
 * @author guojing09
 * 
 */
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					initSSLWithHttpClinet();
				} catch (Exception e) {
					Log.e("HTTPS TEST", e.getMessage());
				}
			}
		}).start();
	}

	/**
	 * HttpUrlConnection 方式,支持指定load-der.crt证书验证,此种方式Android官方建议
	 * 
	 * @throws CertificateException
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public void initSSL() throws CertificateException, IOException, KeyStoreException,
			NoSuchAlgorithmException, KeyManagementException {
		CertificateFactory cf = CertificateFactory.getInstance("X.509");
		InputStream in = getAssets().open("load-der.crt");
		Certificate ca = cf.generateCertificate(in);

		KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
		keystore.load(null, null);
		keystore.setCertificateEntry("ca", ca);

		String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
		tmf.init(keystore);

		// Create an SSLContext that uses our TrustManager
		SSLContext context = SSLContext.getInstance("TLS");
		context.init(null, tmf.getTrustManagers(), null);
		URL url = new URL("https://certs.cac.washington.edu/CAtest/");
		HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
		urlConnection.setSSLSocketFactory(context.getSocketFactory());
		InputStream input = urlConnection.getInputStream();

		BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
		StringBuffer result = new StringBuffer();
		String line = "";
		while ((line = reader.readLine()) != null) {
			result.append(line);
		}
		Log.e("TTTT", result.toString());
	}

	/**
	 * HttpUrlConnection支持所有Https免验证,不建议使用
	 * 
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 * @throws IOException
	 */
	public void initSSLALL() throws KeyManagementException, NoSuchAlgorithmException, IOException {
		URL url = new URL("https://certs.cac.washington.edu/CAtest/");
		SSLContext context = SSLContext.getInstance("TLS");
		context.init(null, new TrustManager[] { new TrustAllManager() }, null);
		HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
		HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

			@Override
			public boolean verify(String arg0, SSLSession arg1) {
				return true;
			}
		});
		HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
		connection.setDoInput(true);
		connection.setDoOutput(false);
		connection.setRequestMethod("GET");
		connection.connect();
		InputStream in = connection.getInputStream();
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		String line = "";
		StringBuffer result = new StringBuffer();
		while ((line = reader.readLine()) != null) {
			result.append(line);
		}
		Log.e("TTTT", result.toString());
	}
	
	/**
	 * HttpClient方式实现,支持所有Https免验证方式链接
	 * 
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public void initSSLAllWithHttpClient() throws ClientProtocolException, IOException {
		int timeOut = 30 * 1000;
		HttpParams param = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(param, timeOut);
		HttpConnectionParams.setSoTimeout(param, timeOut);
		HttpConnectionParams.setTcpNoDelay(param, true);

		SchemeRegistry registry = new SchemeRegistry();
		registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		registry.register(new Scheme("https", TrustAllSSLSocketFactory.getDefault(), 443));
		ClientConnectionManager manager = new ThreadSafeClientConnManager(param, registry);
		DefaultHttpClient client = new DefaultHttpClient(manager, param);

		HttpGet request = new HttpGet("https://certs.cac.washington.edu/CAtest/");
		// HttpGet request = new HttpGet("https://www.alipay.com/");
		HttpResponse response = client.execute(request);
		HttpEntity entity = response.getEntity();
		BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()));
		StringBuilder result = new StringBuilder();
		String line = "";
		while ((line = reader.readLine()) != null) {
			result.append(line);
		}
		Log.e("HTTPS TEST", result.toString());
	}

	/**
	 * HttpClient方式实现,支持验证指定证书
	 * 
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public void initSSLCertainWithHttpClient() throws ClientProtocolException, IOException {
		int timeOut = 30 * 1000;
		HttpParams param = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(param, timeOut);
		HttpConnectionParams.setSoTimeout(param, timeOut);
		HttpConnectionParams.setTcpNoDelay(param, true);

		SchemeRegistry registry = new SchemeRegistry();
		registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		registry.register(new Scheme("https", TrustCertainHostNameFactory.getDefault(this), 443));
		ClientConnectionManager manager = new ThreadSafeClientConnManager(param, registry);
		DefaultHttpClient client = new DefaultHttpClient(manager, param);

		// HttpGet request = new
		// HttpGet("https://certs.cac.washington.edu/CAtest/");
		HttpGet request = new HttpGet("https://www.alipay.com/");
		HttpResponse response = client.execute(request);
		HttpEntity entity = response.getEntity();
		BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent()));
		StringBuilder result = new StringBuilder();
		String line = "";
		while ((line = reader.readLine()) != null) {
			result.append(line);
		}
		Log.e("HTTPS TEST", result.toString());
	}

	public class TrustAllManager implements X509TrustManager {

		@Override
		public void checkClientTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub

		}

		@Override
		public void checkServerTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub

		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			// TODO Auto-generated method stub
			return null;
		}
	}

}

 

package com.example.httpstest;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;

import android.os.Build;

public class TrustAllSSLSocketFactory extends SSLSocketFactory {
	private javax.net.ssl.SSLSocketFactory factory;
	private static TrustAllSSLSocketFactory instance;

	private TrustAllSSLSocketFactory() throws KeyManagementException, UnrecoverableKeyException,
			NoSuchAlgorithmException, KeyStoreException {
		super(null);

		SSLContext context = SSLContext.getInstance("TLS");
		context.init(null, new TrustManager[] { new TrustAllManager() }, null);
		factory = context.getSocketFactory();
		setHostnameVerifier(new X509HostnameVerifier() {

			@Override
			public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
				// TODO Auto-generated method stub

			}

			@Override
			public void verify(String host, X509Certificate cert) throws SSLException {
				// TODO Auto-generated method stub

			}

			@Override
			public void verify(String host, SSLSocket ssl) throws IOException {
				// TODO Auto-generated method stub

			}

			@Override
			public boolean verify(String host, SSLSession session) {
				// TODO Auto-generated method stub
				return true;
			}
		});
	}

	public static SocketFactory getDefault() {
		if (instance == null) {
			try {
				instance = new TrustAllSSLSocketFactory();
			} catch (KeyManagementException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (UnrecoverableKeyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (KeyStoreException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return instance;
	}

	@Override
	public Socket createSocket() throws IOException {
		return factory.createSocket();
	}

	@Override
	public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
			throws IOException, UnknownHostException {
		if (Build.VERSION.SDK_INT < 11) { // 3.0
			injectHostname(socket, host);
		}

		return factory.createSocket(socket, host, port, autoClose);
	}

	private void injectHostname(Socket socket, String host) {
		try {
			Field field = InetAddress.class.getDeclaredField("hostName");
			field.setAccessible(true);
			field.set(socket.getInetAddress(), host);
		} catch (Exception ignored) {
		}
	}

	public class TrustAllManager implements X509TrustManager {

		@Override
		public void checkClientTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub

		}

		@Override
		public void checkServerTrusted(X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub

		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			// TODO Auto-generated method stub
			return null;
		}
	}

}

 

package com.example.httpstest;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;

import org.apache.http.conn.ssl.SSLSocketFactory;

import android.content.Context;

public class TrustCertainHostNameFactory extends SSLSocketFactory {

	private static TrustCertainHostNameFactory mInstance;

	public TrustCertainHostNameFactory(KeyStore truststore) throws NoSuchAlgorithmException,
			KeyManagementException, KeyStoreException, UnrecoverableKeyException {
		super(truststore);
	}

	public static TrustCertainHostNameFactory getDefault(Context context) {
		KeyStore keystore = null;
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			InputStream in;
			in = context.getAssets().open("load-der.crt");
			Certificate ca = cf.generateCertificate(in);

			keystore = KeyStore.getInstance(KeyStore.getDefaultType());
			keystore.load(null, null);
			keystore.setCertificateEntry("ca", ca);

			if (null == mInstance) {
				mInstance = new TrustCertainHostNameFactory(keystore);
			}
		} catch (Exception e) {

		}
		return mInstance;
	}

	@Override
	public Socket createSocket() throws IOException {
		return super.createSocket();
	}

	@Override
	public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
			throws IOException, UnknownHostException {
		return super.createSocket(socket, host, port, autoClose);
	}

}


https相关知识补充:

转至:http://edison0663.iteye.com/blog/996526


为了便于更好的认识和理解 SSL 协议,这里着重介绍 SSL 协议的握手协议。SSL 协议既用到了公钥加密技术又用到了对称加密技术,对称加密技术虽然比公钥加密技术的速度快,可是公钥加密技术提供了更好的身份认证技术。SSL 的握手协议非常有效的让客户和服务器之间完成相互之间的身份认证,其主要过程如下:
  ① 客户端的浏览器向服务器传送客户端 SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。
  ② 服务器向客户端传送 SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。
  ③ 客户利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的 CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第四步。
  ④ 用户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤②中的服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。
  ⑤ 如果服务器要求客户的身份认证(在握手过程中为可选),用户可以建立一个随机数然后对其进行数据签名,将这个含有签名的随机数和客户自己的证书以及加密过的“预主密码”一起传给服务器。
  ⑥ 如果服务器要求客户的身份认证,服务器必须检验客户证书和签名随机数的合法性,具体的合法性验证过程包括:客户的证书使用日期是否有效,为客户提供证书的 CA 是否可靠,发行 CA 的公钥能否正确解开客户证书的发行 CA 的数字签名,检查客户的证书是否在证书废止列表(CRL)中。检验如果没有通过,通讯立刻中断;如果验证通过,服务器将用自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主通讯密码(客户端也将通过同样的方法产生相同的主通讯密码)。
  ⑦ 服务器和客户端用相同的主密码即“通话密码”,一个对称密钥用于 SSL 协议的安全数据通讯的加解密通讯。同时在 SSL 通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。
  ⑧ 客户端向服务器端发出信息,指明后面的数据通讯将使用的步骤⑦中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。
  ⑨ 服务器向客户端发出信息,指明后面的数据通讯将使用的步骤⑦中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。
  ⑩ SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。


  双向认证 SSL 协议的具体过程
  ① 浏览器发送一个连接请求给安全服务器。
  ② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器。
  ③ 客户浏览器检查服务器送过来的证书是否是由自己信赖的 CA 中心所签发的。如果是,就继续执行协议;如果不是,客户浏览器就给客户一个警告消息:警告客户这个证书不是可以信赖的,询问客户是否需要继续。
  ④ 接着客户浏览器比较证书里的消息,例如域名和公钥,与服务器刚刚发送的相关消息是否一致,如果是一致的,客户浏览器认可这个服务器的合法身份。
  ⑤ 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。
  ⑥ 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。
  ⑦ 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。
  ⑧ 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。
  ⑨ 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。
  ⑩ 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的。
  上面所述的是双向认证 SSL 协议的具体通讯过程,这种情况要求服务器和用户双方都有证书。单向认证 SSL 协议不需要客户拥有 CA 证书,具体的过程相对于上面的步骤,只需将服务器端验证客户证书的过程去掉,以及在协商对称密码方案,对称通话密钥时,服务器发送给客户的是没有加过密的(这并不影响 SSL 过程的安全性)密码方案。 这样,双方具体的通讯内容,就是加过密的数据,如果有第三方攻击,获得的只是加密的数据,第三方要获得有用的信息,就需要对加密的数据进行解密,这时候的安全就依赖于密码方案的安全。而幸运的是,目前所用的密码方案,只要通讯密钥长度足够的长,就足够的安全。这也是我们强调要求使用 128 位加密通讯的原因。

 

     一般web应用都是采用单向认证的,原因很简单,用户数目广泛,且无需做在通讯层做用户身份验证,一般都在应用逻辑层来保证用户的合法登入。

     但如果是企业应用对接,情况就不一样,可能会要求对client(相对而言)做身份验证。这时需要做双向认证。

 

 

     用java简单试了一下如何搭建一个https站点, tomcat 6 https 单向认证,网上google一下,例子很多,这里就不贴出来了,然后浏览器测试。

    

     继续浏览此网站,可以正式访问。 服务器搭建,应该是没问题了。 但是这个警告是怎么回事? 还是应该再了解一下的。

     这个证书是自己做的,用jdk的工具keytool 很容易可以搞出一个(不懂请google), 浏览器很明显不信任这个服务器发过来的证书(公钥)。 这里还需要了解一下,浏览器是如何信任一个证书的。

     1 浏览器 https: 访问一个网站

     2 网站服务器会发给浏览器 一个证书

     3 浏览器验证证书有效性:

        3.1 证书与访问域名是否匹配,证书是否还在已经有效期内。

        3.2 证书的路径。

              如图:

             
             
              证书是否由 浏览器(客户端) 所信任的机构认证。 如果在,则通过,否则给用户自己选择。

              自己折腾的证书没给第三方认证,肯定弹warn, 于是乎出现了第一个图。

             
             

    有了以上的基础知识,网上再找一下,自己写一个简单的java程序,要实现,https顺利访问(无warn)

    easy, 把这个证书设置为自己信任的就ok.

    通过jdk的keytool 产生对应证书,并导入到truststore,生成一个文件(该文件存储了信任的证书)。在java程序中指定truststore对应的文件。

 

 

Java代码   收藏代码
  1. public static void main(String[] args) throws Exception {  
  2.         System.setProperty("javax.net.ssl.trustStore""C:/Java/Tomcat/conf/client.truststore" );  
  3.         new testhttpspostdata().testIt();  
  4.     }  
  5.   
  6.     private void testIt() {  
  7.         String postdata="data=testpostdata";  
  8.         String https_url = "https://localhost:8443/examples/jsp/getpost.jsp";  
  9.         URL url;  
  10.         try {  
  11.   
  12.             url = new URL(https_url);  
  13.             HttpsURLConnection con = (HttpsURLConnection) url.openConnection();  
  14.             System.out.println("ready post data");  
  15.             con.setDoOutput(true);  
  16.             con.setRequestMethod("POST");  
  17.             con.getOutputStream().write(postdata.getBytes());  
  18.             con.getOutputStream().flush();  
  19.             con.getOutputStream().close();  
  20.            
  21.             // dumpl all cert info  
  22.             print_https_cert(con);  
  23.             //  
  24.             //System.out.println( con.getLocalPrincipal().toString() );  
  25.             System.out.println( con.getPeerPrincipal().toString() );  
  26.                          
  27.             // dump all the content  
  28.             print_content(con);                   
  29.             // dump all the content  
  30.             print_content(con);  
  31.   
  32.         } catch (MalformedURLException e) {  
  33.             e.printStackTrace();  
  34.         } catch (IOException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  

    一个周末,学了一些东西,值得mark一下。

 

    ps: 有一篇好文章,有助于深入理解。

    http://www.williamlong.info/archives/2058.html




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值