您的位置:首页 > 理论基础 > 计算机网络

使用HttpsURLConnection的3种方法小结

2017-07-04 10:08 399 查看
最近遇到网络安全方面的问题,要将http转移到https,由于在工程中使用了HttpURLConnection,所以要相应的转而使用HttpsURLConnection,当然大部分是参考的网络上一些前辈们的成果,过程中也遇到了一些坑,在这里进行一下总结。

由于https涉及到证书的认证方式,这里简单介绍一下: 

关于证书,可以简单把它理解为网站的身份证。而给网站颁发身份证的就是CA(证书颁发机构)。 

可以颁发证书的CA有很多(国内外都有),只有少数CA被认为是权威、公正的,这些CA颁发的证书,浏览器、操作系统才认为是信得过的。 

Android系统中,就有一个根证书信任列表,若我们的证书是由这个列表中的某个根证书的子证书,就不需要在https使用过程中特别指定了。 

我们自己也可以自己制作证书,例如使用OpenSSL就可以生成一个CA根证书,然后用这个根证书颁发两个子证书server和client,server证书放在服务器端,而这个client证书就可以用于浏览器或者安卓app中。这种自己制作的证书,就必须在app中指定了,否则https握手是不能成功的。 

我就按使用证书的不同方式来进行分别说明:

1,信任系统提供的证书(权威CA颁发); 

2,全部信任证书; 

3,信任指定证书;


1,信任系统提供的证书

这是最简单的方式,相比较于http访问,转到https协议,只是将HttpURLConnection 替换为 HttpsURLConnection 就够了。 

下面是一个使用 HttpsURLConnection 实现的POST请求:
public static void httpsPostData(final Context context, final String urlPath, final String content){
new Thread()
{
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
URL url;
try {
url = new URL(TimeValidity.addTimeValidityUrl(urlPath));
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
//conn.setSSLSocketFactory(getSSLContext(context).getSocketFactory());
conn.setConnectTimeout(TIMEOUT_LONG);//5
conn.setReadTimeout(TIMEOUT_LONG);
conn.setDoOutput(true);// 设置允许输出
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
OutputStream os = conn.getOutputStream();
os.write(content.getBytes());
os.close();

/* 服务器返回的响应码 */
int code = conn.getResponseCode();
Log.i("https","code="+code);
if (code == 200) {
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "UTF-8"));
String retData = null;
String responseData = "";
while ((retData = in.readLine()) != null) {
responseData += retData;
}
in.close();
} else {
Log.i("https","return error");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Looper.loop();
}
}.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

调用示例:
httpPostData(MainActivity.this, url, content);
1
1


2,全部信任证书

添加HTTPSTrustManager类,如下:
public class HTTPSTrustManager implements X509TrustManager {

private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};

@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
// To change body of implemented methods use File | Settings | File
// Templates.
}

@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
// To change body of implemented methods use File | Settings | File
// Templates.
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}

public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

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

});

SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[] { new HTTPSTrustManager() };
}

try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

再在所有https开始进行请求之前,执行一次即可:
HTTPSTrustManager.allowAllSSL();//信任所有证书
1
1

后面就是正常的进行https访问就可以了:
httpPostData(MainActivity.this, url, content);
1
1


3,信任指定证书

先要获取到证书,我们可以放到assert目录下,例如这里使用的证书的文件名为“root.crt”。 

通过如下函数来读取,并返回SSLContext:
public static SSLContext getSSLContext(Context inputContext){
SSLContext context = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream in = inputContext.getAssets().open("root.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
context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
} catch (Exception e){
e.printStackTrace();
}
return context;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

然后,在使用 HttpsURLConnection 的过程中,也就是httpsPostData()函数中,使用指定证书的 SSLContext 即可:
conn.setSSLSocketFactory(getSSLContext(context).getSocketFactory());
1
1

当然,如果仔细看了前面的 httpsPostData()函数内容的话,就知道前面的代码中已经有这句话了,只是被注释掉了。打开就可以了,就是使用指定证书的了。

至于调用POST请求的地方,是一样的:
httpPostData(MainActivity.this, url, content);
1
1


最后总结一下:

1,全部信任证书:不太安全,Google也不推荐。但是毕竟是https,比http安全多了,只是还存在被中间人攻击的风险;

2,信任指定证书:这种方式保证了网络传输链路的安全,是可以防住中间人攻击的。 

但是问题可能会出在App上:这个证书直接放在app中,若是由于某些原因导致证书需要更新,就需要更新所有的app,在用户量较大的情况下,这几乎是不可能完成的任务。

3,信任系统提供的证书(CA颁发);这种方式最好,既安全又好维护,更换一个CA颁发的证书,对代码不需要做任何改动,但是可能需要花钱。也可以找些免费的证书,但是使用期限可能有较大的限制。

这三种方式各有特色,具体采用哪种方式,还是需要根据自己项目的实际情况来确定。


参考:

http://blog.csdn.net/whu_zhangmin/article/details/45868057 
http://www.cnblogs.com/cxjchen/p/3152832.html

原创地址:http://blog.csdn.net/lintax/article/details/70195138
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  https