一、知识预热
我这里就不在赘述https的原理了,网上已经有很好的文章,列表如下:
1、握手过程
HTTPS那些事儿(一)-实例分析:http://blog.youkuaiyun.com/sgbfblog/article/details/28180759
HTTPS那些事儿(二)-实例分析:http://blog.youkuaiyun.com/sgbfblog/article/details/30748177
2、握手报文示例
http://blog.youkuaiyun.com/tterminator/article/details/50675540
3、证书编码格式
http://blog.sina.com.cn/s/blog_7cd471930102x2kl.html
二、https java6如何实现?下面正式进入主题,开讲啦!
我建立一个名称为test的java web工程,index.jsp内容如下:
<html>
<head>
<title>test</title>
</head>
<body>
Hello World!
</body>
</html>
我打好war包放到tomcat应用服务器的webapps目录,tomcat的访问端口是8080,启动tomcat,在浏览器的地址栏输入:http://127.0.0.1:8080/test/index.jsp,就会显示一个这样页面:
那好,到底服务器返回什么数据给浏览器呢?
我们来还原真相!
我们运行一下这段代码,console输出:import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class test { public static void main(String[] args){ try { URL url = new URL("http://127.0.0.1:8080/test/index.jsp"); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.connect(); InputStream input = connection.getInputStream(); byte[] bytes = new byte[1024];// index.jsp页面字节数远远小于1024 int bytesLength = input.read(bytes); System.out.println("服务器返回:\n" + new String(bytes, 0, bytesLength)); connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } } }
上面其实并不是完整的服务器返回给浏览器的内容,被我们输出的只是http报文体,但这并不重要!重要的是,我如果是用https的访问方式:https://127.0.0.1:8080/test/index.jsp,跟http的不同到底在哪里?服务器返回: <html> <head> <title>test</title> </head> <body> Hello World! </body> </html>
其实区别我们早就知道了,http服务器返回的数据没有加密,https服务器返回的数据有加密,就这么简单!
这里稍微补充一下加密知识(因为https都用到了下面这几种,不得不讲啊):
1、对称加密
加解密用同一把秘钥,数据用秘钥加密,就得用这把秘钥解密,这就是所谓的“对称”,比如:des、aes;
https使用对称加密方式加密http报文内容
2、非对称加密
秘钥是一对,数据用其中一把加密,就得用另一把解密,这就是所谓的“不对称”,比如:rsa;
https使用非对称加密来传输对称秘钥
3、摘要算法
不需要秘钥,数据经过摘要计算之后,生成一个固定长度的摘要值,比如:md5、sha;
https使用摘要算法,用来验证数据完整性
那好,https返回的数据是加密的(而且是对称加密)我们说了好几遍了,那就意味着,浏览器如果要知道服务器返回加密数据的原文(明文),双方在加密数据之前,需要确认对称加密的算法(des?aes?还是...)和对称密钥。
这个确认的过程,他们说是握手。
为了能看到这个握手的过程,需要tomcat配置支持https访问,用keytool生成一个秘钥库,在tomcat server.xml配置8443 Connector秘钥库地址,这个网上有现成的教程,这里不再赘述!
我们稍微改一下前面贴的代码:
console输出了一大堆!天啦噜,鬼知道这些东西是什么啊?不急,听我慢慢给你道来......import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; import java.io.InputStream; import java.net.URL; public class test { public static void main(String[] args){ System.setProperty("javax.net.debug", "all");// 输出https握手过程 System.setProperty("javax.net.ssl.trustStore", "e:/tomcat.keystore");// 值配置成与tomcat keyStoreFile一致,服务器发过来的证书,要存在于信任秘钥库中 System.setProperty("javax.net.ssl.trustStorePassword", "123456");// 信任秘钥库密码 System.setProperty("https.protocols", "TLSv1");// https协议版本 try { URL url = new URL("https://127.0.0.1:8443/test/index.jsp"); HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); connection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true;// 证书里面有个扩展域,记录了服务器ip,如果与访问url里面的ip不一致不报错(其实是因为用keytool生成的证书,并没有这个扩展域) } }); connection.connect(); InputStream input = connection.getInputStream(); byte[] bytes = new byte[1024]; int bytesLength = input.read(bytes); System.out.println("服务器返回:\n" + new String(bytes, 0, bytesLength)); connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } } }
我们先关注一下[Raw write]和[Raw read],我们和tomcat服务器之间建立socket连接之后,Raw write是我们写给服务器的字节内容,Raw read是服务器返回给我们的字节内容。
https报文格式:消息类型(第1字节),版本号(第2~3字节),消息体长度(第4~5字节),消息体(消息体长度个字节)
按顺序来看,我本地第一条是这样的:
客户端消息的意思是这样的:[Raw write]: length = 80 0000: 16 03 01 00 4B 01 00 00 47 03 01 59 31 23 36 1F ....K...G..Y1#6. 0010: 6C CC 70 97 E1 24 CD FA 0C 5D 27 5C A9 4C AF 49 l.p..$...]'\.L.I 0020: 2E 23 0F 35 00 4E 51 94 48 7E 99 00 00 20 00 04 .#.5.NQ.H.... .. 0030: 00 05 00 2F 00 33 00 32 00 0A 00 16 00 13 00 09 .../.3.2........ 0040: 00 15 00 12 00 03 00 08 00 14 00 11 00 FF 01 00 ................
16 ->消息类型(1字节):握手 0301 ->版本号(2字节):TLS 1.0 004B ->消息体长度(2字节):75字节 01 ->握手类型(1字节):client hello 000047 ->握手消息体长度(3字节):71字节 0301 ->版本号(2字节):TLS 1.0 593123361F6CCC7097E124CDFA0C5D275CA94CAF492E230F35004E5194487E99 ->秘钥随机数(32字节:4字节时间戳(秒) + 28字节随机数) 00 ->会话SESSION ID长度(1字节):0字节 0020 ->加密套件列表长度(2字节):32字节 0004 ->SSL_RSA_WITH_RC4_128_MD5 0005 ->SSL_RSA_WITH_RC4_128_SHA 002F ->TLS_RSA_WITH_AES_128_CBC_SHA 0033 ->TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0032 ->TLS_DHE_DSS_WITH_AES_128_CBC_SHA 000A ->SSL_RSA_WITH_3DES_EDE_CBC_SHA 0016 ->SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0013 ->SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0009 ->SSL_RSA_WITH_DES_CBC_SHA 0015 ->SSL_DHE_RSA_WITH_DES_CBC_SHA 0012 ->SSL_DHE_DSS_WITH_DES_CBC_SHA 0003 ->SSL_RSA_EXPORT_WITH_RC4_40_MD5 0008 ->SSL_RSA_EXPORT_WITH_DES40_CBC_SHA 0014 ->SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0011 ->SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 00FF ->TLS_EMPTY_RENEGOTIATION_INFO_SCSV 01 ->压缩算法长度(1字节) 00 ->压缩算法:0(NULL)
简言之:服务器你好,我把我支持的加密套件列表发给你了,你挑一个吧,我的秘钥随机数也发给你了,你先记着哦。
接着服务器回答了:
服务器消息的意思如下:[Raw read]: length = 5 0000: 16 03 01 02 B5 ..... [Raw read]: length = 693 0000: 02 00 00 46 03 01 59 31 23 36 33 44 92 7B 7F CC ...F..Y1#63D.... 0010: CB FB 17 E0 DE BF AD 60 25 60 F2 7A 2B F2 86 A9 .......`%`.z+... 0020: 61 C3 81 57 A9 33 20 59 31 23 36 72 75 A3 D9 76 a..W.3 Y1#6ru..v 0030: 14 30 20 DE 26 5E 22 9A A9 17 CC 25 37 15 82 FA .0 .&^"....%7... 0040: BD BA 6E 08 77 C1 EE 00 2F 00 0B 00 02 63 00 02 ..n.w.../....c.. 0050: 60 00 02 5D 30 82 02 59 30 82 01 C2 A0 03 02 01 `..]0..Y0....... 0060: 02 02 04 58 9D 62 4B 30 0D 06 09 2A 86 48 86 F7 ...X.bK0...*.H.. 0070: 0D 01 01 05 05 00 30 71 31 0B 30 09 06 03 55 04 ......0q1.0...U. 0080: 06 13 02 43 4E 31 12 30 10 06 03 55 04 08 13 09 ...CN1.0...U.... 0090: 31 32 37 2E 30 2E 30 2E 31 31 12 30 10 06 03 55 127.0.0.11.0...U 00A0: 04 07 13 09 31 32 37 2E 30 2E 30 2E 31 31 12 30 ....127.0.0.11.0 00B0: 10 06 03 55 04 0A 13 09 31 32 37 2E 30 2E 30 2E ...U....127.0.0. 00C0: 31 31 12 30 10 06 03 55 04 0B 13 09 31 32 37 2E 11.0...U....127. 00D0: 30 2E 30 2E 31 31 12 30 10 06 03 55 04 03 13 09 0.0.11.0...U.... 00E0: 31 32 37 2E 30 2E 30 2E 31 30 1E 17 0D 31 37 30 127.0.0.10...170 00F0: 32 31 30 30 36 34 38 34 33 5A 17 0D 31 37 30 35 210064843Z..1705 0100: 31 31 30 36 34 38 34 33 5A 30 71 31 0B 30 09 06 11064843Z0q1.0.. 0110: 03 55 04 06 13 02 43 4E 31 12 30 10 06 03 55 04 .U....CN1.0...U. 0120: 08 13 09 31 32 37 2E 30 2E 30 2E 31 31 12 30 10 ...127.0.0.11.0. 0130: 06 03 55 04 07 13 09 31 32 37 2E 30 2E 30 2E 31 ..U....127.0.0.1 0140: 31 12 30 10 06 03 55 04 0A 13 09 31 32 37 2E 30 1.0...U....127.0 0150: 2E 30 2E 31 31 12 30 10 06 03 55 04 0B 13 09 31 .0.11.0...U....1 0160: 32 37 2E 30 2E 30 2E 31 31 12 30 10 06 03 55 04 27.0.0.11.0...U. 0170: 03 13 09 31 32 37 2E 30 2E 30 2E 31 30 81 9F 30 ...127.0.0.10..0 0180: 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 81 ...*.H.......... 0190: 8D 00 30 81 89 02 81 81 00 C2 4E 1F 32 7B F7 65 ..0.......N.2..e 01A0: 43 BB 8C A6 70 A5 C6 86 07 C6 B9 1C F0 74 BF EB C...p........t.. 01B0: CE 44 A4 1D 54 A8 73 6F 8C 4D 6E C8 89 8F 75 33 .D..T.so.Mn...u3 01C0: C5 06 45 7F F6 EA 9F F6 40 15 27 CD 1F 2B 34 D0 ..E.....@.'..+4. 01D0: 3F EF DF F7 A7 82 E7 5A 0D 46 01 31 2E 47 1E D2 ?......Z.F.1.G.. 01E0: 32 05 E3 DE 15 4E 96 79 C2 4A F3 83 43 E7 0D B0 2....N.y.J..C... 01F0: 8B EA D1 86 81 73 56 DC 56 48 B0 70 04 18 72 5B .....sV.VH.p..r[ 0200: A7 D5 AB B7 32 B3 5E DF 33 29 9B B8 29 28 F5 E4 ....2.^.3)..)(.. 0210: AB 4B 03 13 D3 1A 2C F2 ED 02 03 01 00 01 30 0D .K....,.......0. 0220: 06 09 2A 86 48 86 F7 0D 01 01 05 05 00 03 81 81 ..*.H........... 0230: 00 0E AC A0 C9 52 D4 50 84 A3 42 C3 99 92 28 87 .....R.P..B...(. 0240: 17 0B 2B E3 61 F1 54 AE 65 16 89 0D B7 73 42 D0 ..+.a.T.e....sB. 0250: 4B 2D D6 CE 5E 2C 71 45 B6 43 3E A9 09 AA 48 4D K-..^,qE.C>...HM 0260: 9B 10 AF 89 E6 EF C2 19 92 C1 88 6D F5 56 64 12 ...........m.Vd. 0270: BC 0E 3D D6 B0 D2 17 7D 4F D4 CF A3 43 63 65 4D ..=.....O...CceM 0280: AA 7D C4 CD 7C FA 44 33 AC F8 10 00 6B 07 7B 6C ......D3....k..l 0290: BA C6 6F 81 84 A0 D4 0C F9 A0 0F 86 90 77 6D CE ..o..........wm. 02A0: D4 79 96 B6 B5 01 1E 66 21 9A 45 E2 DA F0 29 E6 .y.....f!.E...). 02B0: 62 0E 00 00 00 b....
服务端说了三句话,简言之:16 ->消息类型(1字节):握手 0301 ->版本号(2字节):TLS 1.0 02B5 ->消息体长度(2字节):693字节 02 ->握手类型(1字节):Server Hello 000046 ->握手消息体长度(3字节):70字节 0301 ->版本号(2字节):TLS 1.0 593123363344927B7FCCCBFB17E0DEBFAD602560F27A2BF286A961C38157A933 ->秘钥随机数(32字节:4字节时间戳(秒) + 28字节随机数) 20 ->会话SESSION ID长度(1字节):32字节 593123367275A3D976143020DE265E229AA917CC25371582FABDBA6E0877C1EE ->会话SESSION ID 002F ->加密套件:TLS_RSA_WITH_AES_128_CBC_SHA 00 ->压缩算法:0(NULL) 0B ->握手类型(1字节):certificate 000263 ->握手消息体长度(3字节):611字节 000260 ->证书消息长度(3字节):608字节 00025D ->证书长度(3字节):605字节 30820259308201C2A0030201020204589D624B300D06092A864886F70D01010505003071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E31301E170D3137303231303036343834335A170D3137303531313036343834335A3071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E3130819F300D06092A864886F70D010101050003818D0030818902818100C24E1F327BF76543BB8CA670A5C68607C6B91CF074BFEBCE44A41D54A8736F8C4D6EC8898F7533C506457FF6EA9FF6401527CD1F2B34D03FEFDFF7A782E75A0D4601312E471ED23205E3DE154E9679C24AF38343E70DB08BEAD186817356DC5648B0700418725BA7D5ABB732B35EDF33299BB82928F5E4AB4B0313D31A2CF2ED0203010001300D06092A864886F70D0101050500038181000EACA0C952D45084A342C399922887170B2BE361F154AE6516890DB77342D04B2DD6CE5E2C7145B6433EA909AA484D9B10AF89E6EFC21992C1886DF5566412BC0E3DD6B0D2177D4FD4CFA34363654DAA7DC4CD7CFA4433ACF810006B077B6CBAC66F8184A0D40CF9A00F8690776DCED47996B6B5011E66219A45E2DAF029E662 ->证书 0E ->握手类型(1字节):server_hello_done 000000 ->握手消息体长度(3字节):0字节
1、客户端你好!我从你的列表挑了这个加密套件:TLS_RSA_WITH_AES_128_CBC_SHA,我们的非对称用RSA,对称用AES,摘要算法用SHA,我的秘钥随机数也发给你了,你也记着哦。 2、我把证书发给你了,你看一下
3、我说完了
这时候,客户端说了一句:
这个客户端消息的意思是:[Raw write]: length = 139 0000: 16 03 01 00 86 10 00 00 82 00 80 BA 28 87 10 7E ............(... 0010: 63 72 06 4B 5E 16 C8 53 FD 1A 7A 65 46 DD D1 4D cr.K^..S..zeF..M 0020: 3E 95 88 89 F4 40 E4 5C DF 51 F7 2C A2 5C AD 6D >....@.\.Q.,.\.m 0030: A3 A1 08 3B F0 5E 0C 88 64 B3 1D F8 34 67 7B 49 ...;.^..d...4g.I 0040: F6 5F 67 0A FB 3F 54 1F 24 AF 43 DD A6 BB 4B 1E ._g..?T.$.C...K. 0050: 3D D6 DC 63 92 75 61 40 D2 3A E0 66 74 80 D0 B6 =..c.ua@.:.ft... 0060: E6 31 7D AA CA BB 8D 40 6B 45 97 80 81 D6 31 A6 .1.....@kE....1. 0070: 0C DF BF 7B 60 E4 9E E8 D2 09 C0 2E 68 14 EB B4 ....`.......h... 0080: 27 72 FF CC 5F F9 94 3E FB 90 FC 'r.._..>...
通过消息ClientKeyExchange,客户端把最重要的对称秘钥值(preMaster)传给了服务器。注意,真正的对称秘钥(masterKey)是需要通过preMaster和秘钥随机数杂交产生的,接着看完你就会懂。16 ->消息类型(1字节):握手 0301 ->版本号(2字节):TLS 1.0 0086 ->消息体长度(2字节):134字节 10 ->握手类型(1字节):ClientKeyExchange 000082 ->握手消息体长度(3字节):130字节 0080 ->秘钥长度(3字节):128字节 BA2887107E6372064B5E16C853FD1A7A6546DDD14D3E958889F440E45CDF51F72CA25CAD6DA3A1083BF05E0C8864B31DF834677B49F65F670AFB3F541F24AF43DDA6BB4B1E3DD6DC6392756140D23AE0667480D0B6E6317DAACABB8D406B45978081D631A60CDFBF7B60E49EE8D209C02E6814EBB42772FFCC5FF9943EFB90FC ->对称秘钥值(非对称加密,用服务器证书的公钥加密)
紧接着,客户端说了这样一句话:
这是个ChangeCipherSpec消息(消息类型=0x14),客户端告知服务器,我和你之后的报文,消息体内容我会加密,你要用我们之间的秘钥解密才能看到明文。[Raw write]: length = 6 0000: 14 03 01 00 01 01
然后,客户端又说了句话:这条客户端消息翻译如下:[Raw write]: length = 53 0000: 16 03 01 00 30 2E 8C A5 C3 91 B3 50 41 1F 0B 9B ....0......PA... 0010: 24 51 7A E0 36 6E 10 5F 0A 56 1A 47 E4 AD 23 B9 $Qz.6n._.V.G..#. 0020: 1D A3 2A 3D 02 15 98 83 4D D0 89 86 54 B8 B5 25 ..*=....M...T..% 0030: 86 95 B1 5D 20 ...]
16 ->消息类型(1字节):握手 0301->版本号(2字节):TLS 1.0 0030->消息体长度(2字节):48字节 2E8CA5C391B350411F0B9B24517AE0366E105F0A561A47E4AD23B91DA32A3D021598834DD0898654B8B5258695B15D20 ->Finishd消息体
这时候服务器也回了个ChangeCipherSpec消息:
[Raw read]: length = 5 0000: 14 03 01 00 01 ..... [Raw read]: length = 1 0000: 01
服务器告知客户端,我和你之后的报文,消息体内容我会加密,你要用我们之间的秘钥解密才能看到明文。
最后,客户端收到服务器发送的Finished消息,这个消息是握手的最后一个消息:[Raw read]: length = 5 0000: 16 03 01 00 30 ....0 [Raw read]: length = 48 0000: 17 96 79 E2 09 64 BE F2 1B 84 B1 72 DE E0 9B D5 ..y..d.....r.... 0010: 70 0C EB 8E 37 17 3F A9 17 8F 41 8B 1A 3E EC 0D p...7.?...A..>.. 0020: A8 C1 34 2F 6E 3D 2C 0E DD B2 61 8E 7A DF E3 7B ..4/n=,...a.z... main, READ: TLSv1 Handshake, length = 48
服务端的Finished消息整理一下:
16 ->消息类型(1字节):握手 0301->版本号(2字节):TLS 1.0 0030->消息体长度(2字节):48字节 179679E20964BEF21B84B172DEE09BD5700CEB8E37173FA9178F418B1A3EEC0DA8C1342F6E3D2C0EDDB2618E7ADFE37B ->Finishd消息体
Finished消息是握手的最后一步,至关重要!握手成不成功,就看Finished消息里面的摘要字符串验证能不能通过,客户端发过来的Finished,服务器会验,服务器发回应的Finished,客户端来验。对方怎么验?咱们接着往下说。
Finished消息是握手结束报文,因为这条消息是在ChangeCipherSpec消息之后发送的,消息体已被对称秘钥加密,服务器或客户端用秘钥解密之后才能得到消息体明文。用客户端Finished消息来举例,Finishd消息体解密之后包含两部分:1、握手消息的摘要,让对方来核实收到的报文有没有被篡改的;2、Finished消息本身的消息消息验证码,对方验证Finished消息本身有没有被篡改的。
这里稍微梳理一下握手过程:
1 客户端:Client Hello,发送了客户端秘钥随机数和加密套件列表
2 服务器:Server Hello,回应了服务器秘钥随机数(与客户端秘钥随机数不同),选择了一个加密套件
Certificate,返回服务器证书
Server Hello Done,说完了
3 客户端:ClientKeyExchange,传送秘钥值(PreMaster)给服务器,并经过服务器证书里面的公钥加密
4 客户端:ChangeCipherSpec,告知服务器,我开始说密文了
5 客户端:Finished,你验一下摘要,看看我们说的话有没有被人篡改
6 服务器:ChangeCipherSpec,告知客户端,我开始说密文了
7 服务器:Finished,你验一下摘要,看看我们说的话有没有被人篡改
到此,握手结束!通过握手,双方确认了一个重要的事情那就是对称秘钥,有了对称秘钥,接下来就开始传输数据咯。
下面这个是客户端发送的经过加密的请求:
然后服务器回应了一个这样的经过加密的应答:[Raw write]: length = 197 0000: 17 03 01 00 C0 D3 1B 8F 27 35 7A B1 0B 43 96 60 ........'5z..C.` 0010: 03 2C ED E5 0D B3 C5 8D 06 E3 54 BA 66 D8 3D 21 .,........T.f.=! 0020: C7 E7 3F 0A 61 19 11 D1 81 04 0F FC 41 99 DB 58 ..?.a.......A..X 0030: CA 82 B2 65 16 C2 29 D6 5B 0B 03 AD 65 D8 23 B5 ...e..).[...e.#. 0040: FE 73 B9 59 2F 21 81 85 73 35 A2 88 FF 0D 22 60 .s.Y/!..s5...."` 0050: 6C 49 49 99 D9 E9 1B D8 1D 1E A0 F6 5B 11 5B 90 lII.........[.[. 0060: CF F6 3B 72 D3 29 7C 16 A8 A9 19 C1 58 0C A7 BC ..;r.)......X... 0070: 3C DA 58 F0 A1 B8 BA 4E D6 58 21 0B 99 3B A9 6E <.X....N.X!..;.n 0080: 69 1A D3 78 F7 6E E6 91 68 1F FD 18 5D A0 62 E3 i..x.n..h...].b. 0090: 85 A5 E2 84 63 F4 AA 32 E9 EB AD 57 0E 00 12 C3 ....c..2...W.... 00A0: 1D A1 88 6D 0F 3B 0B 7C 39 3A B8 24 34 2E 09 AF ...m.;..9:.$4... 00B0: 70 16 72 15 ED AB 2B FA FE 8E CC 33 0E 9B E9 9C p.r...+....3.... 00C0: 6C 2A F1 F2 68 l*..h
可以看得出,这下的消息类型是0x17了,这表明这是一个http应用报文了,只不过报文体经过了加密,我接下来会上一段代码,真实还原https握手过程和http报文加解密以及防篡改校验,其中很多代码是从jsse.jar反编译中截取的,看得可能会费劲。[Raw read]: length = 5 0000: 17 03 01 01 30 ....0 [Raw read]: length = 304 0000: F3 60 41 1E 57 F2 3A 3B FD 4E 76 4D 7F FF 62 BC .`A.W.:;.NvM..b. 0010: F6 8C 18 0C EC 25 F2 A3 E0 E9 FD E1 39 56 42 75 .....%......9VBu 0020: 57 7B AE 75 62 9D 1C 6F 7F FA 89 40 62 F6 9C F9 W..ub..o...@b... 0030: F0 19 61 BC CD F1 5F 7F E8 F4 C2 EF 46 0B A5 9E ..a..._.....F... 0040: 3A CC B3 F9 E1 AF 8B 97 22 C3 20 07 78 41 88 8D :.......". .xA.. 0050: AE 05 9B FF 01 3F 12 AE C1 6B 60 40 B1 A0 A6 C7 .....?...k`@.... 0060: D6 6E EB 99 36 A0 22 15 87 94 D0 B8 9D 7E 2D F2 .n..6.".......-. 0070: 2F DC F2 BC 80 98 C5 97 8A 07 B9 A8 04 AA 4D 99 /.............M. 0080: 5A 53 43 F0 7A 5E A3 CD BA BF 11 8D CB 71 05 AE ZSC.z^.......q.. 0090: 3B 45 6A F4 29 FA 13 66 90 C6 08 14 28 19 E7 F9 ;Ej.)..f....(... 00A0: 32 8A 4C F6 47 40 13 9C 9E F7 58 50 EA 44 68 60 2.L.G@....XP.Dh` 00B0: 48 9E 97 86 F8 D1 32 BD 6E C0 F0 6C B5 96 FF 23 H.....2.n..l...# 00C0: F8 B0 B4 E6 B1 AB 91 57 CD CB 52 7A 01 CC CA 3D .......W..Rz...= 00D0: 7E 0E 7C AD 26 17 8E 08 00 78 A3 0D 69 8A 25 D9 ....&....x..i.%. 00E0: D2 42 F8 7A 0D 5E BD 89 9F 0B 73 CA 78 9D 18 D0 .B.z.^....s.x... 00F0: 51 C9 8A 4B 5A 54 C6 BE 6C C2 D1 B4 8B 19 26 12 Q..KZT..l.....&. 0100: 74 C3 74 EB 11 DD D3 2C 8E D3 10 99 CE D5 57 57 t.t....,......WW 0110: 45 C2 F1 56 ED 2D FB 09 70 80 16 0C 9A 4D BF 85 E..V.-..p....M.. 0120: 23 E6 AA D2 B4 EE 18 21 F0 B3 5F D5 B6 52 91 B8 #......!.._..R..
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.security.Key; import java.security.KeyStore; import java.security.MessageDigest; import java.security.PrivateKey; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import sun.security.internal.spec.TlsKeyMaterialParameterSpec; import sun.security.internal.spec.TlsKeyMaterialSpec; import sun.security.internal.spec.TlsMasterSecretParameterSpec; import sun.security.internal.spec.TlsPrfParameterSpec; public class testSsl3 { public static void main(String[] args) { /** * 我使用keytool生成一个密钥库,里面只有一对非对称密钥信息,别名为:tomcat */ String rasAlias = "tomcat";// 非对称密钥别名 char[] keyStorePassword = "123456".toCharArray();// 密钥库密码 String keyStorePath = "e:/tomcat.keystore";// 密钥库路径 FileInputStream keyStoreFileInput = null; try { // 1 加载密钥库,拿出私钥 keyStoreFileInput = new FileInputStream(keyStorePath); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(keyStoreFileInput, keyStorePassword); PrivateKey privateKey = (PrivateKey) keyStore.getKey(rasAlias, keyStorePassword);// 获取私钥 // 2 非对称解密ClientKeyExchange消息被加密的preMaster String preMasterEncryptStr = "BA2887107E6372064B5E16C853FD1A7A6546DDD14D3E958889F440E45CDF51F72CA25CAD6DA3A1083BF05E0C8864B31DF834677B49F65F670AFB3F541F24AF43DDA6BB4B1E3DD6DC6392756140D23AE0667480D0B6E6317DAACABB8D406B45978081D631A60CDFBF7B60E49EE8D209C02E6814EBB42772FFCC5FF9943EFB90FC";// 客户端ClientKeyExchange消息送给服务器的preMaster byte[] preMasterDecryptBts = rsaUpdate(privateKey, Cipher.DECRYPT_MODE, preMasterEncryptStr);// 客户端ClientKeyExchange消息送给服务器的preMaster是经过服务器证书公钥加密,所以必须用私钥解密,私钥存在于密钥库中 String preMasterDecryptStr = byte2hex(preMasterDecryptBts); System.out.println("preMaster(我解密的):\n" + preMasterDecryptStr); // 3 preMaster+密钥随机数生成对称密钥 /** java6 解密preMaster的过程[begin] **/ Cipher cipher_premaster = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher_premaster.init(Cipher.UNWRAP_MODE, privateKey); SecretKey preMasterKey = ((SecretKey) cipher_premaster.unwrap(hex2byte(preMasterEncryptStr), "TlsRsaPremasterSecret", Cipher.SECRET_KEY)); /** java6 解密preMaster的过程[end] **/ preMasterDecryptStr = byte2hex(preMasterKey.getEncoded()); System.out.println("preMaster(java6方式解密的):\n" + preMasterDecryptStr); byte[] clnt_random = hex2byte("593123361F6CCC7097E124CDFA0C5D275CA94CAF492E230F35004E5194487E99");// 客户端密钥随机数 byte[] svr_random = hex2byte("593123363344927B7FCCCBFB17E0DEBFAD602560F27A2BF286A961C38157A933");// 服务器密钥随机数 TlsMasterSecretParameterSpec localTlsMasterSecretParameterSpec = new TlsMasterSecretParameterSpec(preMasterKey, 3, 1, clnt_random, svr_random); KeyGenerator keyGenerator1 = KeyGenerator.getInstance("SunTlsMasterSecret"); keyGenerator1.init(localTlsMasterSecretParameterSpec); SecretKey masterKey = keyGenerator1.generateKey(); String masterStr = byte2hex(masterKey.getEncoded()); System.out.println("masterKey:\n" + masterStr); TlsKeyMaterialParameterSpec localTlsKeyMaterialParameterSpec = new TlsKeyMaterialParameterSpec(masterKey, 3, 1, clnt_random, svr_random, "AES", 16, 0, 16, 20); KeyGenerator keyGenerator2 = KeyGenerator.getInstance("SunTlsKeyMaterial"); keyGenerator2.init(localTlsKeyMaterialParameterSpec); TlsKeyMaterialSpec localObject1 = (TlsKeyMaterialSpec) keyGenerator2.generateKey(); SecretKey clntWriteKey = ((TlsKeyMaterialSpec) localObject1).getClientCipherKey();// 客户端加解密数据使用的密钥 SecretKey svrWriteKey = ((TlsKeyMaterialSpec) localObject1).getServerCipherKey();// 服务器加解密数据使用的密钥 IvParameterSpec clntWriteIV = ((TlsKeyMaterialSpec) localObject1).getClientIv();// 客户端向量 IvParameterSpec svrWriteIV = ((TlsKeyMaterialSpec) localObject1).getServerIv();// 服务器向量 SecretKey clntMacSecret = ((TlsKeyMaterialSpec) localObject1).getClientMacKey();// 客户端生成消息验证码密钥 SecretKey svrMacSecret = ((TlsKeyMaterialSpec) localObject1).getServerMacKey();// 服务器生成消息验证码密钥 System.out.println("clntWriteKey:\n" + byte2hex(clntWriteKey.getEncoded())); System.out.println("clntWriteIV:\n" + byte2hex(clntWriteIV.getIV())); System.out.println("clntMacSecret:\n" + byte2hex(clntMacSecret.getEncoded())); System.out.println("svrWriteKey:\n" + byte2hex(svrWriteKey.getEncoded())); System.out.println("svrWriteIV:\n" + byte2hex(svrWriteIV.getIV())); System.out.println("svrMacSecret:\n" + byte2hex(svrMacSecret.getEncoded())); // 4 对称解密客户端Finished消息 Cipher cipher_client = Cipher.getInstance("AES/CBC/NoPadding"); cipher_client.init(Cipher.DECRYPT_MODE, clntWriteKey, clntWriteIV); String clientFinishedEncryptStr = "16030100302E8CA5C391B350411F0B9B24517AE0366E105F0A561A47E4AD23B91DA32A3D021598834DD0898654B8B5258695B15D20"; byte[] clientFinishedEncryptBts = hex2byte(clientFinishedEncryptStr); cipher_client.update(clientFinishedEncryptBts, 5, clientFinishedEncryptBts.length - 5, clientFinishedEncryptBts, 5);// 只解密消息体 String clientFinishedDecryptStr = byte2hex(clientFinishedEncryptBts); System.out.println("client finished decrypt:\n" + clientFinishedDecryptStr); int clientFinishedPaddingLength = Integer.valueOf(clientFinishedDecryptStr.substring(clientFinishedDecryptStr.length() -1), 16) + 1;// Finished填充的字节数 String clientFinishedStr = clientFinishedDecryptStr.substring(10, clientFinishedDecryptStr.length() - clientFinishedPaddingLength * 2);// 截出Finished System.out.println("client finished:\n" + clientFinishedStr); int clientFinishedLength = Integer.valueOf(clientFinishedStr.substring(2, 8), 16);// Finished长度:去掉1字节的0x14,后面3字节为长度 String clientHandShakeMacStr = clientFinishedStr.substring(8, 8 + clientFinishedLength*2);// 客户端握手消息验证码 String clientFinishedMacStr = clientFinishedStr.substring(8 + clientFinishedLength*2);// Finished消息验证码 System.out.println("client handshake mac(报文里的):\n" + clientHandShakeMacStr); System.out.println("client finished mac(报文里的):\n" + clientFinishedMacStr); Mac clientMac = Mac.getInstance("HmacSHA1"); clientMac.init(clntMacSecret); clientMac.update(hex2byte("00000000000000001603010010"));// 第8字节是计算次数,第9字节是消息类型,第10~11字节是版本号,第12~13字节是数据长度:即clientFinishedLength clientMac.update(hex2byte(clientFinishedStr.substring(0, 8 + clientFinishedLength*2))); byte[] clientFinishedMacBts = clientMac.doFinal(); String clientFinishedMacStr2 = byte2hex(clientFinishedMacBts); System.out.println("client finished mac(我计算的):\n" + clientFinishedMacStr2);// 服务器验证客户端发过来的finished报文有没有被篡改,过程就是根据Finished原文计算出clinet finished mac,比较与Finished报文末尾的client finished mac是否一致 // 5 验证客户端握手消息验证码 String clientStr = "client finished"; StringBuilder clientSb = new StringBuilder(); /** Client Hello **/ clientSb.append("010000470301593123361F6CCC7097E124CDFA0C5D275CA94CAF492E230F35004E5194487E9900002000040005002F00330032000A00160013000900150012000300080014001100FF0100"); /** Server Hello **/ clientSb.append("020000460301593123363344927B7FCCCBFB17E0DEBFAD602560F27A2BF286A961C38157A93320593123367275A3D976143020DE265E229AA917CC25371582FABDBA6E0877C1EE002F00"); /** Certificate **/ clientSb.append("0B00026300026000025D30820259308201C2A0030201020204589D624B300D06092A864886F70D01010505003071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E31301E170D3137303231303036343834335A170D3137303531313036343834335A3071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E3130819F300D06092A864886F70D010101050003818D0030818902818100C24E1F327BF76543BB8CA670A5C68607C6B91CF074BFEBCE44A41D54A8736F8C4D6EC8898F7533C506457FF6EA9FF6401527CD1F2B34D03FEFDFF7A782E75A0D4601312E471ED23205E3DE154E9679C24AF38343E70DB08BEAD186817356DC5648B0700418725BA7D5ABB732B35EDF33299BB82928F5E4AB4B0313D31A2CF2ED0203010001300D06092A864886F70D0101050500038181000EACA0C952D45084A342C399922887170B2BE361F154AE6516890DB77342D04B2DD6CE5E2C7145B6433EA909AA484D9B10AF89E6EFC21992C1886DF5566412BC0E3DD6B0D2177D4FD4CFA34363654DAA7DC4CD7CFA4433ACF810006B077B6CBAC66F8184A0D40CF9A00F8690776DCED47996B6B5011E66219A45E2DAF029E662"); /** Server Hello Done **/ clientSb.append("0E000000"); /** Client Key Exchange **/ clientSb.append("100000820080BA2887107E6372064B5E16C853FD1A7A6546DDD14D3E958889F440E45CDF51F72CA25CAD6DA3A1083BF05E0C8864B31DF834677B49F65F670AFB3F541F24AF43DDA6BB4B1E3DD6DC6392756140D23AE0667480D0B6E6317DAACABB8D406B45978081D631A60CDFBF7B60E49EE8D209C02E6814EBB42772FFCC5FF9943EFB90FC"); MessageDigest clientMd5 = MessageDigest.getInstance("MD5"); MessageDigest clientSha = MessageDigest.getInstance("SHA"); clientMd5.update(hex2byte(clientSb.toString())); clientSha.update(hex2byte(clientSb.toString())); byte[] arrayOfByteClient = new byte[36]; clientMd5.digest(arrayOfByteClient, 0, 16); clientSha.digest(arrayOfByteClient, 16, 20); TlsPrfParameterSpec tlsPrfParameterSpecClient = new TlsPrfParameterSpec(masterKey, clientStr, arrayOfByteClient, 12); KeyGenerator keyGeneratorClient = KeyGenerator.getInstance("SunTlsPrf"); keyGeneratorClient.init(tlsPrfParameterSpecClient); SecretKey secretKeyClient = keyGeneratorClient.generateKey(); byte[] clientHandShakeMacBts = secretKeyClient.getEncoded(); String clientHandShakeMacStr2 = byte2hex(clientHandShakeMacBts); System.out.println("client handshake mac(我计算的):\n" + clientHandShakeMacStr2);// 服务器验证握手消息有没有篡改,过程就是客户端Finished消息和之前握手类型消息明文,按顺序拼接计算出client handshake mac,比较与客户端Finished消息内的client handshake mac是否一致 // 6 对称解密服务器Finished消息 Cipher cipher_server = Cipher.getInstance("AES/CBC/NoPadding"); cipher_server.init(Cipher.DECRYPT_MODE, svrWriteKey, svrWriteIV); String serverFinishedEncryptStr = "1603010030179679E20964BEF21B84B172DEE09BD5700CEB8E37173FA9178F418B1A3EEC0DA8C1342F6E3D2C0EDDB2618E7ADFE37B"; byte[] serverFinishedEncryptBts = hex2byte(serverFinishedEncryptStr); cipher_server.update(serverFinishedEncryptBts, 5, serverFinishedEncryptBts.length - 5, serverFinishedEncryptBts, 5);// 只解密消息体 String serverFinishedDecryptStr = byte2hex(serverFinishedEncryptBts); System.out.println("server finished decrypt:\n" + serverFinishedDecryptStr); int serverFinishedPaddingLength = Integer.valueOf(serverFinishedDecryptStr.substring(serverFinishedDecryptStr.length() -1), 16) + 1;// Finished填充的字节数 String serverFinishedStr = serverFinishedDecryptStr.substring(10, serverFinishedDecryptStr.length() - serverFinishedPaddingLength * 2);// 截出Finished System.out.println("server finished:\n" + serverFinishedStr); int serverFinishedLength = Integer.valueOf(serverFinishedStr.substring(2, 8), 16);// Finished长度:去掉1字节的0x14,后面3字节为长度 String serverHandShakeMacStr = serverFinishedStr.substring(8, 8 + serverFinishedLength*2);// 服务端握手消息验证码 String serverFinishedMacStr = serverFinishedStr.substring(8 + serverFinishedLength*2);// Finished消息验证码 System.out.println("server handshake mac(报文里的):\n" + serverHandShakeMacStr); System.out.println("server finished mac(报文里的):\n" + serverFinishedMacStr); Mac serverMac = Mac.getInstance("HmacSHA1"); serverMac.init(svrMacSecret); serverMac.update(hex2byte("00000000000000001603010010"));// 第8字节是计算次数,第9字节是消息类型,第10~11字节是版本号,第12~13字节是数据长度:即serverFinishedLength serverMac.update(hex2byte(serverFinishedStr.substring(0, 8 + serverFinishedLength*2))); byte[] finishedMacBts = serverMac.doFinal(); String finishedMacStr2 = byte2hex(finishedMacBts); System.out.println("server finished mac(我计算的):\n" + finishedMacStr2);// 客户端验证服务器发过来的finished报文有没有被篡改,过程就是根据Finished原文计算出server finished mac,比较与Finished报文末尾的里面的server finished mac是否一致 // 7 验证服务器握手消息验证码 String serverStr = "server finished"; StringBuilder serverSb = new StringBuilder(); /** Client Hello **/ serverSb.append("010000470301593123361F6CCC7097E124CDFA0C5D275CA94CAF492E230F35004E5194487E9900002000040005002F00330032000A00160013000900150012000300080014001100FF0100"); /** Server Hello **/ serverSb.append("020000460301593123363344927B7FCCCBFB17E0DEBFAD602560F27A2BF286A961C38157A93320593123367275A3D976143020DE265E229AA917CC25371582FABDBA6E0877C1EE002F00"); /** Certificate **/ serverSb.append("0B00026300026000025D30820259308201C2A0030201020204589D624B300D06092A864886F70D01010505003071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E31301E170D3137303231303036343834335A170D3137303531313036343834335A3071310B300906035504061302434E31123010060355040813093132372E302E302E3131123010060355040713093132372E302E302E3131123010060355040A13093132372E302E302E3131123010060355040B13093132372E302E302E3131123010060355040313093132372E302E302E3130819F300D06092A864886F70D010101050003818D0030818902818100C24E1F327BF76543BB8CA670A5C68607C6B91CF074BFEBCE44A41D54A8736F8C4D6EC8898F7533C506457FF6EA9FF6401527CD1F2B34D03FEFDFF7A782E75A0D4601312E471ED23205E3DE154E9679C24AF38343E70DB08BEAD186817356DC5648B0700418725BA7D5ABB732B35EDF33299BB82928F5E4AB4B0313D31A2CF2ED0203010001300D06092A864886F70D0101050500038181000EACA0C952D45084A342C399922887170B2BE361F154AE6516890DB77342D04B2DD6CE5E2C7145B6433EA909AA484D9B10AF89E6EFC21992C1886DF5566412BC0E3DD6B0D2177D4FD4CFA34363654DAA7DC4CD7CFA4433ACF810006B077B6CBAC66F8184A0D40CF9A00F8690776DCED47996B6B5011E66219A45E2DAF029E662"); /** Server Hello Done **/ serverSb.append("0E000000"); /** Client Key Exchange **/ serverSb.append("100000820080BA2887107E6372064B5E16C853FD1A7A6546DDD14D3E958889F440E45CDF51F72CA25CAD6DA3A1083BF05E0C8864B31DF834677B49F65F670AFB3F541F24AF43DDA6BB4B1E3DD6DC6392756140D23AE0667480D0B6E6317DAACABB8D406B45978081D631A60CDFBF7B60E49EE8D209C02E6814EBB42772FFCC5FF9943EFB90FC"); /** Client Finished **/ serverSb.append("1400000CE983C22990366CD0A35E41B3"); MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha = MessageDigest.getInstance("SHA"); md5.update(hex2byte(serverSb.toString())); sha.update(hex2byte(serverSb.toString())); byte[] arrayOfByteServer = new byte[36]; md5.digest(arrayOfByteServer, 0, 16); sha.digest(arrayOfByteServer, 16, 20); TlsPrfParameterSpec tlsPrfParameterSpecServer = new TlsPrfParameterSpec(masterKey, serverStr, arrayOfByteServer, 12); KeyGenerator keyGeneratorServer = KeyGenerator.getInstance("SunTlsPrf"); keyGeneratorServer.init(tlsPrfParameterSpecServer); SecretKey secretKeyServer = keyGeneratorServer.generateKey(); byte[] serverHandShakeMacBts = secretKeyServer.getEncoded(); String serverHandShakeMacStr2 = byte2hex(serverHandShakeMacBts); System.out.println("server handshake mac(我计算的):\n" + serverHandShakeMacStr2);// 客户端验证握手消息有没有篡改,过程就是服务器Finished消息和之前握手类型消息明文,按顺序拼接计算出server handshake mac,比较与服务器Finished内的server handshake mac是否一致 // 8解密http请求内容,验证请求mac String httpRequestEncryptStr = "17030100C0D31B8F27357AB10B439660032CEDE50DB3C58D06E354BA66D83D21C7E73F0A611911D181040FFC4199DB58CA82B26516C229D65B0B03AD65D823B5FE73B9592F2181857335A288FF0D22606C494999D9E91BD81D1EA0F65B115B90CFF63B72D3297C16A8A919C1580CA7BC3CDA58F0A1B8BA4ED658210B993BA96E691AD378F76EE691681FFD185DA062E385A5E28463F4AA32E9EBAD570E0012C31DA1886D0F3B0B7C393AB824342E09AF70167215EDAB2BFAFE8ECC330E9BE99C6C2AF1F268"; byte[] httpRequestEncryptBts = hex2byte(httpRequestEncryptStr); cipher_client.update(httpRequestEncryptBts, 5, httpRequestEncryptBts.length - 5, httpRequestEncryptBts, 5); int requestPaddingLen = httpRequestEncryptBts[httpRequestEncryptBts.length - 1] + 1;// 填充字节数 byte[] requestMacBts = new byte[20]; /** https报文头5字节 ,20字节消息鉴别码 ,还有末尾的填充字节,掐头去尾剩下中间的就是被加密的http报文 **/ System.arraycopy(httpRequestEncryptBts, httpRequestEncryptBts.length - requestPaddingLen - 20, requestMacBts, 0, requestMacBts.length); byte[] requestMacSrcBts = new byte[httpRequestEncryptBts.length - requestPaddingLen - 5 - 20]; System.arraycopy(httpRequestEncryptBts, 5, requestMacSrcBts, 0, requestMacSrcBts.length); clientMac.update(hex2byte("000000000000000117030100A7"));// 第8字节是计算次数,第9字节是消息类型,第10~11字节是版本号,第12~13字节是数据长度:即requestMacSrcBts长度 clientMac.update(requestMacSrcBts); byte[] requestMacBts2 = clientMac.doFinal(); System.out.println("http请求解密(十六进制):" + byte2hex(httpRequestEncryptBts)); System.out.println("http请求字节填充数:" + requestPaddingLen); System.out.println("http请求mac:" + byte2hex(requestMacBts)); System.out.println("http请求计算mac数据源:" + byte2hex(requestMacSrcBts)); System.out.println("http请求计算mac结果:" + byte2hex(requestMacBts2)); System.out.println("http请求解密(文本):\n" + new String(httpRequestEncryptBts, 5, httpRequestEncryptBts.length - requestPaddingLen - 5 - 20, "ISO-8859-1")); // 9解密http响应内容,验证响应mac String httpResponseEncryptStr = "1703010130F360411E57F23A3BFD4E764D7FFF62BCF68C180CEC25F2A3E0E9FDE139564275577BAE75629D1C6F7FFA894062F69CF9F01961BCCDF15F7FE8F4C2EF460BA59E3ACCB3F9E1AF8B9722C320077841888DAE059BFF013F12AEC16B6040B1A0A6C7D66EEB9936A022158794D0B89D7E2DF22FDCF2BC8098C5978A07B9A804AA4D995A5343F07A5EA3CDBABF118DCB7105AE3B456AF429FA136690C608142819E7F9328A4CF64740139C9EF75850EA446860489E9786F8D132BD6EC0F06CB596FF23F8B0B4E6B1AB9157CDCB527A01CCCA3D7E0E7CAD26178E080078A30D698A25D9D242F87A0D5EBD899F0B73CA789D18D051C98A4B5A54C6BE6CC2D1B48B19261274C374EB11DDD32C8ED31099CED5575745C2F156ED2DFB097080160C9A4DBF8523E6AAD2B4EE1821F0B35FD5B65291B8"; byte[] httpResponseEncryptBts = hex2byte(httpResponseEncryptStr); cipher_server.update(httpResponseEncryptBts, 5, httpResponseEncryptBts.length - 5, httpResponseEncryptBts, 5); int responsePaddingLen = httpResponseEncryptBts[httpResponseEncryptBts.length - 1] + 1;// 填充字节数 byte[] responseMacBts = new byte[20]; System.arraycopy(httpResponseEncryptBts, httpResponseEncryptBts.length - responsePaddingLen - 20, responseMacBts, 0, responseMacBts.length); byte[] responseMacSrcBts = new byte[httpResponseEncryptBts.length - responsePaddingLen - 5 - 20]; System.arraycopy(httpResponseEncryptBts, 5, responseMacSrcBts, 0, responseMacSrcBts.length); serverMac.update(hex2byte("0000000000000001170301011A"));// 第8字节是计算次数,第9字节是消息类型,第10~11字节是版本号,第12~13字节是数据长度:即responseMacSrcBts长度 serverMac.update(responseMacSrcBts); byte[] responseMacBts2 = serverMac.doFinal(); System.out.println("http响应解密(十六进制):" + byte2hex(httpResponseEncryptBts)); System.out.println("http响应字节填充数:" + responsePaddingLen); System.out.println("http响应mac:" + byte2hex(responseMacBts)); System.out.println("http响应计算mac数据源:" + byte2hex(responseMacSrcBts)); System.out.println("http响应计算mac结果:" + byte2hex(responseMacBts2)); System.out.println("http响应解密(文本):\n" + new String(httpResponseEncryptBts, 5, httpResponseEncryptBts.length - responsePaddingLen - 5 - 20, "ISO-8859-1")); } catch (Exception e) { e.printStackTrace(); } } /** * RSA加解密 * @param key 密钥 * @param opmode 操作模式:加密/解密 * @param data 数据 * @return */ public static byte[] rsaUpdate(Key key, int opmode, String data) { try { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(opmode, key); return cipher.doFinal(hex2byte(data)); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 十六进制字符串转字节数组 * @param hex 十六进制字符串 * @return */ public static byte[] hex2byte(String hex){ int length = hex.length(); if (length%2 != 0){ throw new RuntimeException("长度错误"); } ByteArrayOutputStream bo = new ByteArrayOutputStream(); for (int i = 0; i < length; i += 2){ int v = Integer.valueOf(hex.substring(i, i+2), 16); bo.write(v); } return bo.toByteArray(); } /** * 字节数组转16进制字符串 * @param bytes * @param start_index * @param length * @return */ public static String byte2hex(byte[] bytes, int start_index, int length){ if (bytes == null){ return null; } StringBuilder sb = new StringBuilder(); for (byte b : bytes){ if (--start_index >= 0){ continue; } if (--length < 0){ break; } String hex = Integer.toHexString(b & 0xff); if (hex.length() == 1){ hex = "0" + hex; } sb.append(hex); } return sb.toString().toUpperCase(); } /** * 字节数组转16进制字符串 * @param bytes * @param start_index * @param length * @return */ public static String byte2hex(byte[] bytes){ return byte2hex(bytes, 0, bytes.length); } }
console输出如下:
preMaster(我解密的): 0301A423A5A3FC070A11DCA8259B7D469909C5E37AD34CD9567473213B4AE072A3D7EC4A365DFB1BF18A0459365FB358 preMaster(java6方式解密的): 0301A423A5A3FC070A11DCA8259B7D469909C5E37AD34CD9567473213B4AE072A3D7EC4A365DFB1BF18A0459365FB358 masterKey: ADCD2A11D8BB3F26B4C6DDA62053C4BDD64B5EA79EE86EB4E257F04D234F1567A14A7162C871B919D3A073C229F86AF4 clntWriteKey: 97CCEB77738E5CBE81D3EA7C5A02A447 clntWriteIV: 2FED03D9D60E03A86C1280BDD487F347 clntMacSecret: D8513F838891D9A0F5E1A04970CD04B4006E34F4 svrWriteKey: BC6E8027E3B15062AF1B44BF801F1398 svrWriteIV: 4AAF38831283AC3322097820479E42EF svrMacSecret: FE8241F0661862654C9609F5BDD6DBC86BE72674 client finished decrypt: 16030100301400000CE983C22990366CD0A35E41B36B32481B52B5CF31909A31871F5085768D4359FB0B0B0B0B0B0B0B0B0B0B0B0B client finished: 1400000CE983C22990366CD0A35E41B36B32481B52B5CF31909A31871F5085768D4359FB client handshake mac(报文里的): E983C22990366CD0A35E41B3 client finished mac(报文里的): 6B32481B52B5CF31909A31871F5085768D4359FB client finished mac(我计算的): 6B32481B52B5CF31909A31871F5085768D4359FB client handshake mac(我计算的): E983C22990366CD0A35E41B3 server finished decrypt: 16030100301400000C70774150DE43C5D2EF19F7E0DE1D1F0D4A4B4698290586CB5FFF446AC79F0E9A0B0B0B0B0B0B0B0B0B0B0B0B server finished: 1400000C70774150DE43C5D2EF19F7E0DE1D1F0D4A4B4698290586CB5FFF446AC79F0E9A server handshake mac(报文里的): 70774150DE43C5D2EF19F7E0 server finished mac(报文里的): DE1D1F0D4A4B4698290586CB5FFF446AC79F0E9A server finished mac(我计算的): DE1D1F0D4A4B4698290586CB5FFF446AC79F0E9A server handshake mac(我计算的): 70774150DE43C5D2EF19F7E0 http请求解密(十六进制):17030100C0474554202F746573742F696E6465782E6A737020485454502F312E310D0A557365722D4167656E743A204A6176612F312E362E305F32370D0A486F73743A203132372E302E302E313A383434330D0A4163636570743A20746578742F68746D6C2C20696D6167652F6769662C20696D6167652F6A7065672C202A3B20713D2E322C202A2F2A3B20713D2E320D0A436F6E6E656374696F6E3A206B6565702D616C6976650D0A0D0A4DFCB522DBB0EBA964958B5E79A6A21E15CE4EC70404040404 http请求字节填充数:5 http请求mac:4DFCB522DBB0EBA964958B5E79A6A21E15CE4EC7 http请求计算mac数据源:474554202F746573742F696E6465782E6A737020485454502F312E310D0A557365722D4167656E743A204A6176612F312E362E305F32370D0A486F73743A203132372E302E302E313A383434330D0A4163636570743A20746578742F68746D6C2C20696D6167652F6769662C20696D6167652F6A7065672C202A3B20713D2E322C202A2F2A3B20713D2E320D0A436F6E6E656374696F6E3A206B6565702D616C6976650D0A0D0A http请求计算mac结果:4DFCB522DBB0EBA964958B5E79A6A21E15CE4EC7 http请求解密(文本): GET /test/index.jsp HTTP/1.1 User-Agent: Java/1.6.0_27 Host: 127.0.0.1:8443 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive http响应解密(十六进制):1703010130485454502F312E3120323030204F4B0D0A5365727665723A204170616368652D436F796F74652F312E310D0A5365742D436F6F6B69653A204A53455353494F4E49443D45343533443138394237413430354645304536374344303330463130433532343B20506174683D2F746573743B205365637572650D0A436F6E74656E742D547970653A20746578742F68746D6C0D0A436F6E74656E742D4C656E6774683A2037370D0A446174653A204672692C203032204A756E20323031372030383A33353A303220474D540D0A0D0A3C68746D6C3E0A3C686561643E0A3C7469746C653E746573743C2F7469746C653E0A3C2F686561643E0A3C626F64793E0A48656C6C6F20576F726C64210A3C2F626F64793E0A3C2F68746D6C3E4358380EF3D9A0D11C69FD77082A45D86C25FCFA0101 http响应字节填充数:2 http响应mac:4358380EF3D9A0D11C69FD77082A45D86C25FCFA http响应计算mac数据源:485454502F312E3120323030204F4B0D0A5365727665723A204170616368652D436F796F74652F312E310D0A5365742D436F6F6B69653A204A53455353494F4E49443D45343533443138394237413430354645304536374344303330463130433532343B20506174683D2F746573743B205365637572650D0A436F6E74656E742D547970653A20746578742F68746D6C0D0A436F6E74656E742D4C656E6774683A2037370D0A446174653A204672692C203032204A756E20323031372030383A33353A303220474D540D0A0D0A3C68746D6C3E0A3C686561643E0A3C7469746C653E746573743C2F7469746C653E0A3C2F686561643E0A3C626F64793E0A48656C6C6F20576F726C64210A3C2F626F64793E0A3C2F68746D6C3E http响应计算mac结果:4358380EF3D9A0D11C69FD77082A45D86C25FCFA http响应解密(文本): HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=E453D189B7A405FE0E67CD030F10C524; Path=/test; Secure Content-Type: text/html Content-Length: 77 Date: Fri, 02 Jun 2017 08:35:02 GMT <html> <head> <title>test</title> </head> <body> Hello World! </body> </html>
https生成对称秘钥过程,是由客户端生成一个preMaster,通过服务器公钥加密,传输给服务器,通过preMaster和密钥随机数生成masterKey,再有masterKey分散出6个对称相关数据:客户端、服务器加密数据密钥,客户端、服务器计算消息验证码密钥,客户端、服务器向量。客户端的加密数据密钥和客户端向量,用来加密客户端数据,服务器用这两个数据解密客户端密文。服务器的加密数据密钥和服务器向量,用来加密服务器数据,客户端用这两个数据解密服务器密文。握手消息类型消息验证码:握手消息按顺序拼接,先摘要计算,再使用计算消息验证码密钥加密生成密文,存在Finished消息体中。Finished消息和Application消息(消息类型=0x17)的消息验证码:消息体使用计算消息验证码密钥加密生成的密文。
java6实现https大致如我上面贴的代码,有很多地方我没有做出很详细的说明,完全是由于篇幅和时间原因。因为本人完全没有阅读过相关规范文档,所有的东西是从网上来了解,代码也是看的反编译,所以定义了一些自己认为好理解的词汇,还请谅解。
大家可以通过评论与我交流。