Android HTTPS实战1

看完了理论Android HTTPS基础,HTTPS实际在Android中的是怎样应用的呢?

通过查看官方文档,通过 HTTPS 和 SSL 确保安全, 我们看到,访问HTTPS非常简单,系统已经完成了全部的工作,代码如下:

    URL url = new URL("https://wikipedia.org");
    URLConnection urlConnection = url.openConnection();
    InputStream in = urlConnection.getInputStream();
    copyInputStreamToOutputStream(in, System.out);

没错,就这么简单。url.openConnection()根据请求URL区分返回的是HttpURLConnection还是HttpsURLConnection。只要你的服务器证书是由知名 CA 颁发的,Android系统内部会自动去验证服务器证书的有效性,建立起安全的连接。

如何从CA申请证书,可以参看阿里云证书申请。证书有免费的和收费的, 分为单域名,多域名,通配符域名等,可以从不同的知名CA中申请,例如Symantec赛门铁克, CFCA中国金融认证中心,GlobalSign, GeoTrust等等。申请完成后,部署在服务器就可以了。

如果我们只需要简单跑通代码,不需要往下看了,看看官网文档即可。下面是简单了解一下Android是怎样验证服务器证书的,以及自签名证书如何使用。

Android如何验证服务器证书?从前面HTTPS基础我们知道

如果某受信CA为"Sam的签名商店"签发了一个证书,而Sam的签名商店也签发了一个站点证书,浏览器可能会将其作为从有效CA路径导出的证书接受。

所以Android系统内置了很多知名CA的根证书,只要是该CA签发的证书(中间CA),以及中间CA签发的证书,形成了证书链,该证书链上的所有证书都是可信任的。
更详细Android源代码可参考Android 根证书管理与证书验证

除了看源代码,还可以如何观察验证证书过程呢?

HttpsURLConnection位于javax.net.ssl包下,提供了两个方法,可以传HostnameVerifier,SSLSocketFactory的类实例进去,HttpsURLConnection实例每次发起HTTPS请求都会调用这两个对象的方法。

public void setHostnameVerifier(HostnameVerifier v) {
}

public void setSSLSocketFactory(SSLSocketFactory sf) {
}

public interface HostnameVerifier {
    /**
     * Verify that the host name is an acceptable match with
     * the server's authentication scheme.
     *
     * @param hostname the host name
     * @param session SSLSession used on the connection to host
     * @return true if the host name is acceptable
     */
    public boolean verify(String hostname, SSLSession session);
}

HostnameVerifier是用来查看要访问的域名是否正确,verify方法返回true则继续访问, 返回false则阻止访问。这里跟服务器证书没啥关系,仅仅是用来检查客户端的请求URL域名部分。

SSLSocketFactory是一个抽象类,继承SocketFactory。javax.net.SocketFactory就是一个创建普通Socket的抽象工厂类,看看源码就明白,没啥好说的。而SSLSocketFactory顾名思义,是创建基于SSL的安全Socket的抽象工厂类,它的实例的创建不是通过简单的继承,下面给出一个创建实例的方法:MyX509TrustManager.getSSLSocketFactory():

public class MyX509TrustManager implements X509TrustManager {

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

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

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


    public static SSLSocketFactory getSSLSocketFactory() {
        SSLContext context = null;
        TrustManager[] trustManagers = new TrustManager[]{new MyX509TrustManager()};

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        return context.getSocketFactory();
    }
}

X509TrustManager是一个接口,提供了验证服务器证书的checkServerTrusted方法,在这里面我们就可以检查服务端证书是否正确。

/**
     * Given the partial or complete certificate chain provided by the
     * peer, build a certificate path to a trusted root and return if
     * it can be validated and is trusted for server SSL
     * authentication based on the authentication type.
     * 

* The authentication type is the key exchange algorithm portion * of the cipher suites represented as a String, such as "RSA", * "DHE_DSS". Note: for some exportable cipher suites, the key * exchange algorithm is determined at run time during the * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, * the authType should be RSA_EXPORT when an ephemeral RSA key is * used for the key exchange, and RSA when the key from the server * certificate is used. Checking is case-sensitive. * * @param chain the peer certificate chain * @param authType the key exchange algorithm used * @throws IllegalArgumentException if null or zero-length chain * is passed in for the chain parameter or if null or zero-length * string is passed in for the authType parameter * @throws CertificateException if the certificate chain is not trusted * by this TrustManager. */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException;

参数X509Certificate[] chain就是X.509 v3证书链,一般chain[0]就是根证书,chain[1]可能是中间证书,chain[2]就是服务器证书,取决于chain数组的长度,可以通过代码获取每个证书版本号,序列号,有效日期,CA签名,使用者名称,使用者公钥等等。

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        for (X509Certificate c : chain) {
            Log.d("TAG", "c version:"+c.getVersion()+" SerialNumber:" + c.getSerialNumber()+
                    " Principal:"+c.getIssuerDN().toString()+" X500Principal:"+c.getIssuerX500Principal().toString()+
                    " getSubjectDN:"+c.getSubjectDN().toString() +
                    " pb:" + GlobalUtils.getMD5(c.getPublicKey().getEncoded()));
        }
    }

如果不信任该证书,这里手动抛出异常即可,该连接就会断掉
如果需要自己处理来信任证书,比如自签名证书,也是在这里处理

我们可以看一下服务器证书一般是长啥样的,以win10系统,浏览器chrome访问百度https://www.baidu.com为例:
Android HTTPS实战1_第1张图片
Android HTTPS实战1_第2张图片
Android HTTPS实战1_第3张图片

参考:
volley源码
通过 HTTPS 和 SSL 确保安全
Android 根证书管理与证书验证
Android Https相关完全解析 当OkHttp遇到Https
X.509证书的解析、验证及使用
SSL证书原理讲解
关于SSL证书之证书链
https证书配置完整证书链常见问题

你可能感兴趣的:(Android,Web开发,java)