IKEv2协议几个问题调试过程以及关键知识点总结整理

1. IKEv2基本原理

IKEv2基本原理介绍

2. IKEv2功能遇到的问题

  • 隧道协商速度慢,多条隧道时经常协商不起来
  • 与华为设备对接IKEv2时,协商报文认证失败
  • 与华为设备对接IKEv2时,MD5算法认证失败(SHA1可以成功情况下)
  • 与华为设备对接IKEv2时,数据流量不通
  • 与华为设备对接IKEv2时,IPSec SA配置不同算法时,显示协商成功,但华为显示未成功,数据不通

2.1 问题一:隧道协商速度慢,多条隧道协商时经常协商不起来

①初步分析定位

现象正如问题的描述一样,仅从现象无法得知导致问题的原因。通过打开pluto调试信息发现协商过程中产生的密钥长度为0,密钥都是空的;对比正常的IPSec协商过程中的调试信息,发现这里确实存在问题,因此初步确定协商不起来于此有关。当时调试信息基本如下:

... ...
6.26 14:7:36 pluto(52881): KEY length: SK_d=0  SK_ai=0  SK_ar=0  SK_ei=0  SK_er=0  SK_pi=0  SK_pr=0

6.26 14:7:36 pluto(52881): shared:    12 16 4d 32  b8 3e 1e cb  fe c4 08 29  dd 8d 14 31
6.26 14:7:36 pluto(52881):   07 29 0f 14  41 84 ea d3  50 9f ed 43  55 a7 e9 96
6.26 14:7:36 pluto(52881):   a4 16 e6 47  a2 af ed 8b  e4 1e 51 d2  bd 52 49 f0
6.26 14:7:36 pluto(52881):   18 1f d6 66  dd 8c 4e 43  52 78 4e 2a  1d 36 6d f8
6.26 14:7:36 pluto(52881):   b2 2b e2 bd  1e c4 e4 bf  cc 54 dd 99  1b 92 04 4e
6.26 14:7:36 pluto(52881):   dd f5 b1 b3  43 84 df 22  bd e8 ef d3  05 c8 be bf
6.26 14:7:36 pluto(52881): skeyseed:  81 4d 5e 9f  c1 7f 82 5b  75 73 f8 13  da f6 7b 20
6.26 14:7:36 pluto(52881):   3e a0 39 24
6.26 14:7:36 pluto(52881): SK_d:  
6.26 14:7:36 pluto(52881): SK_ai:  
6.26 14:7:36 pluto(52881): SK_ar:  
6.26 14:7:36 pluto(52881): SK_ei:  
6.26 14:7:36 pluto(52881): SK_er: 
6.26 14:7:36 pluto(52881): SK_pi:  
6.26 14:7:36 pluto(52881): SK_pr:  
6.26 14:7:36 pluto(52881): ikev2 parent inI2outR2: calculating g^{xy}, sending R2
6.26 14:7:36 pluto(52881): processing connection IKEv1
6.26 14:7:36 pluto(52881): decrypting as RESPONDER, using INITIATOR keys

②函数调用关系

  • ikev2parent_inI2outR2
    • start_dh_v2
      • send_crypto_helper_request
        • pluto_do_crypto_op
          • case pcr_build_kenonce:
          • case pcr_build_nonce:
          • case pcr_compute_dh_iv:
          • case pcr_compute_dh:
          • case pcr_compute_dh_v2:
            • calc_dh_v2
              • shared
              • skeyseed
              • SK_d
              • SK_ai
              • SK_ar
              • SK_ei
              • SK_er
              • SK_pi
              • SK_pr
        • ikev2_parent_inI2outR2_tail
          • finish_dh_v2
            • st->st_shared
            • st->st_skey_d
            • st->st_skey_ai
            • st->st_skey_ar
            • st->st_skey_ei
            • st->st_skey_er
            • st->st_skey_pi
            • st->st_skey_pr

在这里插入图片描述

③问题原因

IKEv2协商使用四个报文两次交换便完成IPSec隧道的协商建立。前两个报文用来协商IKE-SA的策略,发起方在收到响应方的应答后进行密钥的计算,而响应方是在收到发起方第三个报文后再进行各个密钥的计算生成(也就是ikev2_parent_inI2outR2()中,最终调用calc_dh_v2()完成密钥生成),之后在ikev2_parent_inI2outR2_tail()中通过finish_dh_v2()将生成的密钥存储在state上。

