当前位置:   article > 正文

IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包_ikev2交互过程

ikev2交互过程

IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

1. IKEv2 协商总体框架

IKEv1协议建立一对IPSec SA,使用主动模式需要9个报文,使用野蛮模式需要使用6个报文方能协商成功。IKEv2对IKEv1协议进行了优化,IKEv2只需要进行两次交互,使用 4 条消息就可以完成一个 IKEv2 SA 和一对 IPsec SA 的协商建立。IKEv2 定义了三种交互:

  • 初始交换

  • 创建子 SA 交换

  • 通知交换

    下图简单介绍一下 IKEv2 协商过程中的初始交换过程,之后我们将对ikev2_parent_inI1outR1()接口相关的处理流程做一个简单说明。
    在这里插入图片描述

初始化交换通过两次交换共4个报文便可以完成一对IKE SA和IPSec SA的协商。上图主要用来描述协商报文的内容和对应的处理函数入口。下图则是用来说明各接口对应的协商状态。协商过程中是根据该状态来确定当前的协商阶段。

在这里插入图片描述

RFC文档中的报文格式:

Initiator                         Responder
-------------------------------------------------------------------
HDR, SAi1, KEi, Ni  -->
                                  <--  HDR, SAr1, KEr, Nr, [CERTREQ]

HDR, SK {IDi, [CERT,] [CERTREQ,]
     [IDr,] AUTH, SAi2,
     TSi, TSr}  -->
                                  <--  HDR, SK {IDr, [CERT,] AUTH, 
                                  			SAr2, TSi, TSr}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中:

报文字段说明
HDR报文头部
SAi1、SAr1IKE SA建议
SAi2、SAr2IPSEC SA建议载荷
KEi、KErDH算法公共值
Ni、NrNonce随机数
CERT、CERTREQ证书载荷、证书请求载荷
IDi、IDrID载荷
TSi、TSr流量选择器,使用此载荷完成保护子网的协商
AUTH认证数据载荷

这里面需要说明的是:报文中的SK并不是一个载荷。而是:SK {…}表示里面的内容被加密和认证保护

下面对响应端对发起端第一包的处理流程做一个详细说明。入口函数为:ikev2_parent_inI1outR1()

2. 第二包流程图

在这里插入图片描述

3. openswan源码学习

3.1 ikev2parent_inI1outR1()

此函数是IKEv2协议 响应端开始协商的入口函数。主要功能包括:

  • 根据收到报文四元组(IP和端口)查找连接:c
  • 新建一个协商状态结构:state
  • 根据发起端的地址等信息生成Cookie值
  • 将连接上的参数信息、配置信息初始化state结构上参数
    • 隧道本端IP和端口
    • 隧道对端IP和端口
    • 隧道的出接口
    • 配置策略
    • … …
  • 抗重放检测(Cookie challenge)
    • 根据发起端IP、SPI、Nonce计算用于抗重放的cookie
    • 如果报文中包含KE和Cookie类型的通知载荷
      • 比较两端的cookie是否一致
    • 如果报文中不包含Cookie类型的通知载荷
      • 发送携带cookie的通知报文,使发起端重新开始协商
  • 解析报文中的KE载荷,获取到DH组信息
    • DH组猜测成功,则计算DH算法的公共值,用于构建响应报文的KE载荷
static stf_status
ikev2_parent_inI1outR1_tail(struct pluto_crypto_req_cont *pcrc
                            , struct pluto_crypto_req *r);

