DHE_RSA_WITH_AES_128_CBC_SHA256模式下, RSA只用于身份验证, 不用于加密. 加密密钥是通过DH算法交换的. 因此需要DH相关的参数才能解密. 本文的demo样本使用了特殊方法来获取这些参数.
准备工作
下载和修改最新版的openssl源码
修改crypto/dh/dh_key.c
加入一段日志函数
/** 用于打印一段数据内容 */
static const char hexdig[] = "0123456789abcdef";
void log_hex(const char* tag, unsigned char* data, int len){
char msg[50], *ptr;
int i;
ptr = msg;
for(i=0; i<len; i++) {
*ptr++ = hexdig[0x0f & (data[i] >> 4)];
*ptr++ = hexdig[0x0f & data[i]];
if ((i & 0x0f) == 0x0f) {
*ptr = '\0';
ptr = msg;
printf("%s: %s\r\n", tag, msg);
} else {
*ptr++ = ' ';
}
}
if (i & 0x0f) {
*ptr = '\0';
printf("%s: %s\r\n",tag, msg);
}
}
修改static int generate_key(DH *dh)
函数, 将DH参数打印出来
/** log */
unsigned char temp[1024];
memset(temp, 0, 1024);
BN_bn2bin(pub_key, temp);
printf("pub key, bits: %d\r\n", BN_num_bits(pub_key));
log_hex("pub key: ", temp, 1024);
memset(temp, 0, 1024);
BN_bn2bin(priv_key, temp);
printf("priv key, bits: %d\r\n", BN_num_bits(priv_key));
log_hex("priv key: ", temp, 1024);
memset(temp, 0, 1024);
BN_bn2bin(dh->g, temp);
printf("dh->g, bits: %d\r\n", BN_num_bits(dh->g));
log_hex("dh->g: ", temp, 1024);
memset(temp, 0, 1024);
BN_bn2bin(dh->p, temp);
printf("dh->p, bits: %d\r\n", BN_num_bits(dh->p));
log_hex("dh->p: ", temp, 1024);
dh->pub_key = pub_key;
dh->priv_key = priv_key;
ok = 1;
err:
然后编译
./config
make
cd apps
抓包
tcpdump -v -w ./my.tcpdump
连接服务器
./openssl s_client -debug -connect x.x.x.x:443 -cipher DHE-RSA-AES128-SHA256
这里修改成自己的IP
然后使用wireshark打开my.tcpdump, 导出相关的参数
开始解析
本文中, 通过修改openssl, 拿到了client的DH私钥, 再加上server的DH公钥, 就可以计算出pre-master.
计算公式为
服务端DH公钥
客户端私钥 mod p
public static final BigInteger DH_P = new BigInteger(HexUtils.fromHexString(
"00 " + //为了保证BigInteger按照正数解析, 第1个字节必须为0
"ff ff ff ff ff ff ff ff c9 0f da a2 21 68 c2 34\n" +
"c4 c6 62 8b 80 dc 1c d1 29 02 4e 08 8a 67 cc 74\n" +
"02 0b be a6 3b 13 9b 22 51 4a 08 79 8e 34 04 dd\n" +
"ef 95 19 b3 cd 3a 43 1b 30 2b 0a 6d f2 5f 14 37\n" +
"4f e1 35 6d 6d 51 c2 45 e4 85 b5 76 62 5e 7e c6\n" +
"f4 4c 42 e9 a6 37 ed 6b 0b ff 5c b6 f4 06 b7 ed\n" +
"ee 38 6b fb 5a 89 9f a5 ae 9f 24 11 7c 4b 1f e6\n" +
"49 28 66 51 ec e6 53 81 ff ff ff ff ff ff ff ff"));
/** PUBKEY = g^privkey mod p */
public static final BigInteger DH_SERVER_PUBKEY = new BigInteger(HexUtils.fromHexString(
"78 8d 66 69 7b bf c9 01 f8 2c f0 02 cc 5b 70 cf\n" +
"af 53 4e 65 26 19 16 48 21 7d 43 50 2f af a1 8c\n" +
"e8 c9 7c c6 52 f9 a9 fc f9 8d 57 35 e9 c2 d6 41\n" +
"75 4d 96 15 fa ae 3e 90 b5 47 96 1c 7e e9 10 46\n" +
"d7 25 73 f7 c6 f2 7b c0 10 3f 76 ab 5c c5 fd 65\n" +
"ec d0 8f 36 c9 28 66 3f 64 78 84 9c 5a 16 17 8e\n" +
"78 f5 30 d1 11 f9 d3 19 fa f8 83 2e 97 50 f7 d4\n" +
"7a 70 b2 10 c2 db 45 73 e6 ef 8a 1c 27 a2 73 86"));
public static final BigInteger DH_CLIENT_PRIVKEY = new BigInteger(HexUtils.fromHexString(
"57 99 8b f0 c8 9b bd 7f 42 3a 57 c8 e6 ad 10 80\n" +
"ad 5e 25 7f e9 a3 c0 ea cc 28 21 35 bb f5 88 69\n" +
"d0 9c 03 0e a6 d3 9a 5d 93 5a 6b ff 0e aa 93 91\n" +
"a2 93 f9 5d fd dd a9 fa 26 e9 4a cd b3 17 b9 ab\n" +
"f9 b8 72 38 90 3c 0e 50 b2 b4 4a 40 61 dd 45 64\n" +
"f9 d2 cc d3 26 b3 e3 5c ac 02 0d 31 91 2a f5 46\n" +
"e3 58 70 6b 62 68 a3 be 93 7d 41 1b 1b a9 73 35\n" +
"1b 52 60 3d f8 d1 45 94 3c ff 76 bf a1 9a 07 7d"));
/** 使用DH算法, 计算出pre master */
public static byte[] computePreMaster(){
byte[] preMaster = DH_SERVER_PUBKEY.modPow(DH_CLIENT_PRIVKEY, DH_P).toByteArray();
logger.info("premaster:\r\n{}", HexUtils.dumpString(preMaster, 16));
return preMaster;
}
通过pre master, 可以计算出master secret, 进而计算出各端的加密私钥. 参照本人写的
https://blog.csdn.net/wzj_whut/article/details/86626529#Master_Secret_106
代码参考
https://github.com/wzjwhut/tlsv12-demo/blob/master/src/main/java/com/wzjwhut/example/Analyse_DHE_RSA_WITH_AES_128_CBC_SHA256.java