基于openswan klips的IPsec实现分析(十)认证算法维护

本文分析了基于openswan klips的IPsec实现中,认证算法在发送方和接收方的工作原理。发送方通过ipsec_xmit_encap_once()函数对数据进行哈希处理,而接收方则在验证哈希后解密数据。通过对比发送和接收的哈希值来确保数据完整性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于openswan klips的IPsec实现分析(十)认证算法维护

转载请注明出处:http://blog.csdn.net/rosetta

      这里指的认证算法是ESP使用的,对通信过程中的信息做哈希,用来校验信息的完整性的;协商时的认证算法仅用来做哈希,哈希结果再用来做签名和验签。

    相对于加密算法,认证算法会比较简单些,它也不需要像加密算法一样去构造一个结构体(当然如果需要也是可以这么做的)。

    发送方处理比较简单,主要是由ipsec_xmit_encap_once()对加密后的部分数据做哈希,然后一起发给对方;接收方稍微复杂点,先把发送方已经计算好的那份哈希弄出来,再对密文用同样的哈希算法做哈希,最后比较两个结果。

   详细过程如下分析。

发送方

        发送方就一个函数ipsec_xmit_encap_once()。

ipsec_xmit_encap_once()函数

enum ipsec_xmit_value

ipsec_xmit_encap_once(structipsec_xmit_state *ixs)

