使用HttpsURLConnection的3种方法小结

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

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

    }

}


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

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

1

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

httpPostData(MainActivity.this, url, content);

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;

    }


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

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

1

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

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

httpPostData(MainActivity.this, url, content);

1

最后总结一下:

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

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

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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • retrofit中如何正确的使用https? 很多文章对客户端https的使用都是很模糊的,不但如此,有些开发者直...
    流水潺湲阅读 1,191评论 3 27
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • 1、不安全的随机数生成,在CSRF TOKEN生成、password reset token生成等,会造成toke...
    nightmare丿阅读 3,577评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 在现代社会,我们会发现越来越多的女生有一定的收入和地位,打扮精致品味不俗,他们除了对生活有追求对于婚姻家庭也有期待...
    小九飞飞飞阅读 457评论 0 1