Android Https的详解

22 篇文章 0 订阅
5 篇文章 0 订阅

Https的通信过程

在这里插入图片描述

两种加密

加密方式分两种,对称加密非对称加密。这两种方式都有自己的优劣势, https中这两种方式都采用了。 我们约定S是服务端,C是客户端,客户端需要从服务端获取信息;

对称加密

这种加密方式比较简单,就是双方都持有密匙。S和C都持有密匙, S通过密匙加密明文传递给C,C获取加密后的信息,用密匙解密信息。

  • 优势: 加密速度快
  • 劣势: 密匙的传递是个问题,容易被截取,密匙一旦被截取后, 就能轻易破解信息。

常见的对称加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。

非对称加密

非对称加密中,S和C端都有自己的公钥和私钥。公钥是公开的,私钥是私有的,私钥需要保密的。 这套公钥和私钥的有个两种加密解密流程:

  • 用公钥加密的信息,用私钥才能解密。因为私钥是私有的, 这种流程用于信息的加密解密;
  • 用私钥加密信息,用公钥来解密。因为公钥是共有的,这种流程用于认证。

在https中信息传递的密匙的传递是采用非对称加密传递的.
C端需要把信息传递给S端, 需要分几步.

  1. C端请求S端,S端把自己的公钥传递给C端。
  2. C用S的公钥把信息加密后传递给S. S用自己的私钥解密获取信息。

常用的非对称加密算法有RSA、Elgamal、Rabin、D-H、ECC(椭圆曲线加密算法)等。

问:既然对称加密和非对称加密都需要保密好自己的私钥, 那有什么区别呢?

对称加密中,私钥不仅需要自己知道也需要解密方知道。 这样私钥就有一个传递的流程, 这个流程就会有很大风险。 而非对称加密只需要自己保密好自己的私钥就好了。 公钥大家都知道,不需要保密,就少了一个私钥传递的过程。 少了很大的风险。

HTTPS中的SSL/TLS协议

HTTPS = HTTP + SSL/TLS协议
  • SSL的全称是Secure Sockets
    Layer,即安全套接层协议,是为网络通信提供安全及数据完整性的一种安全协议。SSL协议在1994年被Netscape发明,后来各个浏览器均支持SSL,其最新的版本是3.0;
  • TLS的全称是Transport Layer Security,即安全传输层协议,最新版本的TLS建立在SSL
    3.0协议规范之上.在理解HTTPS时候,可以把SSL和TLS看做是同一个协议。
HTTPS加密方式

HTTPS为了兼顾安全与效率,同时使用了对称加密和非对称加密。

  • 数据是被对称加密传输的,对称加密过程需要客户端的一个密钥,为了确保能把该密钥安全传输到服务器端;
  • 采用非对称加密对该密钥进行加密传输,总的来说,对数据进行对称加密,对称加密所要使用的密钥通过非对称加密传输。

HTTPS通信流程

一个HTTPS请求实际上包含了两次HTTP传输,可以细分为8步。

1. 第一次HTTP请求:

1.客户端向服务器发起HTTPS请求,连接到服务器的443端口。
2.服务器端配置存放着权威CA机构颁发的证书,CA证书是用CA机构的私钥加密过的,里面包含服务器端提交给CA机构的公钥,网站域名、有效时长等等信息。服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人,此公钥包含在了加密过的CA证书里。
3.服务器端将自己配置在服务器上且加过密的CA证书发送给客户端。
4.客户端收到服务器端的CA证书之后,世界上的网站是无限多的,而CA机构总共就那么几家。任何正版操作系统都会将所有主流CA机构的公钥内置到操作系统当中,所以我们不用额外获取,解密时只需遍历系统中所有内置的CA机构的公钥,只要有任何一个公钥能够正常解密出数据,就说明它是合法的。客户端用CA机构的公钥对证书进行解密,获取到服务器端的公钥,对公钥进行检查,验证其合法性,如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。

2. 第二次HTTP请求:

1.客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
2.服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
然后服务器将加密后的密文发送给客户端。
3.客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。

数字证书

为什么需要数字证书

在https中需要证书,证书的作用是为了防止"中间人攻击"的。 如果有个中间人M拦截客户端请求,然后M向客户端提供自己的公钥,M再向服务端请求公钥,作为"中介者" 这样客户端和服务端都不知道,信息已经被拦截获取了。这时候就需要证明服务端的公钥是正确的.
怎么证明呢?
就需要权威第三方机构来公正了.这个第三方机构就是CA. 也就是说CA是专门对公钥进行认证,进行担保的,也就是专门给公钥做担保的担保公司。 全球知名的CA也就100多个,这些CA都是全球都认可的,比如VeriSign、GlobalSign等,国内知名的CA有WoSign。

数字证书怎么起作用

不论什么平台,设备的操作系统中都会内置100多个全球公认的CA,说具体点就是设备中存储了这些知名CA的公钥。当客户端接收到服务器的数字证书的时候,会进行如下验证:

1.首先客户端会用设备中内置的CA的公钥尝试解密数字证书,如果所有内置的CA的公钥都无法解密该数字证书,说明该数字证书不是由一个全球知名的CA签发的,这样客户端就无法信任该服务器的数字证书。
2.如果有一个CA的公钥能够成功解密该数字证书,说明该数字证书就是由该CA的私钥签发的,因为被私钥加密的密文只能被与其成对的公钥解密。
3.除此之外,还需要检查客户端当前访问的服务器的域名是与数字证书中提供的“颁发给”这一项吻合,还要检查数字证书是否过期等。
证书链

