TLSv1.2抓包解密分析过程之ECDHE_RSA_WITH_AES_128_CBC_SHA256

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
可以通过以下方式自己抓包实验

下载和修改最新版的openssl源码

修改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<len; i++) {
        *ptr++ = hexdig[0x0f & (data[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

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/86659577