CA证书
CA证书:客户端识别自己访问服务端的身份证明。换句话说,客户端访问www.58.com,怎么能确定返回的消息是58服务器返回的数据,而不是假冒网站返回的消息。权威机构为服务器颁发数字证书(怎么认证自己就是本人呢,通过公安局颁发的身份证)。
CA机构:(Certificate Authority)即颁发数字证书的机构。是电子交易、网络数据交流中权威的受信任的第三方机构,承担公钥体系中公钥的合法性检验的责任。(机构私钥A 机构公钥A)机构公钥内置在电脑的操作系统中。
服务器申请证书
- 服务器向证书机构申请证书,同时提供自己的域名、地址、公钥等信息。
- 证书机构对服务器的信息使用自己的私钥进行非对称加密得到证书
- 客户端请求服务器时,服务器把证书返回给客户端。
哈希算法
又称摘要算法: 作用是对任意一组输入数据进行计算,得到一个固定长度的输出摘要。检验数据完整性的重要技术,运算结果具有不可逆性。
客户端验证证书
- 客户端拿到证书,得到服务器信息、数字签名、证书机构信息。
- 客户端对服务器信息进行哈希算法计算得出一份摘要S1。
- 客户端使用证书机构的公钥对数字签名进行解密得到一份摘要S2。
- 对比S1和S2即可辨别此证书是否来自服务器且没经过篡改。
证书链
计算机产商,会在系统中安装一些根证书机构的信息,其中就包含了这些机构的公钥。这些公钥是在一定程度上是绝对安全的,是可以信任的。客户端可以使用这些公钥对数字签名进行解密。系统中预装的证书机构是有限的,但世界上每时每刻申请数字证书却非常多,他们“忙不过来”,因此有了二级证书机构。二级证书机构由根证书机构签发,二级证书机构再去给服务器签发证书。
- 利用根证书机构给二级证书机构签发的时候同样是一份数字证书,其中包含了二级证书机构信息、数字签名、根证书机构信息。
- 服务器的数字证书中包含了二级证书机构的数字证书。
- 客户端使用根证书机构的公钥对二级证书机构的数字签名进行解密得到摘要再进行比对,得到二级证书机构的公钥。
- 使用二级证书机构的公钥对服务器证书进行验证。
58同城证书
58网页就是包含了三级的证书机构的证书链
- 根证书颁发机构——过期时间:2029年3月18日 星期日 中国标准时间 18:00:00
- 中级证书颁发机构——过期时间:2028年11月21日 星期二 中国标准时间 08:00:00
- *.58.com——过期时间:2023年3月28日 星期二 中国标准时间 10:51:06
openssl制作CA根证书
openssl genrsa -des3 -out ca.key 2048
openssl pkcs8 -topk8 -nocrypt -inform PEM -outform DER -in ca.key -out ca_private.pem
- 再通过CA私钥生成CA证书
openssl req -sha256 -new -x509 -days 365 -key ca.key -out ca.crt \
-subj "/C=CN/ST=GD/L=SZ/O=lee/OU=study/CN=ProxyeeRoot"
具体可以看这篇文章openssl用法详解
Https通信过程
对称加密
对称加密是指加密和解密都用同一份密钥
非对称加密
非对称加密对应于一对密钥,称为私钥和公钥,用私钥加密后需要用公钥解密,用公钥加密后需要用私钥解密
建立连接的过程
Client Hello报文: 作为协商的开始。 报文最主要包含:TLS版本号,随机数(Client Random),密码套件列表,以及sessionID。
其中
-
TLS版本号:用来确定当前TLS采用的版本,目前有TLS1.0,TLS1.1, TLS1.2,TLS1.3。
-
SessionID:用来表示客户端是否想复用先前存在的session。如果不复用,则此字段为0;如果想要复用,则此字段为想要复用的sessionID。是否复用由服务端确定。
- 服务器向客户端发送数字证书,服务端生成随机数R2(Server Random),从客户端支持的加密算法中选择一种双方都支持的加密算法(此算法用于后面的会话密钥生成),服务端把证书、随机数R2、加密算法,一同发给客户端;
Server Hello, Certificate, ServerHellodone共计三个报文
-
ServerHello 报文: 是对ClientHello报文中协商确认的结果。包括:TLS版本号,随机数(Server Random), SeRSSionID, 加密套件。
-
Certificate 报文:返回数字证书
-
ServerHellodone 报文:表明服务端的响应已经发送完毕,客户端可以进行后续处理操作;
- 客户端验证数字证书。
-
验证证书的可靠性,先用CA的公钥解密被加密过后的证书,能解密则说明证书没有问题,然后通过证书里提供的摘要算法进行对数据进行摘要,然后通过自己生成的摘要与服务端发送的摘要比对。
-
验证证书合法性,包括证书是否吊销、是否到期、域名是否匹配,通过后则进行后面的流程
-
用服务端证书的公钥加密随机数R3并发送给服务端。
- ClientKeyExchange 报文:携带客户端为密钥交换的所有信息
- Change Cipher Spec 报文:通知服务端下一个传送的数据采用新的加密方式。
- Encrypted handshake 报文:用对称秘钥进行加密的第一个报文,如果这个报文加解密校验成功,那么就说明对称秘钥是正确的。
PreMasterKey:是第三个随机数R3
- 服务器得到会话密钥
(1)服务器用私钥解密客户端发过来的随机数R3
(2)根据会话秘钥算法使用R1、R2、R3生成会话秘钥
通信的过程
- 客户端使用会话密钥对传输的数据进行对称加密传输给服务器;
- 服务器使用会话密钥对传输的数据进行解密;
- 服务器使用会话密钥对响应的数据进行对称加密传输给客户端;
- 客户端使用会话密钥对传输的数据进行解密;
charles 抓包原理
-
客户端向服务器发起HTTPS请求,charles截获客户端发送给服务器的HTTPS请求,charles伪装成客户端向服务器发送请求进行握手。
-
服务器发回响应,charles获取到服务器的CA证书,用根证书(内置)公钥进行解密,验证服务器数据签名,获取到服务器CA证书公钥。然后charles伪造自己的CA证书(这里的CA证书,也是根证书,只不过是charles伪造的根证书),冒充服务器证书传递给客户端。
-
与普通过程中客户端的操作相同,客户端根据返回的数据进行证书校验、生成R3、用charles伪造的证书公钥加密,并生成HTTPS通信用的对称密钥。
-
客户端将重要信息(Charles公钥加密后的随机数)传递给服务器,又被charles截获。charles将截获的密文用自己伪造证书的私钥解开,获得并计算得到HTTPS通信用的对称密钥。charles将客户端发送的数据用服务器证书公钥加密传递给服务器。
-
与普通过程中服务器端的操作相同,服务器用私钥解开后建立信任,然后再发送加密的消息给客户端。
-
charles截获服务器发送的密文,用对称密钥解开,再用自己伪造证书的私钥加密传给客户端。
-
客户端拿到加密信息后,用公钥解开,验证HASH。建立连接正式完成,客户端与服务器端就这样建立了”信任“。
正常加密通信过程中:
- 服务器—>客户端:charles接收到服务器发送的密文,用对称密钥解开,获得服务器发送的明文。再次加密发送给客户端。
- 客户端—>服务端:客户端用对称密钥加密,被charles截获后,解密获得明文。再次加密,发送给服务器端。由于charles一直拥有通信用对称密钥,所以在整个HTTPS通信过程中信息对其透明。
Netty
netty介绍
Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。它极大地简化和流线了网络编程。
Netty核心组件
Handler
为了支持各种协议和处理数据的方式,便诞生了Handler组件。Handler主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。自己处理的业务需要继承Handler。
ChannelInitializer
当一个链接建立时,需要知道怎么来接收或者发送数据,当然我们有各种各样的Handler实现来处理它,那么ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。
Pipeline(ChannelPipeline)
ChannelPipeline 是一个 Handler 的集合,它负责处理和拦截 Channel 的入站事件和出站操作,相当于一个贯穿 Netty 的链。ChannelPipeline 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。ChannelPipeline负责安排Handler的顺序及其执行
Channels
NIO的基本组件,表示一个打开的连接(网络,文件),可以用于处理一个或者多个不同的I/O操作(读/写)
netty写一个简单的demo
HTTP代理
HTTP:超文本传输协议
HTTP协议传输数据是明文传输,任意的人抓包就能看到传输的数据,试着用netty抓一下HTTP请求,http请求经过代理服务器,代理服务器只要负责转发相应的http响应体就可以了。
public class CustomHttpServer {
private final int port;
public CustomHttpServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
new CustomHttpServer(8887).start();
}
public void start() throws Exception {
ServerBootstrap b = new ServerBootstrap(); // 引导器,用来配置参数
NioEventLoopGroup group = new NioEventLoopGroup(); //创建一个线程组:用来处理网络事件(接受客户端连接
b.group(group)
.channel(NioServerSocketChannel.class) //使用 NioServerSocketChannel 作为服务器端通道实现
.childHandler(new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象
@Override
public void initChannel(SocketChannel ch) //往 Pipeline 链中添加自定义的业务
throws Exception {
System.out.println("initChannel ch:" + ch);
ch.pipeline()
.addLast("decoder", new HttpRequestDecoder()) // 用于解码request
.addLast("encoder", new HttpResponseEncoder()) // 用于编码response
.addLast("aggregator", new HttpObjectAggregator(512 * 1024)) // 消息聚合器
.addLast("handler", new HttpHandler()); // 自定义的业务处理
}
})
.option(ChannelOption.SO_BACKLOG, 128) // determining the number of connections queued
.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
b.bind(port).sync();
}
public static class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { // 1
private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
System.out.println("gzp_uri:" + msg.getUri());
if(msg.getUri().startsWith("www.baidu.com")){
System.out.println("gzp_uri_www.baidu.com:" + msg.getUri());
}
if(msg.getUri().startsWith("http://")){
System.out.println("gzp_uri_http://" + msg.getUri());
ByteBuf buf= msg.content();
byte[] bytes= new byte[buf.readableBytes()];//
buf.getBytes(buf.readerIndex(),bytes);
String str =new String(bytes);
System.out.println("gzp_uri_http_content://" + str);
}
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
new HttpResponseStatus(200,
"Connection established"),
Unpooled.wrappedBuffer("<script> alert('this is insert alert hello android ')</script>".getBytes())); // 2
HttpHeaders heads = response.headers();
heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 3
heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.write(response); //将数据写到ChannelPipeline中的下一个 ChannelHandler 开始处理
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelReadComplete");
super.channelReadComplete(ctx);
ctx.flush(); // 4
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exceptionCaught");
if (null != cause) cause.printstacktrace();
if (null != ctx) ctx.close();
}
}
}
HTTPS代理
未完待续。。。