在NGFW支持IKEv2过程中,在pcr_compute_dh_v2()计算密钥时,IKEv2相关的函数calc_dh_v2()被注释,从而导致生成的密钥全部为空,并最终存到state上的密钥也是空。因此第二阶段的认证加密都会出现问题(有时还会出现段错误的问题),但偶尔也能协商成功。修改后隧道便可以正常协商,且支持了多条隧道同时协商。

2.2 问题二和三:与华为设备对接IKEv2时,协商报文认证失败

①初步分析定位

在这里插入图片描述
通过抓包发现是在收到第三个协商报文后,出现错误导致没有应答报文。打开pluto日志信息发现时由于认证失败导致的。而发起方在收到第四个报文后也会出现该问题。下面时发前方收到第四个报文后的信息。

7.1 9:33:7 pluto(49216): ikev2 parent inR2: calculating g^{xy} in order to decrypt I2
7.1 9:33:7 pluto(49216): decrypting as INITIATOR, using RESPONDER keys
7.1 9:33:7 pluto(49216): data being hmac:  65 d0 57 0a  93 0c 90 ac  a9 d9 44 45  75 56 1a f2
7.1 9:33:7 pluto(49216):   2e 20 23 20  00 00 00 01  00 00 00 74  24 00 00 58
7.1 9:33:7 pluto(49216):   ee 58 f5 c7  50 d4 6e 87  db 11 4f 36  b7 e7 92 0c
7.1 9:33:7 pluto(49216):   a3 cb ff e5  54 c3 f1 47  f4 17 ca 2e  26 99 10 c0
7.1 9:33:7 pluto(49216):   d0 90 5a aa  51 33 c0 27  53 6c 36 a3  d9 c0 b8 3b
7.1 9:33:7 pluto(49216):   0f 6c fa a9  4a 9a 7d f6  c3 12 eb 04  04 c6 7e fc
7.1 9:33:7 pluto(49216):   bd 97 f6 4e  79 58 04 39
7.1 9:33:7 pluto(49216): R2 calculated auth:  05 e1 bf 2d  1a ba 0e ea  9a cd 1f 23
7.1 9:33:7 pluto(49216): R2  provided  auth:  f4 17 ca 2e  26 99 10 c0  50 d4 6e 87
7.1 9:33:7 pluto(49216): R2 failed to match authenticator

