发起请求、验证身份、协商秘钥、加密会话,具体流程如下(此例子只有客户端对服务端的单向验证):
1、客户端向服务端发起建立HTTPS请求。
2、服务器向客户端发送数字证书。
3、客户端验证数字证书,证书验证通过后客户端生成会话密钥(双向验证则此处客户端也会向服务器发送证书)。
4、服务器生成会话密钥(双向验证此处服务端也会对客户端的证书验证)。
5、客户端与服务端开始进行加密会话。
具体的步骤如下:
第一步:客户端向服务端发起请求
1、客户端生成随机数R1 发送给服务端;
2、告诉服务端自己支持哪些加密算法;
第二步:服务器向客户端发送数字证书
1、服务端生成随机数R2;
2、客户端支持的加密算法中选择一种双方都支持的加密算法(此算法用于后面的会话密钥生成)
3、服务端生成把证书、随机数R2、会话密钥生成算法,一同发给客户端;
第三步:客户端验证数字证书。
1、验证证书的可靠性,先用CA的公钥解密被加密过后的证书,能解密则说明证书没有问题,然后通过证书里提供的摘要算法进行对数据进行摘要,然后通过自己生成的摘要与服务端发送的摘要比对。
2、验证证书合法性,包括证书是否吊销、是否到期、域名是否匹配,通过后则进行后面的流程
3、获得证书的公钥、会话密钥生成算法、随机数R2
4、生成一个随机数R3。
5、根据会话秘钥算法使用R1、R2、R3生成会话秘钥。
6、用服务端证书的公钥加密随机数R3并发送给服务端。
第四步:服务器得到会话密钥
1、服务器用私钥解密客户端发过来的随机数R3
2、根据会话秘钥算法使用R1、R2、R3生成会话秘钥
第五步:客户端与服务端进行加密会话
1、客户端发送加密数据给服务端
发送加密数据:客户端加密数据后发送给服务端。
2、服务端响应客户端
解密接收数据:服务端用会话密钥解密客户端发送的数据; 加密响应数据:用会话密钥把响应的数据加密发送给客户端。
3、客户端解密服务端响应的数据
(1)、当我们通过OkhttpClient创立一个okHttpClient 、Request 、Call,并发起同步或者异步请求时;
(2)、okhttp会通过Dispatcher对我们所有的Call(RealCall实现类)进行统一管理,并通过execute()及enqueue()方法对同步或者异步请求进行执行;
(3)、execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()方法,从阻拦器链中获取返回结果;
(4)、拦截器链中,依次通过ApplicationInterceptor(应用拦截器)、RetryAndFollowUpInterceptor(重定向阻拦器)、BridgeInterceptor(桥接阻拦器)、CacheInterceptor(缓存阻拦器)、ConnectInterceptor(连接阻拦器)、NetwrokInterceptor(网络拦截器)、CallServerInterceptor(请求阻拦器)对请求依次处理,与服务的建立连接后,获取返回数据,再经过上述阻拦器依次解决后,最后将结果返回给调用方。
1、 使用Proxy.NO_PROXY
进行防止抓包。
我们在使用OkHttp进行网络请求的时候防止Fiddler抓包可以简单的使用OkHttpClient.Builder
中的builder.proxy(Proxy.NO_PROXY);
方法就可以避免Fiddler基本抓包。
2、使用builder.proxySelector
进行防止抓包。
因为第一种方法只能避免Fiddler基本抓包方式在OkHttp
中还有一种方法可以防止抓包。
builder.proxySelector(new ProxySelector() {
@Override
public List select(URI uri) {
return Collections.singletonList(Proxy.NO_PROXY);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
}
});
3、验证Https证书
这种方式要在app嵌入证书,以okhttp为例:当okhttp使用X509TrustManager对服务器证书进行校验时,如果服务器证书的 subjectDN 和嵌入证书的 subjectDN 一致,我们再进行签名内容 signature 的比对,如果不一致,抛出异常。示例代码如下:
val myCrt: X509Certificate by lazy {
getCrt(R.raw.my_ca)
}
private fun getCrt(@RawRes raw: Int): X509Certificate {
val certificateFactory = CertificateFactory.getInstance("X.509")
val input = ApplicationContext.resources.openRawResource(raw)
input.use {
return certificateFactory.generateCertificate(input) as X509Certificate
}
}
private fun getTrustManagerInRelease(): X509TrustManager {
return object : X509TrustManager {
override fun checkClientTrusted(chain: Array, authType: String?) {}
override fun getAcceptedIssuers(): Array = arrayOf()
override fun checkServerTrusted(chain: Array, authType: String?) {
val myCrt: X509Certificate = myCrt
if (chain[0].subjectDN.name == myCrt.subjectDN.name) {
if (!myCrt.signature!!.contentEquals(chain[0].signature)) {
throw SSLHandshakeException("签名不符!")
}
}
}
}
}
private fun getClient(ssl: SSLSocketFactory, trustManager: X509TrustManager): OkHttpClient {
return OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.proxy(Proxy.NO_PROXY)
.sslSocketFactory(ssl, trustManager)
.build()
}
RetryAndFollowUpInterceptor(重试和重定向拦截器)
第一个接触到请求,最后接触到响应;负责判断是否需要重新发起整个请求;主要就是完成两件事情:重试与重定向。
BridgeInterceptor(桥接拦截器)
补全请求,并对响应进行额外处理
CacheInterceptor(缓存拦截器)
请求前查询缓存,获得响应并判断是否需要缓存
ConnectInterceptor(链接拦截器)
与服务器完成TCP连接 (Socket)
CallServerInterceptor(请求服务拦截器)
与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)
关联:https://blog.csdn.net/qq_45866344/article/details/125052085
(1)内置连接池,支持连接复用;
(2)支持gzip压缩响应体;
(3)通过缓存避免重复的请求;
(4)支持http2,对一台机器的所有请求共享同一个socket
OkHttp里面使用ConnectionPool
实现连接池,而且默认支持5个并发KeepAlive,默认链路生命为5分钟。 双端队列Deque,双端都能进出,用来存储连接的。OkHttp框架采用的是Socket连接,底层涉及到Http协议的封装和解封,TLS/SSL安全协议的封装等;连接池主要涉及到几大类:ConnectionPool,RealConnection,StreamAllocation,ConnectionInterceptor;
1.ConnectionPool类:
该类是OkHttp的连接池,连接池可以有效地提高连接的使用效率;
主要涉及到添加,获取,删除连接等功能;
2.RealConection类:
RealConnection是Connection的是实现类,代表着一个真正的socket连接,RealConnection代表着客户端和服务器端已经有了一个通信链路;
3.StreamAllocation类:
StreamAllocation是外界使用Okhttp的连接的桥梁,通过newStream方法返回一个可使用的流,即HttpCodec实例,通过HttpCodec可以将输入数据封装成相应的Http协议的数据,然后通过socket的输出流发送出去,同时也可以通过socket的输入流读取数据并解析数据;
在OKHttp中,创建了一个阀值是Integer.MAX_VALUE的线程池,它不保留任何最小线程,随时创建更多的线程数,而且如果线程空闲后,只能多活60秒。所以也就说如果收到20个并发请求,线程池会创建20个线程,当完成后的60秒后会自动关闭所有20个线程。他这样设计成不设上限的线程,以保证I/O任务中高阻塞低占用的过程,不会长时间卡在阻塞上。