ECDHE_RSA_WITH_AES_128_CBC_SHA256模式下, RSA只用于身份验证, 不用于加密.
加密密钥是通过有限域的椭圆曲线
算法交换的, 需要拿到ECDH的私钥才能解密
本文的demo样本使用了特殊方法来获取这些参数.
椭圆曲线加密原理 https://blog.csdn.net/wzj_whut/article/details/86649809#_68
已经准备好的抓包数据
https://github.com/wzjwhut/tlsv12-demo/blob/master/src/main/resources/TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256.pcapng
可以通过以下方式自己抓包实验
修改crypto/dh/ec_key.c
加入一段日志函数
/** 用于打印一段数据内容 */
static const char hexdig[] = "0123456789abcdef";
static void log_hex(const char* tag, unsigned char* data, int len){
char msg[50], *ptr;
int i;
ptr = msg;
printf("%s\r\n", tag);
for(i=0; i> 4)];
*ptr++ = hexdig[0x0f & data[i]];
if ((i & 0x0f) == 0x0f) {
*ptr = '\0';
ptr = msg;
printf("%s\r\n", msg);
} else {
*ptr++ = ' ';
}
}
if (i & 0x0f) {
*ptr = '\0';
printf("%s\r\n", msg);
}
}
修改int EC_KEY_generate_key(EC_KEY *eckey)
函数, 将参数打印出来
/** log */
unsigned char 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);
err:
然后编译
./config
make
cd apps
tcpdump -v -w ./my.tcpdump
连接服务器
./openssl s_client -debug -connect x.x.x.x:443 -cipher ECDHE-RSA-AES128-SHA256
然后随便输入点啥
这里修改成自己的IP
然后使用wireshark打开my.tcpdump, 导出相关的参数
本文中, 通过修改openssl, 拿到了client的ECDH私钥, 再加上server的ECDH公钥, 就可以计算出pre-master.
计算公式为
pre-master = (Point(服务端ECDH公钥) * 客户端户端私钥
).x
抓包数据中, Server Key Exchange显示
Named Curve: secp256r1 (0x0017) //secp256r1是官方已经定义好的曲线方程
Pubkey: 043d0f6c38358e0b5b1e3b2c2b0ed5b71df58dd351f82d80...
//第1个字节04表示后面的数据没有压缩过. 此时, 随后的32字节为x坐标, 最后32字节为y坐标
Client Key Exchange类似
client私钥为
static BigInteger clientPrivateKey = new BigInteger(HexUtils.fromHexString(
"00" +
"61 b4 7c 58 92 26 fc 4e aa 82 8d ee 86 d3 c5 33\n" +
"65 a6 aa c0 ae db c5 e3 07 ef 29 3e 22 fd c1 6a"));
server公钥为
static BigInteger serverPublicKeyX = new BigInteger(HexUtils.fromHexString(
"3d 0f 6c 38 35 8e 0b 5b 1e 3b 2c 2b 0e d5 b7\n" +
"1d f5 8d d3 51 f8 2d 80 b8 f4 4c b9 12 5d 33 36\n" +
"26 "));
static BigInteger serverPublicKeyY = new BigInteger(HexUtils.fromHexString(
"00" +
"fb b1 d5 b9 55 ce 8c 3c 34 f0 7b 9a 48 2c 5a\n" +
"3c 89 4b dd 04 56 63 08 71 34 a5 7a 83 a7 83 1e\n" +
"2b"));
执行有限域椭圆曲线
中的乘法, 最终的Point的X坐标就是pre-master
public static byte[] multi(BigInteger pointX, BigInteger pointY, BigInteger k){
ECPoint point = bcCurve.createPoint(pointX, pointY, false);
point = point.multiply(k);
return point.getX().toBigInteger().toByteArray();
}
public static byte[] computePreMaster() throws Exception{
/** 方式1. jdk有现成的系统接口 */
byte[] sharedKey = CipherUtil.ecdhShareKey(clientPrivateKey, serverPublicKeyX, serverPublicKeyY);
logger.info("system shared key: \r\n{}", HexUtils.dumpString(sharedKey, 16));
/** 方式2. 使用开源库计算. 使用对方的Point乘上自己的私钥 */
byte[] myKey = ECCP256R1l.multi(serverPublicKeyX, serverPublicKeyY, clientPrivateKey);
logger.info("my shared key: \r\n{}", HexUtils.dumpString(myKey, 16));
return myKey;
}
通过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_ECDHE_RSA_WITH_AES_128_CBC_SHA256.java