Android与服务端使用Https加密通信

Https证书

现在网络安全越来越受重视,通用做法是采用https加密通信,使用https需要数字证书,只有合法的证书才能被浏览器、操作系统默认支持,而所谓的合法证书是在CA公司那购买的(原来我们的合法性是花钱从别人那买来的,不得不吐槽这种互联网安全设计真是坑爹),虽然现在也有一些免费CA证书,但申请还是挺麻烦,这里我们使用自己生成的https证书。

服务端使用https

生成https证书

JDK自带的keytool工具可以很方便生成https证书,可以查看它的使用方法:

Android与服务端使用Https加密通信_第1张图片

比如这条命令就可以生成一个有效期10年的证书:

keytool -genkey -alias spring -validity 3560 -keystore spring.keystore 

服务端配置https证书

服务端一般使用Tomcat、Jetty、Undertow等作为Servlet容器,我们将上面生成的keystore证书放在项目中,然后在配置文件中引入证书即可:

server.ssl.key-store=spring.keystore
server.ssl.key-alias=spring
server.ssl.key-password=password
server.ssl.key-store-type=JKS

这样服务端就支持https了,启动项目访问服务就要加https前缀,如 https://localhost:8080/user

Android自定义https校验

如果是花钱买的CA证书是不需要额外配置的,Android系统内部有信任列表,会自行校验通过,这里讲配置自定义https校验。

Android端通常使用 Retrofit 做网络请求,Retrofit底层就是OKhttp,OKhttp实现自定义https校验并不难,主要分三步。

把证书公钥预埋在APP中

这条命令可以导出证书公钥字符串:

keytool -list -rfc -keystore tomcat.keystore
Android与服务端使用Https加密通信_第2张图片

把这个公钥作为一个字符串常量放在项目中供后面校验使用。

自定义证书校验逻辑

使用上面的公钥字符串构建X509TrustManager对象,在checkServerTrusted方法中校验服务端证书:

X509TrustManager trustManager = new X509TrustManager() {
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        //校验客户端证书
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        //校验服务端证书
        X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(PUB_KEY.getBytes()));
        for (X509Certificate cert : chain) {
            // 检查服务端证书是否过期
            cert.checkValidity();
            try {
                //和APP预埋证书对比
                cert.verify(ca.getPublicKey());
            } catch (Exception e) {
                //证书校验异常
                throw new SecurityException("证书错误!");
            }
        }
    }

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

注意证书校验异常时抛出一个SecurityException,如果不抛出这个RuntimeException,程序会继续执行,请求依然正常,证书校验就没有意义。

设置OkHttpClient校验证书

然后使用上面的trustManager构建OkHttpClient

SSLSocketFactory sslSocketFactory = null;
try {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
    sslSocketFactory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
    e.printStackTrace();
}
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
    @Override
    public boolean verify(final String hostname,
                          final SSLSession session) {
        //服务端主机域名地址校验
        return true;
    }
};
OkHttpClient client = new OkHttpClient.Builder()
        .hostnameVerifier(hostnameVerifier)
        .sslSocketFactory(sslSocketFactory, trustManager)
        .build();

使用这个配置好的OkHttpClient与服务端交互,就可以支持自定义https证书加密通信了,如果服务端证书不符,请求会自动断开。

Android与服务端使用Https加密通信_第3张图片
扫一扫关注我的微信公众号

你可能感兴趣的:(Android与服务端使用Https加密通信)