stf_status ikev2parent_inI1outR1(struct msg_digest *md)
{
    struct state *st = md->st;
    lset_t policy = POLICY_IKEV2_ALLOW;
    lset_t policy_hint = LEMPTY;
	/*
	*	根据隧道两端协商地址和端口来查找连接
	*/
    struct connection *c = find_host_connection(ANY_MATCH, &md->iface->ip_addr
                 , md->iface->port
                 , KH_IPADDR
                 , &md->sender
                 , md->sender_port
                 , POLICY_IKEV2_ALLOW, LEMPTY, &policy_hint);

    if(c==NULL) {/*连接查找失败则提示相应的错误信息*/
        if(policy_hint & POLICY_IKEV2_ALLOW) {
            /* connection not found, because IKEv2 was not allowed */
            /* send back AUTHENTICATION_FAILED per WG mailing list discussion */
            openswan_log("connection refused, IKEv2 not authorized");
            return STF_FAIL + v2N_AUTHENTICATION_FAILED;
        }

        /*
     * be careful about responding, or logging, since it may be that we
     * are under DOS
     */
        DBG_log("no connection with matching policy found\n");
        return STF_FAIL + v2N_AUTHENTICATION_FAILED;
    }


    loglog(RC_COMMENT, "tentatively considering connection: %s\n", c ? c->name : "<none>");

    if(!st) {/*如果没有对应的状态,则新建一个state*/
	st = new_state();
	/* set up new state */
	memcpy(st->st_icookie, md->hdr.isa_icookie, COOKIE_SIZE);
	
	/* initialize_new_state expects valid icookie/rcookie values, so create it now */
	
	/*相应方根据发起方的地址信息、当前时间生成一个cookie*/
	get_cookie(FALSE, st->st_rcookie, COOKIE_SIZE, &md->sender);
	initialize_new_state(st, c, policy, 0, NULL_FD, pcim_stranger_crypto);
	 st->st_ikev2      = TRUE;
        st->st_localaddr  = md->iface->ip_addr;
        st->st_localport  = md->iface->port;
        st->st_remoteaddr = md->sender;
        st->st_remoteport = md->sender_port;
        st->st_ike_maj    = md->maj;
        st->st_ike_min    = md->min;
	change_state(st, STATE_PARENT_R1);

        md->st = st;
        md->from_state = STATE_IKEv2_BASE;
        md->transition_state = st;
    }

    /* check,as a responder, are we under dos attack or not
     * if yes go to 6 message exchange mode. it is a config option for now.
     * TBD set force_busy dynamically
     * Paul: Can we check for STF_TOOMUCHCRYPTO ?
     */
    if(force_busy == TRUE)/*开启抗重放!!!!!!!!*/
        {
            u_char dcookie[SHA1_DIGEST_SIZE];
            chunk_t dc;
			/*计算用于cookie challenge的cookie值*/
            ikev2_get_dcookie( dcookie, st->st_ni, &md->sender, st->st_icookie);
            dc.ptr = dcookie;
            dc.len = SHA1_DIGEST_SIZE;/*20bytes*/

            /* check if I1 packet contian KE and a v2N payload with type COOKIE */
			/*报文格式??? : HDR + NOTIF + KE*/
            if ( md->chain[ISAKMP_NEXT_v2KE] &&   md->chain[ISAKMP_NEXT_v2N] &&
                 (md->chain[ISAKMP_NEXT_v2N]->payload.v2n.isan_type == v2N_COOKIE))
                {/*对方的协商报文中包含了cookie*/
                /*
         *	ISAKMP_NEXT_v2N  : IKEv2 通知载荷
         * ISAKMP_NEXT_v2Ni : 发起端Nonce载荷
         * ISAKMP_NEXT_v2Nr : 接收端Nonce载荷
         */
                    u_int8_t spisize;
                    const pb_stream *dc_pbs;
                    chunk_t blob;
                    DBG(DBG_CONTROLMORE
                        , DBG_log("received a DOS cookie in I1 verify it"));
					
                    /* we received dcookie we send earlier verify it */
                    spisize = md->chain[ISAKMP_NEXT_v2N]->payload.v2n.isan_spisize;
                    dc_pbs = &md->chain[ISAKMP_NEXT_v2N]->pbs;
                    blob.ptr = dc_pbs->cur + spisize;
                    blob.len = pbs_left(dc_pbs) - spisize;
                    DBG(DBG_CONTROLMORE
                        ,DBG_dump_chunk("dcookie received in I1 Packet", blob);
                        DBG_dump("dcookie computed", dcookie, SHA1_DIGEST_SIZE));

			/*检查收到的cookie和本端发送的cookie是否一致!!!*/
                    if(memcmp(blob.ptr, dcookie, SHA1_DIGEST_SIZE)!=0) {
                        openswan_log("mismatch in DOS v2N_COOKIE,send a new one");
                        SEND_V2_NOTIFICATION_DATA(md, st, v2N_COOKIE, &dc);
                        return STF_FAIL + v2N_INVALID_IKE_SPI;
                    }
                    DBG(DBG_CONTROLMORE
                        ,DBG_log("dcookie received match with computed one"));
                 }
            else/*收到的报文中并不包含COOKIE载荷,因此需要发起cookie challenge*/
                {/*COOKIE challenge通知载荷发送*/
                    /* we are under DOS attack I1 contains no DOS COOKIE */
                    DBG(DBG_CONTROLMORE
                        ,DBG_log("busy mode on. receieved I1 without a valid dcookie");
                        DBG_log("send a dcookie and forget this state"));
                    SEND_V2_NOTIFICATION_DATA(md, st, v2N_COOKIE, &dc);
                    return STF_FAIL;
                }
        }
    else {
        DBG(DBG_CONTROLMORE ,DBG_log("will not send/process a dcookie"));

    }

    /*
     * If we did not get a KE payload, we cannot continue. There should be
     * a Notify telling us why. We inform the user, but continue to try this
     * connection via regular retransmit intervals.
     */
    if(md->chain[ISAKMP_NEXT_v2N]  && (md->chain[ISAKMP_NEXT_v2KE] == NULL))
    {
         const char *from_state_name = enum_name(&state_names, st->st_state);
         const u_int16_t isan_type = md->chain[ISAKMP_NEXT_v2N]->payload.v2n.isan_type;
         openswan_log("%s: received %s"
                     , from_state_name
                     , enum_name(&ikev2_notify_names, isan_type));
         return STF_FAIL + isan_type;
    } else if( md->chain[ISAKMP_NEXT_v2N]) {
            /* XXX/SML: KE payload came with a notification-- is there a problem? */
         DBG(DBG_CONTROL,DBG_log("received a notify.."));
    }

    /*
     * We have to agree to the DH group before we actually know who
     * we are talking to.   If we support the group, we use it.
     *
     * It is really too hard here to go through all the possible policies
     * that might permit this group.  If we think we are being DOS'ed
     * then we should demand a cookie.
     */
    {
        struct ikev2_ke *ke;
        if (md->chain[ISAKMP_NEXT_v2KE] == NULL)
                    return STF_FAIL;
		
        ke = &md->chain[ISAKMP_NEXT_v2KE]->payload.v2ke;
	/*KE载荷中包含DH GROUP信息*/
        st->st_oakley.group=lookup_group(ke->isak_group);/*查找DH GROUP,并存储在st->st_oakley上*/
        if(st->st_oakley.group==NULL) {
            char fromname[ADDRTOT_BUF];

            addrtot(&md->sender, 0, fromname, ADDRTOT_BUF);
            openswan_log("rejecting I1 from %s:%u, invalid DH group=%u"
                         ,fromname, md->sender_port, ke->isak_group);
            return v2N_INVALID_KE_PAYLOAD;
        }
    }

    /* now. we need to go calculate the nonce, and the KE */
    {
        struct ke_continuation *ke = alloc_thing(struct ke_continuation
                                                 , "ikev2_inI1outR1 KE");
        stf_status e;

        ke->md = md;
        set_suspended(st, ke->md);

        if (!st->st_sec_in_use) {
            pcrc_init(&ke->ke_pcrc);
            ke->ke_pcrc.pcrc_func = ikev2_parent_inI1outR1_continue;
            e = build_ke(&ke->ke_pcrc, st, st->st_oakley.group, pcim_stranger_crypto);
            if(e != STF_SUSPEND && e != STF_INLINE) {
                loglog(RC_CRYPTOFAILED, "system too busy");
                delete_state(st);
            }
        } else {
            e = ikev2_parent_inI1outR1_tail((struct pluto_crypto_req_cont *)ke
                                            , NULL);
        }

        reset_globals();

        return e;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197

3.2 ikev2parent_inI1outR1_tail()

此函数最主要的功能就是构建应答报文。具体的步骤包括如下几个方面:

  • 保存对端发送的协商报文,用于后续认证操作
  • 解析协商报文中的通知载荷
    • NAT-D
    • error information
    • … …
  • 构建IKEv2报文头部
  • 构建SA建议载荷
    • 将本地配置策略生成并转换为ikev2 SADB
    • 解析对端的SA载荷中所有的建议载荷
    • 使用排列组合匹配本地的SADB算法信息
  • 解析对端KE载荷
  • 解析对端Nonce载荷。
  • 构建KE载荷
  • 构建Nonce载荷
  • 构建NAT-D载荷
  • 保存当前报文供后续的认证等操作
  • 发送报文
static stf_status
ikev2_parent_inI1outR1_tail(struct pluto_crypto_req_cont *pcrc
                            , struct pluto_crypto_req *r)
{
    struct ke_continuation *ke = (struct ke_continuation *)pcrc;
    struct msg_digest *md = ke->md;
    struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_v2SA];
    struct state *const st = md->st;
    stf_status notok;
    int    numvidtosend=0;
#ifdef PLUTO_SENDS_VENDORID
    numvidtosend++;  /* we send Openswan VID */
#endif

    if (sa_pd == NULL) {
                return STF_FAIL;
    }

    /* note that we don't update the state here yet */

    /* record first packet for later checking of signature */
    clonetochunk(st->st_firstpacket_him, md->message_pbs.start
                 , pbs_offset(&md->message_pbs), "saved first received packet");


    /*
     * verify the NAT DETECTION notify messages before answering.
     * on the responder side, this allows us to detect when *we* are behind
     * at NAPT (probably with a port-forward).
     *
     * If we are, then we set a bit saying so, which later on will make us pick the
     * UDP encapsulation for packets.  It is up to the initiator to switch ports
     * from 500 to 4500.  Could be they have already done so, we do not care here.
     */
    if(md->chain[ISAKMP_NEXT_v2N]) {/*处理通知载荷,其中包括NAT-D探测*/
        ikev2_process_notifies(st, md);
    }


    /* make sure HDR is at start of a clean buffer */
    zero(reply_buffer);
    init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer), "reply packet");

    /* HDR out */
    {
        struct isakmp_hdr r_hdr = md->hdr;

        memcpy(r_hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);

        r_hdr.isa_version = IKEv2_MAJOR_VERSION << ISA_MAJ_SHIFT | IKEv2_MINOR_VERSION;
        r_hdr.isa_np = ISAKMP_NEXT_v2SA;
        r_hdr.isa_flags = ISAKMP_FLAGS_R|IKEv2_ORIG_INITIATOR_FLAG(st);
        r_hdr.isa_msgid = st->st_msgid;
        if (!out_struct(&r_hdr, &isakmp_hdr_desc, &reply_stream, &md->rbody))
            return STF_INTERNAL_ERROR;
    }

    /* start of SA out */
    {
        struct ikev2_sa r_sa = sa_pd->payload.v2sa;/*收到的报文中的SA头部信息*/
        v2_notification_t rn;
        pb_stream r_sa_pbs;

        r_sa.isasa_np = ISAKMP_NEXT_v2KE;  /* XXX */
        if (!out_struct(&r_sa, &ikev2_sa_desc, &md->rbody, &r_sa_pbs))
            return STF_INTERNAL_ERROR;

        /* SA body in and out *//*解析对方的SA建议载荷,并将选择的建议载荷填充到r_sa_pbs中*/
        rn = ikev2_parse_parent_sa_body(&sa_pd->pbs, &sa_pd->payload.v2sa,
                                        &r_sa_pbs, st, FALSE);

        if (rn != v2N_NOTHING_WRONG)
            return STF_FAIL + rn;
    }
