Android学习 - Android网络编程https

https 不验证证书方式(信任所有证书)

前面写了http的联网方式,Android平台上经常有使用https的需求,对于https服务器使用的根证书是受信任的证书的话,实现https是非常简单的,直接用httpclient库就行了,与使用http几乎没有区别。但是在大多数情况下,服务器所使用的根证书是自签名的,或者签名机构不在设备的信任证书列表中,这样使用httpclient进行https连接就会失败。解决这个问题的办法有两种,一是在发起https连接之前将服务器证书加到httpclient的信任证书列表中,这个相对来说比较复杂一些,很容易出错;另一种办法是让httpclient信任所有的服务器证书,这种办法相对来说简单很多,但安全性则差一些,但在某些场合下有一定的应用场景。这一篇主要实现httpclient信任所有的服务器证书。

直接给出代码:

public class HttpsTestActivity extends Activity {
	/** Called when the activity is first created. */
	private TextView text;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		text = (TextView) findViewById(R.id.textView1);
		GetHttps();
	}

	private void GetHttps() {
		String https = "https://www.google.com.hk";
		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, new TrustManager[] { new MyTrustManager() },
					new SecureRandom());
			HttpsURLConnection
					.setDefaultSSLSocketFactory(sc.getSocketFactory());
			HttpsURLConnection
					.setDefaultHostnameVerifier(new MyHostnameVerifier());
			HttpsURLConnection conn = (HttpsURLConnection) new URL(https)
					.openConnection();
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.connect();
			BufferedReader br = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));
			StringBuffer sb = new StringBuffer();
			String line;
			while ((line = br.readLine()) != null)
				sb.append(line);
			text.setText(sb.toString());
		} catch (Exception e) {
			Log.e(this.getClass().getName(), e.getMessage());
		}
	}

	private class MyHostnameVerifier implements HostnameVerifier {
		@Override
		public boolean verify(String hostname, SSLSession session) {
			// TODO Auto-generated method stub
			return true;
		}
	}

	private class MyTrustManager implements X509TrustManager {
		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType)
				throws CertificateException {
			// TODO Auto-generated method stub
		}

		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType)

		throws CertificateException {
			// TODO Auto-generated method stub
		}

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

使用HttpsURLConnection时需要实现HostnameVerifier和X509TrustManager,这两个实现是必须的,要不会报安全验证异常。

使用httpclient对self-signedcertificate网站进行SSL连线

Android SDK在进行https连线时,对于自签署的凭证是会拒绝连线的,会得到Not trustedserver certificate的例外。如果使用HttpsURLConnection来连线,网络上可以找到一些破解方法,在此不多谈。使用apache httpclient其实执行效率比较差一点,但是他最大的好处就是有内建的机制储存cookie,并且也可以跟随server作自动转址。网络上资料比较多的是 httpclient 3.x版,Android使用httpclient 4(而且还有些实作被拿掉)唯一找到比较可信的来源是apachehttpclient官方的example。节录重点段落如下:

KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File("my.keystore"));
try {
    trustStore.load(instream, "nopassword".toCharArray());
} finally {
    instream.close();
}
SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
Scheme sch = new Scheme("https", socketFactory, 443);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);

本来以为是要制造一个假的空凭证骗过httpclient,从Android档案系统有点微妙开始改来改去,一连串不同的例外或直接crash就不多谈了。解决了档案路径,到底有没有建立等等方面的问题之后才终于发现,假凭证是不行的。

1、开启你PC或Mac上的浏览器连上目标网站,从凭证管理的地方把该网站的凭证汇出。每个浏览器做法都不同,请各位发挥正常工程师的水准做完这件事。以Firefox为例,找到site名后选汇出,得到一个文档名为your_site_name.crt的X509(PEM)凭证档。为了之后使用方便,先把这档案复制一份并把文档名改为your_site_name.pem。

2、将这个凭证格式从PEM转为BKS格式,这是关键性的一步啊。

KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());

这行中的getDefaultType会get到BKS。有J2ME经验的人会想说,我看多半是跟J2ME一样的Sun JKS格式吧。而且我不要用getDefaultType就好了,我可以自己指定为PEM、JKS格式啊。是的,你可以。只是你会得到错误讯息说KeyStore JKS implementation not found。测了半天,看起来Android的httpclient只吃BKS就对了,其他都没实作。

3、那格式要怎么转?根据网路上找到的资料,可以使用KeyToolIUI这个Java工具。打开后从介面选create→KeyStore,格式选BKS,自己命名一下,要不要设密码都可。接着选import→Keystore’sentry→Trusted certificate→Regular certificate。Source的部分选PEM并选到刚刚浏览器得到的那个档,Target当然选BKS和刚create出来的档。按OK之后会跳出Entries视窗,下方提示你enternew alias,随便取个名字后ok连打,顺利的话你应该就会有一个your_keystore.bks之类的档案了。

4、要把凭证档放在Androidapp能读到的地方,做法有两种,放在SDcard中,或直接放在resources中。

方案一:放在SD card的情形。首先模拟器要挂上SD card,这在Eclipse用AVD工具产生avd时就可以顺便指定SD了。如果习惯用指令的话,也可以用如下指令产生image并mount上:

$ mksdcard 512M my_sdcard.so
$ emulator -sdcard ./my_sdcard.so

再来要把凭证档copy到SDcard中,目前只知道指令的做法:

$ adb push file_path/your_keystore.bks /sdcard

如果有显示类似ftp传输速度之类的讯息应该就是成功了。

接着把android程式码中档案部分改一改:

FileInputStream instream = new FileInputStream(new File("/sdcard/your_keystore.bks"));
trustStore.load(instream, null);

如果没设密码,可以把keystoreload的第二个密码参数改成null,有设的话当然就改成你的密码。基本上这样应该就大功告成了。

方案二:再讲讲凭证放在resources的方法。

首先把档案复制到专案下的res/raw/your_keystore.bks。Eclipse没有错误的话就ok,否则多半是你档名不合规格,稍微改改。接着取用方法是把android程式码改成:

InputStream instream = getResources().openRawResource(R.raw.your_keystore);

其它同SD card。

5、还有一个地方要注意,就是SSLport。

Scheme sch = new Scheme("https", socketFactory, 443);

如果你要连的网站不是用port 443,这边请记得改掉。

6、另外如果要使用档案放在resources的做法,getResources()应该只能在Activityclass中执行,如果另外包装连线类别的话请不要直接服用上面的程式码,要自行从context抓到资源再传过去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值