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

很久没更新了,其实https明文拦截已经实现很久了只是没发博客(毕竟我太懒),步入正题吧,上一篇用netty实现的http代理服务器还无法对https报文进行解密,原因也说了,就是服务器的私钥不在我们这,根据RSA公钥加密私钥解密的特性,如果我们没有私钥的话是不可能获取到https的真实内容的,那有没有什么办法解密https的报文呢,当然有通过代理服务器伪造ssl证书就可以达到目的,那么具体是什么原理的,下面一步一步来分析。

https协议

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

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证书的时候,不返回给浏览器,而是使用我们自己制作的ssl证书,这个时候有个问题,就是自己制作的证书并不是浏览器中CA根证书签发的,所以我们需要把自己制作的CA根证书加入浏览器受信任的根证书颁发机构。

QQ截图20171031155636.png

代理服务器返回的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证书

QQ截图20171031172306.png

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的请求,并在加上了一对响应头。

QQ截图20171031173455.png

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

new NettyHttpProxyServer().initProxyInterceptFactory(() -> new HttpProxyIntercept() {

    @Override
    public 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的自动替换请求体、响应体功能,具体就看大家的业务需求了!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容