/*解析对端的KE载荷,存储在st_gi中*/
    if((notok = accept_v2_KE(md, st, &st->st_gi, "Gi"))!=STF_OK) {
        return notok;
    }

    /* Ni in *//*解析对端的Nonce载荷,存储在st_ni中*/
    RETURN_STF_FAILURE(accept_v2_nonce(md, &st->st_ni, "Ni"));

    /* send KE *//*填充KE*/
    if(!ship_v2KE(st, r, &st->st_gr, &md->rbody, ISAKMP_NEXT_v2Nr))
        return STF_INTERNAL_ERROR;

    /* send NONCE *//*填充Nonce*/
    unpack_nonce(&st->st_nr, r);
    if(!justship_v2Nonce(st, &md->rbody, &st->st_nr, 0)) {
        return STF_INTERNAL_ERROR;
    }

    if(!justship_v2nat(st, &md->rbody)) {/*填充NAT-D*/
        return STF_INTERNAL_ERROR;
    }

    /* Send VendrID if needed VID */
    {
        pbs_set_np(&md->rbody, ISAKMP_NEXT_v2V);
        if (!out_generic_raw(0, &isakmp_vendor_id_desc, &md->rbody
                             , pluto_vendorid, strlen(pluto_vendorid), "Vendor ID"))
            return STF_INTERNAL_ERROR;
    }

    close_message(&md->rbody);
    close_output_pbs(&reply_stream);

    /* let TCL hack it before we mark the length. */
    TCLCALLOUT("v2_avoidEmitting", st, st->st_connection, md);

    /* keep it for a retransmit if necessary */
    freeanychunk(st->st_tpacket);
    clonetochunk(st->st_tpacket, reply_stream.start, pbs_offset(&reply_stream)
                 , "reply packet for ikev2_parent_inI1outR1_tail")

        /* save packet for later signing */
        freeanychunk(st->st_firstpacket_me);
    clonetochunk(st->st_firstpacket_me, reply_stream.start
                 , pbs_offset(&reply_stream), "saved first packet");


    /* while waiting for initiator to continue, arrange to die if nothing happens */
    delete_event(st);
    event_schedule(EVENT_SO_DISCARD, 300, st);

    return STF_OK;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

