C语言模拟D-H密钥交换及中间人攻击

C语⾔模拟D-H密钥交换及中间⼈攻击
0. 概述
这是⼀次课程设计,话说这个题⽬有些复杂,但毕竟是最后⼀次,可以理解
实现⽬标:
1. ⽤C语⾔在Linux系统环境下模拟D-H密钥交换,并以协商出的密钥对传输信息⽤AES-256-GCM进⾏加密,实现加密通信
2. 对其进⾏中间⼈攻击,在不⼲扰正常通信的情况下,截获其通讯内容
3. 使⽤预共享密钥的⽅法防⽌中间⼈攻击
实现内容:
可⾃由发挥的空间不多,在密钥交换后,我实现的是客户端可以输⼊简单指令在服务端执⾏,并获取反馈
使⽤⼯具:
因为限定了C语⾔和Linux
⼤数计算:Libtommath
加密: Libtomcrypt
抓包发包: Libpcap
由于这三个库安装起来较简单,这⾥就不叙述了(⽹上⼀堆)
libtommath和libtomcrypt⽹上资料较少,看github上的pdf⽂档即可
部分内容凭记忆写的,有问题欢迎指正
1. 密钥交换
libtomcrypt虽然本⾝有D-H密钥交换的功能实现,但是直接⽤的话怕是太⽔了(总不能课设⼀个include就完事),故以学习为⽬的⼿动实现了下,设计如下:
符号&&流程概述:
双⽅共享p,g,g为p的原根
A⽣成⼀个只有⾃⼰知道的数字a,计算x=g^a mod p
将x发送给B
B⽣成⼀个只有⾃⼰知道的数字b,计算y=g^b mod p
将y发送给A
A计算key = y^a mod p
B计算key = x^b mod p
根据交换原理,⼆者⽣成key相同
具体实现:
1. 客户端建⽴连接,计算并发送p,g,x
2. 服务端接收到p, g, x,⽣成b并计算y,将y返回,同时计算出密钥key
3. 客户端收到y,计算出key
(1) p的⽣成
libtommath ⽣成⼀个⼤素数
mp_prime_random_ex(p, 8, 256, LTM_PRIME_2MSB_ON | LTM_PRIME_SAFE, rng, NULL);
注意,⼀定要带上 LTM_PRIME_SAFE 参数,原因在下⼀部分会说
(2) g的⽣成
g是p的原根,并不需要多⼤
⽽⽂档中对LTM_PRIME_SAFE参数的叙述是这样的:
Make a prime p such that (p - 1)/2 is also prime.
This option implies LTM PRIME BBS as well.
即p-1⼀定只有两个质因⼦2和(p-1)/2,这⼤⼤⽅便了检测,具体实现代码如下
(可能有些⿇烦,不过功能没问题)
void get_primitive_root(mp_int* num, mp_int* root)
{
mp_set(root, 2);
恐怖主义产生的原因mp_int temp, param1, param2;
// 第⼀个参数为2
mp_init_set(¶m1, 2);
酸化油mp_init_multi(&temp, ¶m2, NULL);
// 第⼆个参数问 (roo-1)/2
mp_sub_d(num, 1, ¶m2);
mp_div_2(¶m2, ¶m2);
while (true)
{
mp_exptmod(root, ¶m1, num, &temp);
if (mp_cmp_d(&temp, 1) != MP_EQ)
{
mp_exptmod(root, ¶m2, num, &temp);
if (mp_cmp_d(&temp, 1) != MP_EQ)
{
break;
}
}
mp_add_d(root, 1, root);
}
mp_clear_multi(&temp, ¶m1, ¶m2, NULL);
}
(3) a,b的⽣成
随机且⾜够⼤的数即可,这⾥我采⽤的是mp_rand⽅法
官⽅⽂档中的mp_rand是这样描述的
This function generates a random number of digits bits.
然⽽实际测试中这个bits并不是⼆级制位,⽽是空间占⽤? 看了下源码,是mp_int.used的值故如果⽣成⼀个和p差不多⼤的数字:
mp_rand(a, key->p.used);
(4) “协议”的构建
我⾃定义的协议头:
开头两个字节为df,第三位为1(版本号),第四位为操作特征符
定义特征符为
1:发起密钥交换
2:回应密钥交换
0:交换后的正常数据传输
定义⼀个特征头部有助于抓包中进⾏判定和筛选数据包
(5) AES-256-GCM加解密
这⾥直接调⽤的libtomcrypt库中相关加密代码(⾃⼰写头⼤且没必要)
由于是单个包加密,所以没那么⿇烦
加密:
int aes_256_gcm_encrypt(mp_int* key, unsigned char* plain_text, int pt_len, unsigned char* enc_text, unsigned char* IV, int IV_len, unsigned char* tag, un signed long tag_len)
硅酸盐通报
{
gcm_state gcm;
unsigned char pass[32];
register_cipher(&aes_desc);
mp_to_unsigned_bin(key, pass);
return gcm_memory(find_cipher("aes"), pass, 32, IV, IV_len, NULL, 0, plain_text, pt_len, enc_text, tag, &tag_len, GCM_ENCRYPT);
}
解密:
int aes_256_gcm_decrypt(mp_int* key, unsigned char* plain_text, int pt_len, unsigned char* enc_text, unsigned char* IV, int IV_len, unsigned char* tag, un signed long tag_len)
{
gcm_state gcm;
register_cipher(&aes_desc);
unsigned char pass[32];
mp_to_unsigned_bin(key, pass);
return gcm_memory(find_cipher("aes"), pass, 32, IV, IV_len, NULL, 0, plain_text, pt_len, enc_text, tag, &tag_len, GCM_DECRYPT);
}
⼏乎直接调⽤库函数,这⾥IV为32字节随机字符,tag为16字节,⽆需预先赋值
由于解密也需要iv和tag,故这两个参数随着socket⼀起发过去
memcpy(buf + 4, iv, 32);
memcpy(buf + 36, tag, 16);
if (aes_256_gcm_encrypt(&k, input, len, output, iv, 32, tag, 16) != CRYPT_OK)
{
printf("Error in encrypt!\n");
continue;
}
memcpy(buf + 52, output, len);
2.中间⼈攻击
(1) 原理介绍
D-H密钥交换能应付简单的截获数据的中间⼈,即中间⼈在只截获数据的情况下⽆法获得密钥
中国在疫情防控国际合作上采取了哪些行动⽽专门对付D-H的中间⼈则是建⽴双向连接
即分别和双⽅进⾏秘钥交换建⽴连接,双⽅的通信内容会被先加密后解密
计⽹相关:
当⽹卡判断⽬的IP和本机IP在同⼀个⼦⽹时,会将数据包⼴播出去(⽬的MAC为⽬的主机的MAC),否则会直接交给⽹关(⽬的MAC为⽹关)
当本机⽹卡收到⼀个数据包时,会判断数据包的⽬的MAC是不是本机,如果是,保留并有效,否则直接丢弃
ARP攻击的⽬的则是让通讯的双⽅都认为⽬的主机MAC是中间⼈机器,发送的数据包虽然是⼴播(即能到达⽬的主机),但会因为MAC地址不同⽽被丢弃,反⽽是中间⼈机器会正常收到数据包
本次实验模拟的环境是同⼀⼦⽹下的两台主机,即ARP欺骗两台主机即可
如果是不同⼦⽹的,则需要欺骗⼀台主机和⽹关
(2) 具体实现思路
⾸先中间⼈要保证双⽅不能收到真实数据包,这⾥采⽤ARP欺骗的⽅法
封开论坛arpspoof -i ens33 -t 192.168.13.128 -r 192.168.13.129
ens33为⽹卡名称
这⾥双向投毒(-r)保证双⽅的数据包都⽆法正常到达对⽅
投毒后实现思路如下:
1. 对于握⼿、挥⼿包等不具有协议标记(df1)的数据包,将MAC地址直接改为⽬的MAC地址后发送
2. 对于密钥交换发起数据包(flag=1), 中间⼈解析源数据包的src_p, src_g, src_x,计算⽣成src_y, src_key并缓存下来,同时⽣成
dst_p, dst_g, dst_x,修改数据包的数据字段,并发送⾄⽬的MAC
3. 对于回应数据包(flag=2), 中间⼈获得dst_y,并计算出dst_key,同时发送缓存的src_y给客户端
4. 对于常规通讯数据包,中间⼈根据数据包⽅向⽤src_key和dst_key分别对数据包解密,读取,加密,转发,完成通信
(3) 关于校验和
uint16_t calc_cksm(void *pkt, int len)
{
uint16_t *buf = (uint16_t*)pkt;
uint32_t cksm = 0;
while (len > 1)
{
cksm += *buf++;
cksm = (cksm >> 16) + (cksm & 0xffff);
len -= 2;
}主题模式
if (len)
{
cksm += *((uint8_t*)buf);
cksm = (cksm >> 16) + (cksm & 0xffff);
}
return (uint16_t)((~cksm) & 0xffff);
}
这⾥实现由于未改动IP头,故IP校验和不⽤改
(4)其它
发包:
我是⽤libpcap的pcap_sendpacket直接发的包(libnet发包没发出去也没报错,不知为何)
其它的内容就是bug和代码的堆砌,没什么好说的
3. 预共享密钥
1. 客户端建⽴请求时,将数据字段+两个0字节⽤密钥加密
2. 服务端接受请求并解密,如果最后两个字段为0,便认为客户端验证通过

本文发布于:2024-09-22 22:23:48,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/214822.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:密钥   数据包   交换   实现   加密   特征   计算
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议