②函数调用关系

  • ikev2parent_inR2
    • ikev2_decrypt_msg
      • 认证(就是报文的完整性检查)
        • authkey = &pst->st_skey_ar; 认证密钥
        • pst->st_oakley.integ_hasher认证算法
        • 实际的报文认证部分; 认证数据
        • 计算哈希值;认证结果
        • 比较收到的认证数据与计算出的数据
      • 解密
        • cipherkey = &pst->st_skey_er;
        • pst->st_oakley.encrypter
        • 从报文中获取初始向量:IV
        • … …
      • ikev2_process_encrypted_payloads
        • 解析每一个载荷

  • ikev2parent_inR1outI2 或 ikev2_parent_inI1outR1_tail
    • ikev2_parse_parent_sa_body
      • sadb = oakley_alg_makedb(st->st_connection->alg_info_ike;本地策略转换为SA
      • sa_v2_convert(sadb); IKEv1 SA转换为IKEv2 SA。
      • ikev2_process_transforms; 解析对方的建议载荷
      • ikev2_match_transform_list_parent; 将对端建议载荷与本端配置进行匹配
      • 填充加密算法:ta.encrypter
      • 填充认证算法:ta.integ_hasher
      • 填充PRF算法:ta.prf_hasher
      • 填充DH组:ta.group
      • 将算法存储到state上:st->st_oakley = ta;

③重点说明

3.1 常用的加解密、认证算法:

这里需要注意的一点是:完整性检测算法(认证算法)有两个标准算法:MD5, SHA1;他们的hash_integ_len是固定的96bits==12Bytes
在这里插入图片描述

static struct hash_desc crypto_hasher_md5 =
{
    common: {name: "oakley_md5",
	     officname: "md5",
	     algo_type: IKE_ALG_HASH,
	     algo_id:   OAKLEY_MD5,
	     algo_v2id: IKEv2_PRF_HMAC_MD5,
	     algo_next: NULL, },
    hash_ctx_size: sizeof(MD5_CTX),
    hash_key_size:   MD5_DIGEST_SIZE,
    hash_digest_len: MD5_DIGEST_SIZE,
    hash_integ_len: 0,				/*Not applicable*/
    hash_init: (void (*)(void *)) osMD5Init,
    hash_update: (void (*)(void *, const u_int8_t *, size_t)) osMD5Update,
    hash_final: (void (*)(u_char *, void *)) osMD5Final,
};

static struct hash_desc crypto_integ_md5 =
{
    common: {name: "oakley_md5",
	     officname: "md5",
	     algo_type: IKE_ALG_INTEG,
	     algo_id:   OAKLEY_MD5,
	     algo_v2id: IKEv2_AUTH_HMAC_MD5_96,
	     algo_next: NULL, },
    hash_ctx_size: sizeof(MD5_CTX),
    hash_key_size:   MD5_DIGEST_SIZE,
    hash_digest_len: MD5_DIGEST_SIZE,
    hash_integ_len: MD5_DIGEST_SIZE_96,
    hash_init: (void (*)(void *)) osMD5Init,
    hash_update: (void (*)(void *, const u_int8_t *, size_t)) osMD5Update,
    hash_final: (void (*)(u_char *, void *)) osMD5Final,
};

static struct hash_desc crypto_hasher_sha1 =
{
    common: {name: "oakley_sha",
	     officname: "sha1",
	     algo_type: IKE_ALG_HASH,
	     algo_id:   OAKLEY_SHA,
	     algo_v2id: IKEv2_PRF_HMAC_SHA1,
	     algo_next: NULL, },
    hash_ctx_size: sizeof(SHA1_CTX),
    hash_key_size:   SHA1_DIGEST_SIZE,
    hash_digest_len: SHA1_DIGEST_SIZE,
    hash_integ_len: 0,                          /*Not applicable*/
    hash_init: (void (*)(void *)) SHA1Init,
    hash_update: (void (*)(void *, const u_int8_t *, size_t)) SHA1Update,
    hash_final: (void (*)(u_char *, void *)) SHA1Final,
};

static struct hash_desc crypto_integ_sha1 =
{
    common: {name: "oakley_sha",
	     officname: "sha1",
	     algo_type: IKE_ALG_INTEG,
	     algo_id:   OAKLEY_SHA,
	     algo_v2id: IKEv2_AUTH_HMAC_SHA1_96,
	     algo_next: NULL, },
    hash_ctx_size: sizeof(SHA1_CTX),
    hash_key_size:   SHA1_DIGEST_SIZE,
    hash_digest_len: SHA1_DIGEST_SIZE,
    hash_integ_len: SHA1_DIGEST_SIZE_96,
    hash_init: (void (*)(void *)) SHA1Init,
    hash_update: (void (*)(void *, const u_int8_t *, size_t)) SHA1Update,
    hash_final: (void (*)(u_char *, void *)) SHA1Final,
};
3.2 IKEv1与IKEv2在建议载荷上的区别

IKEv2的建议载荷:
在这里插入图片描述
IKEv1的建议载荷:
在这里插入图片描述
IKEv1与IKEv2除了载荷类型不同,报文的结构也发生了变化,IKEv2中没有了属性载荷,相反IKEv1中的每一个属性载荷对应IKEv2中的一个变化载荷。因此IKEv2在oakley_alg_makedb()生成SA后需要sa_v2_convert()进行依次格式转换,转换为IKEv2的SA格式。

3.3 关于配置参数问题

3.3.1 IPSec隧道IKE-SA协商阶段可以配置的参数:

  • 认证方式:
    • 预共享密钥
    • 数字证书
  • 加密算法
    • DES
    • 3DES
    • AES
    • SM4
  • 哈希算法
    • MD5
    • SHA1
    • SM3
  • DH组
    • DH1
    • DH2
    • DH5
    • 代码中的DH包含
enum ike_trans_type_dh {
	OAKLEY_GROUP_MODP768      = 1,
	OAKLEY_GROUP_MODP1024     = 2,
	OAKLEY_GROUP_GP155        = 3,
	OAKLEY_GROUP_GP185        = 4,
	OAKLEY_GROUP_MODP1536     = 5,

	OAKLEY_GROUP_MODP2048     = 14,
	OAKLEY_GROUP_MODP3072     = 15,
	OAKLEY_GROUP_MODP4096     = 16,
	OAKLEY_GROUP_MODP6144     = 17,
	OAKLEY_GROUP_MODP8192     = 18,
#ifdef USE_MODP_RFC5114
	OAKLEY_GROUP_DH22         = 22,
	OAKLEY_GROUP_DH23         = 23,
	OAKLEY_GROUP_DH24         = 24,
#endif
};

3.3.2 IKEv2中PRF算法是怎么来的:

PRF算法与认证算法都是使用hash进行计算的,NGFW中无法进行单独配置,而是同时配置两者保持一致。除此之外:MD5和SHA1算法的值是相同的在PRF算法和完整性算法中

PRF算法包括:

enum ikev2_trans_type_prf {
	IKEv2_PRF_HMAC_MD5      = 1, /* RFC2104 */
	IKEv2_PRF_HMAC_SHA1     = 2, /* RFC2104 */
	IKEv2_PRF_HMAC_TIGER    = 3, /* RFC2104 */
	IKEv2_PRF_AES128_XCBC   = 4, /* RFC4434 */
	IKEv2_PRF_HMAC_SHA2_256 = 5, /* RFC4868 */
	IKEv2_PRF_HMAC_SHA2_384 = 6, /* RFC4868 */
	IKEv2_PRF_HMAC_SHA2_512 = 7, /* RFC4868 */
	IKEv2_PRF_AES128_CMAC   = 8, /* RFC4615 */
	/* 9 - 1023 Reserved to IANA    RFC4306 */
	/* 1024 - 65535 Private Use     RFC4306 */
	IKEv2_PRF_INVALID	= 65536,/*Modify by Sun zhendong for IKEv2 对接华为设备 at 2020.7.1*/
};

认证算法(完整性算法)包括:

enum ikev2_trans_type_integ {
	IKEv2_AUTH_NONE              = 0,  /* RFC4306 */
	IKEv2_AUTH_HMAC_MD5_96       = 1,  /* RFC2403 */
	IKEv2_AUTH_HMAC_SHA1_96      = 2,  /* RFC2404 */
	IKEv2_AUTH_DES_MAC           = 3,  /* RFC4306 */
	IKEv2_AUTH_KPDK_MD5          = 4,  /* RFC1826 */
	IKEv2_AUTH_AES_XCBC_96       = 5,  /* RFC3566 */
	IKEv2_AUTH_HMAC_MD5_128      = 6,  /* RFC4595 */
	IKEv2_AUTH_HMAC_SHA1_160     = 7,  /* RFC4595 */
	IKEv2_AUTH_AES_CMAC_96       = 8,  /* RFC4494 */
	IKEv2_AUTH_AES_128_GMAC      = 9,  /* RFC4543 */
	IKEv2_AUTH_AES_192_GMAC      = 10, /* RFC4543 */
	IKEv2_AUTH_AES_256_GMAC      = 11, /* RFC4543 */
	IKEv2_AUTH_HMAC_SHA2_256_128 = 12, /* RFC4595 */
	IKEv2_AUTH_HMAC_SHA2_384_192 = 13, /* RFC4306 */
	IKEv2_AUTH_HMAC_SHA2_512_256 = 14, /* RFC4306 */
	/* 15 - 1023 Reserved to IANA         RFC4306 */
	/* 1024 - 65535 Private Use           RFC4306 */
	IKEv2_AUTH_INVALID     =65536
};

④原因分析

  • 问题二是因为未添加PRF算法导致的
  • 问题三是因为sa_v2_convert()在转换SA时,将PRF算法设置为固定的算法SHA1, 因此当选用MD5时,认证检测失败。

2.3 问题四:与华为设备对接IKEv2时,隧道协商成功,但数据流量不通

①初步分析定位

根据调试信息显示:数据报文本地解析对端失败,同时对端华为设备也无法解析本设备发出的加密报文。追代码发现IKEv1与IKEv2在隧道协商成功后,使用相同的函数接口将state信息转换为IPSecSA,生成sadb和spdb。这里没有区别,数据流量流程应该是没有问题的,因为IKEv1可以进行数据通信。到这里最大的可能就是:1)IKEv2协商过程中生成的秘钥参数信息是否和IKEv1存储在相同的位置 ; 2)如果不是第一个原因,就需要详细检查三四个报文的协商流程。

