使用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);

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

在Java中,`HttpURLConnection`是一个内置的类,用于通过HTTP协议发送GET、POST等类型的网络请求。对于HTTPS请求,你仍然可以使用`HttpURLConnection`,但是需要进行一些额外的设置,因为HTTPS默认使用的是SSL/TLS加密。 以下是使用`HttpsURLConnection`进行HTTPS请求的基本步骤: 1. **创建连接**: ```java URL url = new URL("https://example.com"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); ``` 注意这里传递的URL是"https"开头的。 2. **配置连接**: - 设置连接为输出流(如果是POST或PUT等需要发送数据的情况): ```java connection.setRequestMethod("POST"); // 或者其他方法如GET, PUT等 ``` - 配置为非缓存请求(如果你不想保存服务器响应): ```java connection.setUseCaches(false); ``` - 可能还需要添加身份验证信息(如果需要): ```java connection.setRequestProperty("Authorization", "Basic " + Base64.getEncoder().encodeToString(YOUR_USERNAME+":"+YOUR_PASSWORD).replace("\n", "")); ``` 3. **打开连接**: ```java connection.connect(); ``` 4. **读取或发送数据**: 如果有数据需要发送,使用`OutputStream`写入;如果没有,可以从`InputStream`读取响应内容。 5. **处理结果**: 检查返回的状态码,处理可能的错误并关闭连接: ```java int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { // 成功,读取响应内容 BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuffer content = new StringBuffer(); while ((inputLine = in.readLine()) != null) { content.append(inputLine); } in.close(); } else { handleErrorResponse(responseCode); // 自定义处理错误的逻辑 } // 关闭连接 connection.disconnect(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值