使用HttpsURLConnection的3种方法小结

来源:互联网 发布:unity3d 2d动画贴图 编辑:程序博客网 时间:2024/06/07 17:46

最近遇到网络安全方面的问题,要将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();    }

调用示例:

httpPostData(MainActivity.this, url, content);

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());    }}

再在所有https开始进行请求之前,执行一次即可:

HTTPSTrustManager.allowAllSSL();//信任所有证书

后面就是正常的进行https访问就可以了:

httpPostData(MainActivity.this, url, content);

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;    }

然后,在使用 HttpsURLConnection 的过程中,也就是httpsPostData()函数中,使用指定证书的 SSLContext 即可:

conn.setSSLSocketFactory(getSSLContext(context).getSocketFactory());

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

至于调用POST请求的地方,是一样的:

httpPostData(MainActivity.this, url, content);

最后总结一下:

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

1 0
原创粉丝点击