3.3 ikev2_parse_parent_sa_body()

该函数主要的作用是解析协商报文中的SA建议载荷,并从中选择本地支持的算法进行协商。

主要流程如下

  • 根据配置的SA建议载荷转换为SADB结构
  • 将SADB结构转换为IKEv2的SA结构
  • 解析报文中的SA载荷
  • 将报文SA中的算法进行排列组合, 并与本地SADB中的算法进行匹配
    • 加密算法
    • 完整性哈希算法
    • PRF哈希算法
    • DH算法
  • 根据算法标识获取算法详细参数,并将其存储在state上
  • 如果为响应端
    • 将选中的算法填充到报文SA载荷中
v2_notification_t
ikev2_parse_parent_sa_body(
    pb_stream *sa_pbs,              /* body of input SA Payload */
    const struct ikev2_sa *sa_prop UNUSED, /* header of input SA Payload */
    pb_stream *r_sa_pbs,	    /* if non-NULL, where to emit winning SA */
    struct state *st,  	            /* current state object */
    bool selection UNUSED           /* if this SA is a selection, only one
				     * tranform can appear. */
    )
{
    pb_stream proposal_pbs;
    struct ikev2_prop proposal;
    unsigned int np = ISAKMP_NEXT_P;
    /* we need to parse proposal structures until there are none */
    unsigned int lastpropnum=-1;
    bool conjunction, gotmatch;
    struct ikev2_prop winning_prop;
    struct db_sa *sadb;
    struct trans_attrs ta;
    struct connection *c = st->st_connection;
    
    /*根据配置确定选用的SADB模板*/
    int    policy_index = POLICY_ISAKMP(c->policy
					, c->spd.this.xauth_server
					, c->spd.this.xauth_client);

    struct ikev2_transform_list itl0, *itl;

    memset(&itl0, 0, sizeof(struct ikev2_transform_list));
    itl = &itl0;

    /* find the policy structures */
	/*根据策略配置生成SADB信息*/
    sadb = st->st_sadb;
    if(!sadb) {
		st->st_sadb = &oakley_sadb[policy_index];
		sadb = oakley_alg_makedb(st->st_connection->alg_info_ike
				 , st->st_sadb, 0);
		if(sadb != NULL) {
	    	st->st_sadb = sadb;
		}
		sadb = st->st_sadb;
    }
	/*将SA结构由IKEv1转换为IKEv2*/
    sadb = st->st_sadb = sa_v2_convert(sadb);

    gotmatch = FALSE;
    conjunction = FALSE;
    zero(&ta);

    while(np == ISAKMP_NEXT_P) {/*一般情况下只有一个建议载荷*/
	/*
	 * note: we don't support ESN,
	 * so ignore any proposal that insists on it
	 */

	if(!in_struct(&proposal, &ikev2_prop_desc, sa_pbs, &proposal_pbs))
	    return PAYLOAD_MALFORMED;

	if(proposal.isap_protoid != PROTO_ISAKMP) {
	    loglog(RC_LOG_SERIOUS, "unexpected PARENT_SA, expected child");
	    return PAYLOAD_MALFORMED;
	}
	... ...
	gotmatch = FALSE;

	{/*将报文中的建议载荷转换为struct ikev2_transform_list*/
		stf_status ret = ikev2_process_transforms(&proposal
						    , &proposal_pbs, itl);
	    if(ret != STF_OK) return ret;
	}

	np = proposal.isap_np;/*下一个建议载荷 或者为空*/

	if(ikev2_match_transform_list_parent(sadb
					     , proposal.isap_propnum
					     , itl)) {/*匹配成功的算法组合存储在itl中*/

	    winning_prop = proposal;
	    gotmatch = TRUE;
	    /* gotmatch is true, so will never go inside if*/

	}
    }

    /*
     * we are out of the loop. There are two situations in which we break
     * out: gotmatch == FALSE, means nothing selected.
     */
    if(!gotmatch) {
		return NO_PROPOSAL_CHOSEN;
    }
    /*
     * since we found something that matched, we might need to emit the
     * winning value.
     *
     * 从对方的建议载荷中找到了合适的算法信息;
     */
    /*加密算法*/
    ta.encrypt   = itl->encr_transforms[itl->encr_i];
    ta.enckeylen = itl->encr_keylens[itl->encr_i] > 0 ?
			itl->encr_keylens[itl->encr_i] : 0;
    ta.encrypter = (struct encrypt_desc *)ike_alg_ikev2_find(IKE_ALG_ENCRYPT
							     , ta.encrypt
							     , ta.enckeylen);
    passert(ta.encrypter != NULL);
    if (ta.enckeylen <= 0)
	ta.enckeylen = ta.encrypter->keydeflen;

	/*完整性哈希算法*/
    ta.integ_hash  = itl->integ_transforms[itl->integ_i];
    ta.integ_hasher= (struct hash_desc *)ike_alg_ikev2_find(IKE_ALG_INTEG,ta.integ_hash, 0);
    passert(ta.integ_hasher != NULL);

	/*PRF 哈希算法*/
    ta.prf_hash    = itl->prf_transforms[itl->prf_i];
    ta.prf_hasher  = (struct hash_desc *)ike_alg_ikev2_find(IKE_ALG_HASH, ta.prf_hash, 0);
    passert(ta.prf_hasher != NULL);

	/*DH 算法*/
    ta.groupnum    = itl->dh_transforms[itl->dh_i];
    ta.group       = lookup_group(ta.groupnum);

    st->st_oakley = ta;/*将算法信息存储到state上*/

    if (r_sa_pbs != NULL)/*将选中的算法填充到应答报文的SA载荷上*/
    {
	return ikev2_emit_winning_sa(st, r_sa_pbs
				     , ta
				     , /*parentSA*/TRUE
				     , winning_prop);
    }
    return NOTHING_WRONG;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

4. 小结

4.1 SA载荷中加密套件算法选择

  • IKEv2加密套件的转换详细说明见下表
用途名称编号
IKE转换类型Ⅰ(加密)ENCR_3DES3
ENCR_NULL11
ENCR_AES_CBC12
ENCR_AES_CTR13
IKE转换类型Ⅱ(PRF)PRF_HMAC_MD51
PEF_HMAC_SHA12
PRF_AES128_CBC4
IKE转换类型Ⅲ(完整性)AUTH_HMAC_MD5_961
AUTH_HMAC_SHA1_962
AUTH_AES_XCBC_965
IKE转换类型Ⅳ(DH组)1024 MODP (Group 2)2
2048 MODP (Group 14)14

安全关联(SA)负载包含了一个SPI数值和一套建议(通常是一个)。这些建议是通过一些复杂的建议结构关联起来的。每一个建议结构都会被编号(参见上表)并且包含一个IPsec协议标识符,此标识符用来支持采用的协议,如IKE, ESP, AH。

每一个建议载荷包含一个或者多个变换载荷用来描述指定协议的算法。通常情况下,AH协议仅有一个变换载荷(与完整性检验算法有关),ESP协议有两个变换载荷(与完整性检验算法和加密算法相对应),DH算法有四个转换结构(DH组编号、PRF算法、完整性检验算法、加密算法)。而第二个报文中的SA加密套件中的建议载荷便属于DH转换结构。

4.2 密钥交换KE和随机负载Nonce

IKE_SA_INIT交换报文中处理SA载荷外,还包括KE载荷和Nonce载荷。

  • KE载荷包括DH组编号和密钥交换数据(DH算法公共值)
  • Nonce载荷则是随机生成的随机数,用于生成密钥材料。

DH一旦交换完成,双方便可以计算出自己的SKEYSEED,该值用来生成所有与IKE_SA相关的子密钥,包括:SK_d, SK_ai, SK_ar, SK_ei, SK_er, SK_pi, SK_pr

SKEYSEED的计算方式如下:
S K E Y S E E D = p r f ( N i ∣ N r , g i r ) SKEYSEED = prf(Ni | Nr, g^{ir} ) SKEYSEED=prf(NiNr,gir)

密钥用途
SK_ai, SK_ar用于认证
SK_ei, SK_er用于加密
SK_d用于派生CHILD_SA密钥
SK_p用于在IKE_AUTH交换中生成AUTH载荷

版本所有,谢绝转载!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/765221
推荐阅读
相关标签
  

闽ICP备14008679号