1. 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
- calc_dh_v2
- 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
- finish_dh_v2
- pluto_do_crypto_op
- send_crypto_helper_request
- start_dh_v2
③問題原因
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
- 解析每一個載荷
- 認證(就是報文的完整性檢查)
- ikev2_decrypt_msg
- 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;
- ikev2_parse_parent_sa_body
③重點說明
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_calculate_psk_sighash
- ikev2_verify_psk_auth
- 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
- … …
- ikev2_decrypt_msg
③重點說明
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進程異常退出。
②其他
略。