上一篇用netty实现的http代理服务器还无法对https报文进行解密,原因也说了,就是服务器的私钥不在我们这,根据RSA公钥加密私钥解密的特性,如果我们没有私钥的话是不可能获取到https的真实内容的,那有没有什么办法解密https的报文呢,当然有通过代理服务器伪造ssl证书就可以达到目的,那么具体是什么原理的,下面一步一步来分析。

## https协议
首先来回顾下https协议的ssl握手

简单叙述下ssl握手中,只说说ssl单向验证过程

  1. 客户端向服务器发出ssl握手,发送client-random随机数。
  2. 服务器返回ssl证书和server-randon随机数。
  3. 客户端校验ssl证书,校验通过,再生成一个premaster-secret随机数用服务器证书里的公钥加密发送,这个时候客户端已经可以通过三个随机数算出对称加密的密钥了。
  4. 服务器用私钥解密premaster-secret,也拿到了三个随机数算出对称加密的密钥。
  5. 两边都用算出来的对称密钥进行报文加密和解密

注意第三步非常关键,ssl证书是采用信任链的方式来验证ssl证书是否有效,在浏览器中都会内置好许多受信任的CA证书,而CA证书下签发的ssl证书来访问浏览器才会验证通过,不然就会提示证书不安全(12306典型的例子,因为12306是自己签发的CA根证书,不存在于浏览器受信任的证书列表中,所以浏览器会提示不安全)。所以就是要制作一个服务器证书,然后还要让浏览器安全校验通过才行。

动态替换ssl证书


在代理服务器拿到目标服务器ssl证书的时候,不返回给浏览器,而是使用我们自己制作的ssl证书,这个时候有个问题,就是自己制作的证书并不是浏览器中CA根证书签发的,所以我们需要把自己制作的CA根证书加入浏览器受信任的根证书颁发机构。

代理服务器返回的ssl证书中是由刚刚的CA证书签发的,这样就形成了一个受信任链,其实代理服务器就相当于一个浏览器访问目标目标服务器,获取到明文之后在通过客户端和代理服务器建立的ssl连接再转发给浏览器,就可以捕获并拦截到明文了。

openssl制作CA根证书

生成java支持的私钥

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"

上面我们就制作了一个自己的CA证书,打开ca.crt来看一看

这里我们就拥有了CA根证书的私钥,只要在程序中通过这个私钥就能向下签发服务器证书了,如图代理服务器签发了一个www.baidu.com的ssl证书

java动态签发ssl证书

JAVA自带的SSL以及X509库只能使用SSL证书,不能生成SSL证书。因此我们使用“Bouncy Castle”这个算法库来实现SSL证书的生成。

//maven
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.49</version>
</dependency>
//注册bouncycastle
Security.addProvider(new BouncyCastleProvider());
//生成ssl证书公钥和私钥
KeyPairGenerator caKeyPairGen = KeyPairGenerator.getInstance("RSA", "BC");
caKeyPairGen.initialize(2048, new SecureRandom());
PrivateKey serverPriKey = keyPair.getPrivate();
PublicKey  serverPubKey = keyPair.getPublic();
//通过CA私钥动态签发ssl证书
public static X509Certificate genCert(String issuer, PublicKey serverPubKey, PrivateKey caPriKey, String host) throws Exception {X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();String issuer = "C=CN, ST=GD, L=SZ, O=lee, OU=study, CN=ProxyeeRoot";String subject = "C=CN, ST=GD, L=SZ, O=lee, OU=study, CN=" + host;v3CertGen.reset();v3CertGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));v3CertGen.setIssuerDN(new X509Principal(issuer));v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 10 * ONE_DAY));v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + 3650 * ONE_DAY));v3CertGen.setSubjectDN(new X509Principal(subject));v3CertGen.setPublicKey(serverPubKey);//SHA256 Chrome需要此哈希算法否则会出现不安全提示v3CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption");//SAN扩展 Chrome需要此扩展否则会出现不安全提示GeneralNames subjectAltName = new GeneralNames(new GeneralName(GeneralName.dNSName, host));v3CertGen.addExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);X509Certificate cert = v3CertGen.generateX509Certificate(caPriKey);return cert;}

至此我们最重要的功能已经实现了,接着就是拿着ssl证书返回给客户端即可捕获明文了。

效果

这里我拦截了访问https://www.baidu.com的请求,并在加上了一对响应头。

这样就以动态签发ssl证书方法,解决了https明文捕获的问题。此外还添加了对websocket的支持,并且提供拦截器对外使用,实现上面效果的代码如下:

new NettyHttpProxyServer().initProxyInterceptFactory(() -> new HttpProxyIntercept() {@Overridepublic boolean afterResponse(Channel clientChannel, Channel proxyChannel, HttpResponse httpResponse) {//拦截响应,添加一个响应头httpResponse.headers().add("intercept","test");return true;}}).start(9999);

后记

希望通过一个sdk的方式把http代理服务器开源出去供大家使用,源码托管在github,欢迎start。
笔者现在用这个sdk嗅探http下载请求,然后对于支持http断点下载的文件,采用多连接分段下载,想体验可以在这里看看。
当然也可以做到类似fiddler的自动替换请求体、响应体功能,具体就看大家的业务需求了!

JAVA写HTTP代理服务器(三)-https明文捕获相关推荐

  1. JAVA写HTTP代理服务器(二)-netty实现

    接上一篇,因为是用BIO的一个连接一个线程去处理,对于HTTP这种短连接协议来说CPU开销是非常大的,就算加入了线程池也不能完美解决BIO的缺陷,所以可以用NIO进行服务器的优化,NIO基于IO多路复 ...

  2. java 写文件的三种方法比较

    原文:http://www.cnblogs.com/yezhenhan/archive/2012/09/10/2678690.html import java.io.File; import java ...

  3. JAVA写HTTP代理服务器(一)-socket实现

    HTTP代理服务器是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接.一些网关.路由器等网络设备具备网络代理功能.一般认为代理服务有利于 ...

  4. java 写文件 0x0d_Java的文件读写操作 转

    目录: file内存----输入流----程序----输出流----file内存 java中多种方式读文件 判断文件是否存在不存在创建文件 判断文件夹是否存在不存在创建文件夹 java 写文件的三种方 ...

  5. java gui 怎么添加背景图片_三分钟教你学会用java写客户端程序!!速进!!

    写了这么多期的代码, 一直都是在控制台里面, 都没有像别的软件那样, 可以有一个桌面客户端的形式, 可以用鼠标点, 用键盘输入. 下面我们就来看看, 怎么用Java写一个客户端程序. 开局还是我们熟悉 ...

  6. JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!

    JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...

  7. 【Java基础】IO流概述分类、字节流写数据、字节流写数据的三种方式及写数据的两个小问题

    目录 一.IO流概述和分类 二.字节流写数据 三.字节流写数据的三种方式 四.字节流写数据的两个小问题 一.IO流概述和分类 IO流介绍: ● IO:输入/输出(Input/Output) ● 流:是 ...

  8. java上机题四取三排列_Java练习题

    Java的问答题. 一. 基本 1.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以,限制:一个文件中只能有一个public类,并且此public类必 ...

  9. Java实现Http代理服务器通过http代理进行内网安装yum软件

    Java实现Http代理服务器&通过http代理进行内网安装yum软件 1.Http代理服务器简介 2.Http代理服务器Java实现 2.1 Java源码 2.2 代码分析说明 3.通过ht ...

最新文章

  1. ACM POJ 2723 Get Luffy Out(2-SAT入门)
  2. 稀疏多项式的运算用链表_用漫画告诉你—什么是HashMap?
  3. python连不上树莓派_树莓派respberry中cmake编译链接python2.7库不成功,求教
  4. 面试精讲之面试考点及大厂真题 - 分布式专栏 21 限流不能乱用-系统限流最佳实践
  5. 一个不错的网络基础知识网站
  6. 通过UserAgent判断智能设备(Android,IOS)
  7. 我在百度运维的成长经历 之六
  8. debian添加快捷启动方式
  9. 苹果手机iOS系统如何降级 数据备份?
  10. UE4项目的命名规则
  11. 统计学权威盘点过去50年最重要的统计学思想
  12. python修改xml文件内容,不废话,拿来即用
  13. html 确定取消dialog,弹出一个带确认和取消的dialog实例
  14. as常用固定搭配_as...as的结构和固定搭配都有什么
  15. netcfg_route_netstat_setprop 等命令的使用
  16. 带省略号的比喻句_标点符号往往能引发人们的联想,例如:“省略号像一条漫长的人生道路,等着你去书写它留下的空白。”请以一种标点符号(省略号除外)为描述对象,写一个比喻句,形象地阐发某种生活道理。...
  17. 不仅仅是Google,您必须知道的全球十大地图API
  18. 2021面试题——微信小程序面试题
  19. Vant IndexBar 在小程序中的简单使用
  20. 5736. Single-Threaded CPU

热门文章

  1. java 头尾 队列_源码|jdk源码之栈、队列及ArrayDeque分析
  2. java爬虫面试题_使用Java实现网络爬虫
  3. 语言学与计算机应用学什么,2016考研专业:语言学及应用语言学
  4. pythonshellnohup_python nohup 实现远程运行不宕机操作
  5. FPGA之道(83)功能仿真之仿真语法(Graphic Waveform )
  6. Node 深入Stream(2)
  7. crontab工具介绍
  8. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #20 使用fio进行I/O的基准测试...
  9. mysql-5.7.14-win32.zip 解压版 安装过程
  10. 【白话设计模式八】命令模式(Command)