一般CA不会直接去使用自己的私钥去签名某网站的证书, 一般CA会签发一个子证书, 然后用这子证书去签网站的证书. 有可能有多个子证书. 如果父证书是可以被信任的,那么这个子证书就是可以被信任的.

Android使用Https

这里只讨论单向通信的情况并且是服务器自签名的证书,权威机构颁发过证书的https使用和普通http使用法师没有什么区别。

Android中使用Https一般有两种写法,

  • 第一种是重写证书信任管理器类,也就是就是实现了接口X509TrustManager的类。
  • 第二种证书锁定,直接用预埋的证书来生成TrustManger
第一种是重写证书信任管理器类

接口X509TrustManager有下述三个公有的方法需要我们实现:

⑴ void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。

⑵ void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。

⑶ X509Certificate[] getAcceptedIssuers() 返回受信任的X509证书数组。

主机域名验证

public static class SafeHostnameVerifier implements HostnameVerifier{

        private String hostName;

        public SafeHostnameVerifier(String hostName){
            this.hostName = hostName;
        }

        @Override
        public boolean verify(String hostname, SSLSession session) {
            //  使用Java自带的HttpsURLConnection
//            HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
//            return hostnameVerifier.verify(this.hostName,session);

            if (hostname.equals(this.hostName)){
                return true;
            }else {
                return false;
            }

        }
    }

这里是简单实现,完整的写法可以参看Okhttp的OkHostnameVerifier

证书信任管理

将assets文件夹中的服务器证书文件流转化成证书的方法

//拿到自己的证书
   X509Certificate getX509Certificate(Context context) throws IOException, CertificateException {
       InputStream in = context.getAssets().open("srca.cer");
       CertificateFactory instance = CertificateFactory.getInstance("X.509");
       X509Certificate certificate = (X509Certificate) instance.generateCertificate(in);
       return certificate;
   }
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

 
public class MyX509TrustManager implements X509TrustManager {
    //如果需要对证书进行校验,需要这里去实现,如果不实现的话是不安全 
    X509Certificate mX509Certificate;
 
 
    public MyX509TrustManager(X509Certificate mX509Certificate) {
        this.mX509Certificate = mX509Certificate;
    }
 
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
 
    }
 
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
       for (X509Certificate certificate:chain){
           //检查证书是否有效
           certificate.checkValidity();
           try {
               certificate.verify(mX509Certificate.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];
    }
}

这里需要注意下错误的写法

public static class UnSafeHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            // 没有验证服务器端的域名
            return true;
        }
    }
private static class UnSafeTrustManager implements X509TrustManager
    {
        @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 new java.security.cert.X509Certificate[0]{};
        }
    }

使用方式

  • Java自带的HttpsURLConnection
                URL url = new URL(path);
                //1.改成s
                HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
                //2.SSLContext 初始化
                SSLContext tls = SSLContext.getInstance("TLS");
                MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));
         TrustManager[] trustManagers={myX509TrustManager};
                tls.init(null,trustManagers,new SecureRandom());
                //3.ssl工厂
                SSLSocketFactory factory = tls.getSocketFactory();
                //4.添加一个主机名称校验器
                conn.setHostnameVerifier((new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
                conn.setSSLSocketFactory(factory);
  • OkHttp使用方式

//1.SSLContext 初始化
                SSLContext tls = SSLContext.getInstance("TLS");
                MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));
         TrustManager[] trustManagers={myX509TrustManager};
                tls.init(null,trustManagers,new SecureRandom());
                //3.ssl工厂
                SSLSocketFactory factory = tls.getSocketFactory();
                
OkHttpClient okhttpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true) //设置出现错误进行重新连接。
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                .sslSocketFactory(factory)
                .hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                })
                 .build();
第二种证书锁定,直接用预埋的证书来生成TrustManger

获取本地的服务器端自签名证书

public static SSLSocketFactory getSSlFactory(Context context) {

  // 从assets中加载证书,取到证书的输入流
    InputStream is = getApplicationContext().getAssets().open("srca.cer");
    // 证书工厂
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    Certificate ca = cf.generateCertificate(is);

    // 加载证书到密钥库中
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null);
    keyStore.setCertificateEntry("cert", ca);

    // 加载密钥库到信任管理器
    String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

    // 用 TrustManager 初始化一个 SSLContext
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);
    return sslContext.getSocketFactory();
    }

若server使用的证书是由认证的CA机构颁发的,则如此配置即可(OkHttp等均已支持),或者根本不需要配置,就和普通http一样使用即可

  private X509TrustManager systemDefaultTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
          TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
            + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

  private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
    try {
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, new TrustManager[] { trustManager }, null);
      return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

参考文章:
写一篇最好懂的HTTPS讲解
Android Https心得
android https简介和证书认证
android 使用https请求
深入理解 Android Https
Android安全开发之安全使用HTTPS
Android 使用 HTTPS
android HTTPS认证
Android使用https
Android 上 Https 双向通信— 深入理解KeyManager 和 TrustManagers

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值