②函数调用关系

  • ikev2parent_inR2
    • ikev2_decrypt_msg
      • 完整性检查
        • st->st_oakley.integ_hasher
    • ikev2_decode_peer_id
    • ikev2_decode_local_id
    • PRF计算ID的HASH值
    • v2_AUTH_RSA
      • ikev2_verify_rsa_sha1
    • v2_AUTH_SHARED
      • ikev2_verify_psk_auth
        • ikev2_calculate_psk_sighash
          • get_preshared_secret
          • st->st_oakley.prf_hasher
          • 对端的首包
          • 对端的Nonce载荷数据
          • 对端的ID的哈希值
    • ikev2_child_validate_responder_proposal
    • ikev2_parse_child_sa_body
      • p2alg = kernel_alg_makedb(c->policy , c->alg_info_esp; 根据当前配置生成IPSecSA载荷
      • sa_v2_convert; 将IPSecSA载荷转换为IKEv2的格式类型
      • ikev2_process_transforms; 解析对端响应的ipsec-SA的建议载荷
      • ikev2_match_transform_list_child;本端配置和对端选择的ipsec-SA载荷进行匹配
      • ta.encrypter保存加密算法
      • ta.integ_hash保存完整性检测算法
      • st->st_esp.attrs.transattrs = ta; 将匹配的算法存储到state
      • st->st_esp.present = TRUE; 封装协议标记位
    • ikev2_child_notify_process
    • ikev2_derive_child_keys 最关键的生成数据流量秘钥函数接口
      • childsacalc.prf_hasher
      • childsacalc.ni
      • childsacalc.nr
      • childsacalc.spii
      • childsacalc.spir
      • childsacalc.skeyseed = &st->st_skey_d; 使用了st->st_skey_d
      • st->st_esp.present = TRUE;
      • st->st_esp.keymat_len=enc_len + auth_len;
      • st->st_esp.peer_keymat=v2genbytes(childsacalc, …)
      • st->st_esp.our_keymat=v2genbytes(childsacalc, …)
    • install_ipsec_sa
      • … …

③重点说明

3.1 PRF算法的使用

在IKEv2中PRF算法的应用场景有:

  • 认证数据来源的可靠性

    例如ikev2_calculate_psk_sighash()中使用PRF算法ID共享秘钥对端首包对端Nonce等对数据报文进行认证,从而确保数据包来源的可靠性。

  • 生成IPSec SA的加密秘钥和认证秘钥

    ikev2_derive_child_keys()中,使用PRF算法双方的Nonce值SPI、以及先前生成的st_skey_d等生成用于数据流量的加密、认证等的秘钥材料。
    在这里插入图片描述

3.2 关键名词

在这里插入图片描述

  • 完整性

即对报文的完整性检查,在ipsec中经常也称为认证(authenticator), 它是对整个报文做哈希(准确的说应该不是整个报文),然后将结果添加到报文的末尾。 这个长度目前好像是固定的12字节,即96位(MD5和SHA1都是)。它使用st->st_oakley.integ_hasher进行哈希。见上图中最后12字节数据。

  • 认证载荷

个人理解认证载荷是为了保证数据来源的可靠性,一般包括两种:预共享秘钥数字证书。它的检查更为严格,以共享秘钥为例:它会对对方的首个报文、Nonce值、ID值同时计算哈希,而不仅仅计算当前报文的内容。认证载荷中填充的数据便是对端对上述内容计算的哈希值。

④原因分析

  • PRF算法是固定的,默认采用了SHA1算法,应该用移植时版本太obsolete的缘故。

2.4 问题五:IPSec SA配置不同算法时,显示协商成功,流量不通

①初步分析定位

显示协商成功,实际上只是NGFW上显示协商成功,华为设备上显示并未成功。这个协商不成功的原因是由于第二阶段的算法不一致导致的,并且协商时对端的应答报文中给出相应的原因。但是由于NGFW的IKEv2现阶段并未处理INFORMATION载荷,因此误认为对端接受了本端的提议,从而显示协商成功。但由于应答报文中没有建议载荷,从而本端并未生成Ipsec SA,数据流量因此无法通讯;如果对当前的隧道进行操作,在删除当前隧道的状态、sadb等时由于sadb中的相关参数为空导致pluto进程异常退出。
在这里插入图片描述

②其他

略。

猜你喜欢

转载自blog.csdn.net/s2603898260/article/details/107117675
今日推荐