{

         printk("infunc:%s\n", "ipsec_xmit_encap_once");

#ifdef CONFIG_KLIPS_ESP

         structesphdr *espp;

         unsignedchar *idat, *pad;

         intauthlen = 0, padlen = 0, i;

#endif /* !CONFIG_KLIPS_ESP */

#ifdef CONFIG_KLIPS_AH

         structiphdr ipo;

         structahhdr *ahp;

#endif /* CONFIG_KLIPS_AH */

#if defined(CONFIG_KLIPS_AUTH_HMAC_MD5) ||defined(CONFIG_KLIPS_AUTH_HMAC_SHA1)

         union{

#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5

                   MD5_CTXmd5;

#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */

#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1

                   SHA1_CTXsha1;

#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */

         }tctx;

         __u8hash[AH_AMAX];

#endif /*defined(CONFIG_KLIPS_AUTH_HMAC_MD5) || defined(CONFIG_KLIPS_AUTH_HMACn_SHA1) */

 

         intheadroom = 0, tailroom = 0, ilen = 0, len = 0;

         unsignedchar *dat;

         intblocksize = 8; /* XXX: should be inside ixs --jjo */

         structipsec_alg_enc *ixt_e = NULL;

         structipsec_alg_auth *ixt_a = NULL;

        

         ixs->iphlen= ixs->iph->ihl << 2;

         ixs->pyldsz= ntohs(ixs->iph->tot_len) - ixs->iphlen;

         ixs->sa_len= satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF);

         KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                       "klips_debug:ipsec_xmit_encap_once:"

                       "calling output for <%s%s%s>,SA:%s\n",

                       IPS_XFORM_NAME(ixs->ipsp),

                       ixs->sa_len ? ixs->sa_txt : "(error)");

        

         KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                            "klips_debug:plmdebug:"

                            "ixs->ipsp->ips_said.proto:%d\n",

                            ixs->ipsp->ips_said.proto);

 

         //因为此函数(ipsec_xmit_encap_once)的外面是一个while(ixs->ipsp)循环,所以会循环sp链表,

         //最常见的情况是隧道模式(IPIP)+ESP,但这两个的协议号都是放在这个proto中的。

         //所以会进这个函数两次。

         switch(ixs->ipsp->ips_said.proto){

#ifdef CONFIG_KLIPS_AH

                   caseIPPROTO_AH:

                            headroom+= sizeof(struct ahhdr);

                            break;

#endif /* CONFIG_KLIPS_AH */

 

#ifdef CONFIG_KLIPS_ESP

                   caseIPPROTO_ESP://esp协议号50.假如只esp无ah,走这里。

                            //如果还有AH的话,会进这个函数三次。

                            {

                                      //printk("in func:%s,%s\n", __func__, "IPPROTO_ESP");

                                     ixt_e=ixs->ipsp->ips_alg_enc;//包含加密算法函数指针等结构体。--esp时指定的加密算法。

                                     if(ixt_e) {

                                               blocksize= ixt_e->ixt_common.ixt_blocksize;

                                               //比如sm1算法的ixt_blocksize为16,ocs算法为8。

                                               headroom+= ESP_HEADER_LEN + ixt_e->ixt_common.ixt_support.ias_ivlen/8;

                                               //ESP_HEADER_LEN为SPI(4字节)+序列号(4字节)=8字节,最后一个是数据,一般是IV。

                                               //可看pdf版本rfc2406第二节图.

                                               KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                                                                 "klips_debug:plmdebug:"

                                                                 "ias_ivlen:%d\n",

                                                                 ixt_e->ixt_common.ixt_support.ias_ivlen

                                                                 );

                                               KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                                                                 "klips_debug:plmdebug:"

                                                                 "ias_id:%d,ias_name:%s\n",

                                                                 ixt_e->ixt_common.ixt_support.ias_id,

                                                                 ixt_e->ixt_common.ixt_support.ias_name

                                                                 );

                                               //以esp 3des-md5为例.

                                               //ias_ivlen为64

                                               //isa_id为3(正是3des算法的id)//whack--status 可以查看

                                               //000algorithm ESP encrypt: id=3, name=ESP_3DES, ivlen=64, keysizemin=192,keysizemax=192

                                               //ias_name为NULL,不知道为什么?可能没加到那个名字宏里。

                                     }else {

                                               ixs->stats->tx_errors++;

                                               returnIPSEC_XMIT_ESP_BADALG;

                                     }

 

                                     ixt_a=ixs->ipsp->ips_alg_auth;

                                     if(ixt_a) {

                                               tailroom+= AHHMAC_HASHLEN;

                                     }else

                                     {

                                               //printk("21111111.\n");//走这

                                               switch(ixs->ipsp->ips_authalg){

#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5

                                               caseAH_MD5:

                                                        authlen= AHHMAC_HASHLEN;

                                                        break;

#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */

#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1

                                               caseAH_SHA:

                                                        authlen= AHHMAC_HASHLEN;

                                                        break;

#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */

                                               caseAH_NONE:

                                                        break;

                                               default:

                                                        ixs->stats->tx_errors++;

                                                        returnIPSEC_XMIT_ESP_BADALG;

                                               }                

                                     }

                                     //认证长度12,为什么MD5,SHA1,甚至是SM3的认证长度都为12?

                                     //这个12的依据是?

 

                                     KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                                                        "klips_debug:plmdebug:"

                                                        "1,authlen:%d, tailroom:%d\n",

                                                        authlen,tailroom);

 

                                     tailroom+= blocksize != 1 ?

                                               ((blocksize- ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :

                                               ((4- ((ixs->pyldsz + 2) % 4)) % 4) + 2;

                                     tailroom+= authlen;

                                     //这个应该根据rfc2406进行填充?

 

                                     KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,

                                                        "klips_debug:plmdebug:"

                                                        "2,authlen:%d, tailroom:%d\n",

                                                        authlen,tailroom);

                            }

                            break;

#endif /* CONFIG_KLIPS_ESP */

 

#ifdef CONFIG_KLIPS_IPIP

                   caseIPPROTO_IPIP://什么时候用IPIP?隧道模式,这里只是分配了下空间。

                            headroom+= sizeof(struct iphdr);

                            ixs->iphlen= sizeof(struct iphdr);

                            break;

#endif /* !CONFIG_KLIPS_IPIP */

 

#ifdef CONFIG_KLIPS_IPCOMP

                   caseIPPROTO_COMP://压缩?

                            break;

