IKEv2基本原理介绍
pluto调试信息如下:
... ... 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 keysIKEv2协商使用四个报文两次交换便完成IPSec隧道的协商建立。前两个报文用来协商IKE-SA的策略,发起方在收到响应方的应答后进行密钥的计算,而响应方是在收到发起方第三个报文后再进行各个密钥的计算生成(也就是ikev2_parent_inI2outR2()中,最终调用calc_dh_v2()完成密钥生成),之后在ikev2_parent_inI2outR2_tail()中通过finish_dh_v2()将生成的密钥存储在state上。
通过抓包发现是在收到第三个协商报文后,出现错误导致没有应答报文。打开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这里需要注意的一点是:完整性检测算法(认证算法)有两个标准算法: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, };IKEv2的建议载荷: IKEv1的建议载荷: IKEv1与IKEv2除了载荷类型不同,报文的结构也发生了变化,IKEv2中没有了属性载荷,相反IKEv1中的每一个属性载荷对应IKEv2中的一个变化载荷。因此IKEv2在oakley_alg_makedb()生成SA后需要sa_v2_convert()进行依次格式转换,转换为IKEv2的SA格式。
3.3.1 IPSec隧道IKE-SA协商阶段可以配置的参数:
认证方式: 预共享密钥数字证书 加密算法 DES3DESAESSM4 哈希算法 MD5SHA1SM3 DH组 DH1DH2DH5openswan源码中的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, };认证算法(完整性算法)包括:
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 };在IKEv2中PRF算法的应用场景有:
认证数据来源的可靠性
例如ikev2_calculate_psk_sighash()中使用PRF算法、ID、共享秘钥、对端首包、对端Nonce等对数据报文进行认证,从而确保数据包来源的可靠性。
生成IPSec SA的加密秘钥和认证秘钥
在ikev2_derive_child_keys()中,使用PRF算法、双方的Nonce值、SPI、以及先前生成的st_skey_d等生成用于数据流量的加密、认证等的秘钥材料。
即对报文的完整性检查,在ipsec中经常也称为认证(authenticator), 它是对整个报文做哈希(准确的说应该不是整个报文),然后将结果添加到报文的末尾。 这个长度目前好像是固定的12字节,即96位(MD5和SHA1都是)。它使用st->st_oakley.integ_hasher进行哈希。见上图中最后12字节数据。
认证载荷个人理解认证载荷是为了保证数据来源的可靠性,一般包括两种:预共享秘钥和数字证书。它的检查更为严格,以共享秘钥为例:它会对对方的首个报文、Nonce值、ID值同时计算哈希,而不仅仅计算当前报文的内容。认证载荷中填充的数据便是对端对上述内容计算的哈希值。