android中使用https是否对服务证书合法性校验的新的体会

Posted luzhouxiaoshuai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android中使用https是否对服务证书合法性校验的新的体会相关的知识,希望对你有一定的参考价值。

package com.cetcs.logreport.utils;

import android.content.Context;

import org.apache.http.conn.ssl.SSLSocketFactory;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * 
 * 解析服务器证书的类,对服务器端的证书serv.crt进行解析,其中server.crt文件是使用
 * openssl生成的自签名的证书
* */
public class SSLVerifyLogServerCrtSocketFactory extends SSLSocketFactory {

    private static final String TAG = "SSLTrustAllSocketFactory";
    private SSLContext mCtx;
    private  Context context;


    public SSLVerifyLogServerCrtSocketFactory(String crtName, KeyStore truststore, Context context)
            throws Throwable {
        super(truststore);
        this.context = context;
        try {
            InputStream certInputStream = new BufferedInputStream(context.getAssets().open(crtName));
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            final X509Certificate serverCertificate = (X509Certificate) certificateFactory.generateCertificate(certInputStream);
            mCtx = SSLContext.getInstance("TLS");
            mCtx.init(null, new TrustManager[]{new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException {
                            if(x509Certificates == null){
                                throw  new IllegalArgumentException("checkServerTrusted x509Certificates is null ");
                            }
                            if(x509Certificates.length < 0){
                                throw  new IllegalArgumentException("checkServerTrusted x509Certificates is null ");
                            }

                            for(X509Certificate cert :x509Certificates){
                                cert.checkValidity();
                                try {
                                    cert.verify(serverCertificate.getPublicKey());
                                } catch (NoSuchAlgorithmException e) {
                                    e.printStackTrace();
                                } catch (InvalidKeyException e) {
                                    e.printStackTrace();
                                } catch (NoSuchProviderException e) {
                                    e.printStackTrace();
                                } catch (SignatureException e) {
                                    e.printStackTrace();
                                }
                            }

                        }

                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
                    }},
                    null);
        } catch (Exception ex) {
        }
    }

    @Override
    public Socket createSocket(Socket socket, String host,
                               int port, boolean autoClose)
            throws IOException, UnknownHostException {
        return mCtx.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return mCtx.getSocketFactory().createSocket();
    }

    //第一个参数是服务器证书的名字例如:server.crt,第二个参数是应用的上下文
    public static SSLSocketFactory getSocketFactory( String crtName,Context context) {
        try {
            if(crtName == null || "".equalsIgnoreCase(crtName)){
                throw  new IllegalArgumentException(" getSocketFactory crtName is null");
            }
            if(context == null){
                throw  new IllegalArgumentException(" getSocketFactory context is null");
            }
            InputStream certInputStream = new BufferedInputStream(context.getAssets().open(crtName));
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate serverCertificate = (X509Certificate) certificateFactory.generateCertificate(certInputStream);
            //生成一个保护服务器证书的keystore
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            String alias = serverCertificate.getSubjectX500Principal().getName();
            keyStore.setCertificateEntry(alias, serverCertificate);

            //生成SSLSocketFactory
            SSLSocketFactory factory = new SSLVerifyLogServerCrtSocketFactory(crtName,keyStore,context);
            return factory;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

}

需求使用:实现客户端对服务器的校验,需要认证服务器证书的合法性,当https在握手的协议中返回给客户端的证书应该和保存在客户端本地的证书解析出来的域名应该一样,说明服务器返回的证书给保证在本地的证书是一样的,说明服务器是合法的

        try {
            String crtName = "server.crt";
            SSLSocketFactory sf = SSLVerifyLogServerCrtSocketFactory.getSocketFactory(crtName, mContext);
            //对主机的有效域名进行严格校验
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);return new DefaultHttpClient(ccm, params);

 其中server.crt就是保存在手机app 例如assert目录下的证书,是app本地保证的证书,这个证书必须和配置到后台例如tomacat服务器中的证书是一模一样,这里为了客户端验证服务器证书的合法性,在手机app客户端保存了一个证书

 

mContext是activity或者context对应的手机的上下文,如果这里客户端和服务器在建立https的过程中,如果服务器返回给客户端的证书的域名和app本地保存的证书解析出来的域名是一样的,说明服务器是合法的。

如果客户端在和服务器建立https协议的时候,不对服务器的合法性做校验,信任所有的服务器
package com.cetcs.logreport.utils;

/**
 * Created by wei.yuan on 2016/8/2.
 *
 * 该类主要是用于对服务器证书的单项验证
 */

import org.apache.http.conn.ssl.SSLSocketFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSLSocketFactoryEx extends SSLSocketFactory {

    SSLContext sslContext = SSLContext.getInstance("TLS");

    public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException,
            KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);
        // set up a TrustManager that trusts everything
        TrustManager tm = new X509TrustManager() {

            public X509Certificate[] getAcceptedIssuers() {
                //return new X509Certificate[]{};
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                // TODO Auto-generated method stub

            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub

            }
        };

        sslContext.init(null, new TrustManager[] { tm }, new java.security.SecureRandom());
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
            throws IOException, UnknownHostException {
        injectHostname(socket, host);
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }

    private void injectHostname(Socket socket, String host) {
        try {
            Field field = InetAddress.class.getDeclaredField("hostName");
            field.setAccessible(true);
            field.set(socket.getInetAddress(), host);
        } catch (Exception ignored) {
        }
    }
}

 

使用技巧:
   KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null, null);

            SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

 

这样大体就是一个使用心得

以上是关于android中使用https是否对服务证书合法性校验的新的体会的主要内容,如果未能解决你的问题,请参考以下文章

HTTPS 抓包原理以及 Android 端如何防止抓包

https简单配置

HTTPS--握手,证书及秘钥协商

HTTPS原理

ssl证书的工作原理?

Android