#endif /* CONFIG_KLIPS_IPCOMP */

 

                   default:

                            ixs->stats->tx_errors++;

                            returnIPSEC_XMIT_BADPROTO;

         }

        

         KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,

                       "klips_debug:ipsec_xmit_encap_once:"

                       "pushing %d bytes, putting %d, proto%d.\n",

                       headroom, tailroom,ixs->ipsp->ips_said.proto);

 

         if(skb_headroom(ixs->skb)< headroom) {

                   printk(KERN_WARNING

                          "klips_error:ipsec_xmit_encap_once:"

                          "tried to skb_push headroom=%d, %davailable.  This should never happen,please report.\n",

                          headroom, skb_headroom(ixs->skb));

                   ixs->stats->tx_errors++;

                   returnIPSEC_XMIT_ESP_PUSHPULLERR;

         }

 

         //skb头部预留headroom字节空间。

         dat= skb_push(ixs->skb, headroom);

         ilen= ixs->skb->len - tailroom;

 

         if(skb_tailroom(ixs->skb)< tailroom) {

                   printk(KERN_WARNING

                          "klips_error:ipsec_xmit_encap_once:"

                          "tried to skb_put %d, %davailable.  This should never happen,please report.\n",

                          tailroom, skb_tailroom(ixs->skb));

                   ixs->stats->tx_errors++;

                   returnIPSEC_XMIT_ESP_PUSHPULLERR;

         }

         //skb尾部预留tailroom字节空间。

         skb_put(ixs->skb,tailroom);

         KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,

                       "klips_debug:ipsec_xmit_encap_once:"

                       "head,tailroom: %d,%d beforexform.\n",

                       skb_headroom(ixs->skb),skb_tailroom(ixs->skb));

         len= ixs->skb->len;

         if(len> 0xfff0) {

                   printk(KERN_WARNING"klips_error:ipsec_xmit_encap_once: "

                          "tot_len (%d) > 65520.  This should never happen, pleasereport.\n",

                          len);

                   ixs->stats->tx_errors++;

                   returnIPSEC_XMIT_BADLEN;

         }

 

         memmove((void*)dat, (void *)(dat + headroom), ixs->iphlen);

         ixs->iph= (struct iphdr *)dat;

         ixs->iph->tot_len= htons(ixs->skb->len);

         //不懂?//虽然没怎么看明白,在IPIP模式下,即隧道模式,这里的目的猜是在ESP包前面加一个原始IP头。

         //这里仅仅是加了空间,具体的赋值在下面操作。

 

         switch(ixs->ipsp->ips_said.proto){

#ifdef CONFIG_KLIPS_ESP

         caseIPPROTO_ESP:

                   espp= (struct esphdr *)(dat + ixs->iphlen);//dat是之前IPIP时加的IP原始头指针。

                   //此时再加一个IP长度,那么这个位置espp就是放ESP头的位置。

                   espp->esp_spi= ixs->ipsp->ips_said.spi;

                   espp->esp_rpl= htonl(++(ixs->ipsp->ips_replaywin_lastseq));

                  

                   if(!ixt_e) {

                            ixs->stats->tx_errors++;

                            returnIPSEC_XMIT_ESP_BADALG;

                   }

                  

                   idat= dat + ixs->iphlen + headroom;

                   ilen= len - (ixs->iphlen + headroom + authlen);

                  

                   /*Self-describing padding */

                   pad= &dat[len - tailroom];

                   padlen= tailroom - 2 - authlen;

                   for(i = 0; i < padlen; i++) {

                            pad[i]= i + 1;

                   }

                   dat[len- authlen - 2] = padlen;

                  

                   dat[len- authlen - 1] = ixs->iph->protocol;

                   ixs->iph->protocol= IPPROTO_ESP;

 

                   //以上这段操作没怎么看懂。对需要加密/认证的数据进行数据补齐。

#ifdef CONFIG_KLIPS_DEBUG

                   if(debug_tunnel& DB_TN_ENCAP) {

                           dmp("pre-encrypt", dat,len);//最终需要加密的数据在dat里。

                   }

#endif

 

                   /*

                    * Do all operations here:

                    * copy IV->ESP, encrypt, update ips IV

                    *

                    */

                   {

                            intret;

                            memcpy(espp->esp_iv,

                                   ixs->ipsp->ips_iv,

                                   ixs->ipsp->ips_iv_size);

 

                   
OpenSwan是一个IPSec实现,它允许您建立安全的虚拟专用网络(VPN)连接来保护您的网络通信。在本教程中,我们将介绍如何使用OpenSwan部署IPSec。我们将讨论每个配置文件的详细信息,并解释第一阶段和第二阶段的认证和加算法如何配置。 步骤1:安装OpenSwan 在开始之前,请确保您已在Linux系统上安装了OpenSwan。您可以使用以下命令在Ubuntu上安装OpenSwan: ``` sudo apt-get update sudo apt-get install openswan ``` 步骤2:配置IPSec 下面是IPSec的配置文件。您可以在/etc/ipsec.conf中找到它。 ``` config setup protostack=netkey nat_traversal=yes virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12 oe=off nhelpers=0 interfaces=%defaultroute plutodebug=all plutostderrlog=/var/log/openswan.log dumpdir=/var/run/pluto/ lockdir=/var/run/pluto/ conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 authby=secret pfs=no conn vpn-to-site left=192.168.1.1 leftsubnet=192.168.1.0/24 right=203.0.113.2 rightsubnet=10.0.0.0/24 auto=start ``` 让我们逐个解释每个配置项: - protostack:指定协议堆栈。在这种情况下,我们使用netkey协议堆栈。 - nat_traversal:启用NAT遍历。如果您的VPN连接将通过NAT进行,则应启用此选项。 - virtual_private:指定内部网络的IP地址段。在这种情况下,我们指定了三个IP地址段:10.0.0.0/8,192.168.0.0/16和172.16.0.0/12。 - oe:关闭超时。如果您的VPN连接长时间处于非活动状态,则关闭此选项可能会更安全,因为它不会在非活动状态下发送任何消息。 - nhelpers:指定NAT助手的数量。在这种情况下,我们将其设置为0。 - interfaces:指定默认路由接口。 - plutodebug:指定调试级别。在这种情况下,我们将其设置为all,以便记录所有信息。 - plutostderrlog:指定日志文件的位置。 - dumpdir:指定该进程的转储目录。 - lockdir:指定该进程的锁定目录。 接下来,我们定义了一些默认连接参数。这些参数将应用于我们所有的连接。 - ikelifetime:指定IKE的生存期。 - keylife:指定IPSec钥的生存期。 - rekeymargin:指定重新生成钥的时间差。 - keyingtries:指定尝试建立连接的次数。 - authby:指定身份验证方式。在这种情况下,我们使用预共享钥(PSK)。 - pfs:指定完美的前向保性。在这种情况下,我们将其禁用。 最后,我们定义了我们的第一个连接,它将使用我们之前定义的默认参数。 - conn vpn-to-site:指定连接的名称。 - left:指定本地IP地址。 - leftsubnet:指定本地IP地址段。 - right:指定远程IP地址。 - rightsubnet:指定远程IP地址段。 - auto:指定连接类型。在这种情况下,我们将其设置为start,以便在启动时自动启动连接。 步骤3:配置PSK 现在,让我们创建一个预共享钥(PSK)。您可以在/etc/ipsec.secrets中找到它。 ``` 192.168.1.1 203.0.113.2 : PSK "myvpnpassword" ``` 在这里,我们将本地IP地址和远程IP地址与PSK绑定。在这种情况下,我们将其设置为“myvpnpassword”。 步骤4:配置IKE 下面是IKE的配置文件。您可以在/etc/ike.conf中找到它。 ``` ikev1=enable esp=3des-sha1 ike=3des-sha1-modp1024 phase2=esp ``` 让我们逐个解释每个配置项: - ikev1:启用IKEv1。 - esp:指定ESP的加算法。在这种情况下,我们使用3DES和SHA1。 - ike:指定IKE的加算法。在这种情况下,我们使用3DES,SHA1和MODP1024。 - phase2:指定第二阶段的协议。在这种情况下,我们使用ESP。 现在,我们已经完成了所有配置。您可以使用以下命令启动IPSec服务: ``` sudo service ipsec start ``` 您可以使用以下命令停止IPSec服务: ``` sudo service ipsec stop ``` 总结 在本教程中,我们介绍了如何使用OpenSwan部署IPSec。我们逐个解释了每个配置文件,并解释了第一阶段和第二阶段的认证和加算法如何配置。现在,您可以使用OpenSwan建立安全的VPN连接来保护您的网络通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值