数字签名和验签过程实现分析

版权声明:本文为博主原创文章,可自由转载,转载请注明出处:http://blog.csdn.net/rosetta https://blog.csdn.net/rosetta/article/details/7712606
本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性。

         数字签名在网络安全领域用的比较多,可实现用户身份的真实可靠性;实现信息的完整性,确保数据在存储、传输和处理的过程中免遭任何非授权的或非预期的修改、插入、删除、重发等破坏,从而实现数据的真实性、有效性和一致性;实现抗抵赖性,通过数字签名确保信息的发送方不能抵赖曾经发送的信息,不能否认自己的操作行为。
        下面结合著名的VPN开源项目openswan-2.4.7来理解数字签名和解签名的具体过程。
一、数字签名和解签(验签)过程:使用hash函数对报文数据生成摘要,使用自己的私钥对摘要进行签名(其实就是加密过程),生成签名数据,把原始数据报文和签名数据发送给对方。接收方收到原始数据报文和签名数据后,首先使用相同的hash函数对原始数据生成一个摘要,其次,用对方的公钥对签名数据进行解签名(解密过程),这个解签名的结果也是个摘要,最后比较这两个摘要是否相同,如果相同那么接收方就能确认该数字签名是发送方的。
        用以下hash1、hash2和hash3表明不同时期的摘要:
              发送方:  签名值=f(hash1(msg),私钥); (msg表示原始报文数据)
              接收方:hash2(msg) = f^(-1)(签名值,公钥);(我觉得就是f的反函数,所以写了f^(-1)表示)
                             hash3(msg);
     正常情况下hash2通过公钥对“发送方发过来的签名值”解签后应该等于hash1。hash1和hash3都是对原始报文数据进行摘要,所以只要最后比较hash2和hash3是否相同,就能确定消息的完整性和证明数据是由对方发送的(抗抵赖)。

二、openswan协商总体流程,只关注第5、6个包即可。
                Initiator                          Responder
               -----------                        -----------
                HDR, SA                     -->
 第1个包      main_outI1(发送初始化数据1)
                                            <--    HDR, SA
 第2个包                                            main_inI1_outR1(接收到初始化数据1, 发送响应数据1)
                HDR, KE, Ni                 -->
第3个包        main_inR1_outI2(接收到响应数据1,发送初始化数据2)
                                            <--    HDR, KE, Nr
第4个包                                             main_inI2_outR2(接收到初始化数据2, 发送响应数据2)
                HDR*, IDii, [ CERT, ] SIG_I -->
第5个包        main_inR2_outI3(接收到响应数据2,发送初始化数据3)
                                            <--    HDR*, IDir, [ CERT, ] SIG_R
第6个包                                             main_inI3_outR3(接收到初始化数据3, 发送响应数据3)
        main_inR3(接收到响应数据3, 完成主模式协商, 建立ISAKMP SA)
        
        由rfc2409 5.1 使用签名方法进行IKE第一阶段认证,可知签名载荷SIG_I在第5个包中发送给对方,对方在第6个包中对签名进行验签,并发送另外一个签名给发起方,最后发起方对收到的签名进行验签。
                5.1 使用签名方法进行IKE第一阶段认证
        
           使用签名方法时,在第二个传输往返中交换的辅助信息是当前时间(nonce);
           交换的认证是通过对一个相互可得到的hash值进行签名。使用签名认证的主模
           式描述如下:   
                发起者                             响应者
               -----------                        -----------
                HDR, SA                     -->
                                            <--    HDR, SA
                HDR, KE, Ni                 -->
                                            <--    HDR, KE, Nr
                HDR*, IDii, [ CERT, ] SIG_I -->
                                            <--    HDR*, IDir, [ CERT, ] SIG_R
三、签名过程
        1,函数调用过程:        
        main_inR2_outI3()
          ->main_mode_hash()
          ->RSA_sign_hash()
                ->sign_hash()
        2,具体分析(只是摘取关键代码片段):
        main_inR2_outI3()
        {
            /* HASH_I or SIG_I out */
            {
            u_char hash_val[MAX_DIGEST_LEN];
            size_t hash_len = main_mode_hash(st, hash_val, TRUE, &id_pbs);//对数据st和id_pbs做摘要(即原始数据msg),摘要值放在hash_val中(即hash1),摘要长度为返回值hash_len。
        
            if (auth_payload == ISAKMP_NEXT_HASH)
            {
                /* HASH_I out */
                if (!out_generic_raw(ISAKMP_NEXT_NONE
                         , &isakmp_hash_desc
                         , &md->rbody
                         , hash_val, hash_len, "HASH_I"))
                return STF_INTERNAL_ERROR;
            }
            else
            {
                /* SIG_I out */
                u_char sig_val[RSA_MAX_OCTETS];
                size_t sig_len = RSA_sign_hash(st->st_connection
                , sig_val, hash_val, hash_len);//对摘要值hash_val(hash1)做签名,签名使用的证书私钥放在st->st_connection中,最终的签名值为sig_val,长度为返回值sig_len.
        
                if (sig_len == 0)
                {
                loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature");
                return STF_FAIL + AUTHENTICATION_FAILED;
                }
        
                if (!out_generic_raw(ISAKMP_NEXT_NONE
                         , &isakmp_signature_desc
                         , &md->rbody
                         , sig_val
                         , "SIG_I"))
                return STF_INTERNAL_ERROR;
            }
            }
        }

        //hash函数
        static size_t   /* length of hash */
        main_mode_hash(struct state *st
        , u_char *hash_val  /* resulting bytes */
        , bool hashi    /* Initiator? */
        , const pb_stream *idpl)    /* ID payload, as PBS; cur must be at end */
        {
            struct hmac_ctx ctx;
        
            hmac_init_chunk(&ctx, st->st_oakley.hasher, st->st_skeyid);
            main_mode_hash_body(st, hashi, idpl, &ctx.hash_ctx, ctx.h->hash_update);
            hmac_final(hash_val, &ctx);
            return ctx.hmac_digest_len;
        }

        //签名函数
        static size_t
        RSA_sign_hash(struct connection *c
        , u_char sig_val[RSA_MAX_OCTETS]
        , const u_char *hash_val, size_t hash_len)
        {
            size_t sz = 0;
            smartcard_t *sc = c->spd.this.sc;
        
            if (sc == NULL)     /* no smartcard */
            {
            const struct RSA_private_key *k = get_RSA_private_key(c);
        
            if (k == NULL)
                return 0;   /* failure: no key to use */
        
            sz = k->pub.k;
            passert(RSA_MIN_OCTETS <= sz && 4 + hash_len < sz && sz <= RSA_MAX_OCTETS);
            sign_hash(k, hash_val, hash_len, sig_val, sz);
            }
                ……
                ……
        }
                
四、验签过程
        1,函数调用关系:
                main_inI3_outR3()
                 ->main_inI3_outR3_tail()
                        ->main_id_and_auth()
                               ->oakley_id_and_auth()
                                  ->decode_peer_id()//使用ca校验对方证书的合法性,先不关注。
                                        ->decode_cert()
                                                ->verify_x509cert()
                               ->RSA_check_signature()//验签
                                      ->take_a_crack()
                                         ->try_RSA_signature()//最终的比较过程
        2,具体分析(摘取关键代码片段):
        static stf_status
        oakley_id_and_auth(struct msg_digest *md
                 , bool initiator   /* are we the Initiator? */
                 , bool aggrmode                /* aggressive mode? */
                 , cont_fn_t cont_fn    /* continuation function */
                 , const struct key_continuation *kc    /* current state, can be NULL */
        )
        {
            struct state *st = md->st;
            u_char hash_val[MAX_DIGEST_LEN];
            size_t hash_len;
            stf_status r = STF_OK;
        
            /* ID Payload in.
             * Note: this may switch the connection being used!
             */
            if (!decode_peer_id(md, initiator, aggrmode))//使用ca校验对方证书的合法性,先不关注。
            return STF_FAIL + INVALID_ID_INFORMATION;
        
            /* Hash the ID Payload.
             * main_mode_hash requires idpl->cur to be at end of payload
             * so we temporarily set if so.
             */
            {
            pb_stream *idpl = &md->chain[ISAKMP_NEXT_ID]->pbs;
            u_int8_t *old_cur = idpl->cur;
        
            idpl->cur = idpl->roof;
            hash_len = main_mode_hash(st, hash_val, !initiator, idpl);//对数据st和id_pbs做摘要(即发送方的原始数据msg),摘要值放在hash_val中(即hash3),摘要长度为返回值hash_len。
            idpl->cur = old_cur;
            }
            switch (st->st_oakley.auth)
            {
            case OAKLEY_PRESHARED_KEY:
            {
                pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs;
        
                if (pbs_left(hash_pbs) != hash_len
                || memcmp(hash_pbs->cur, hash_val, hash_len) != 0)
                {
                DBG_cond_dump(DBG_CRYPT, "received HASH:"
                    , hash_pbs->cur, pbs_left(hash_pbs));
                loglog(RC_LOG_SERIOUS, "received Hash Payload does not match computed value");
                /* XXX Could send notification back */
                r = STF_FAIL + INVALID_HASH_INFORMATION;
                }
            }
            break;
        
            case OAKLEY_RSA_SIG:
            r = RSA_check_signature(st, hash_val, hash_len//hash_val是刚上面计算出来的hash3,这里的st没什么意义,公钥会在函数里取到。
                , &md->chain[ISAKMP_NEXT_SIG]->pbs//pbs中存放发送方发过来的签名值
                );
        ……
        ……
        }

          RSA_check_signature(struct state *st
        , const u_char hash_val[MAX_DIGEST_LEN]
        , size_t hash_len
        , const pb_stream *sig_pbs
        #ifdef USE_KEYRR
        , const struct pubkey_list *keys_from_dns
        #endif /* USE_KEYRR */
        , const struct gw_info *gateways_from_dns
        )
        {
            const struct connection *c = st->st_connection;
            struct tac_state s;
            err_t dns_ugh = NULL;
        
            s.st = st;
            s.hash_val = hash_val;//hash3赋给s
            s.hash_len = hash_len;
            s.sig_pbs = sig_pbs;//签名值赋给s
        
            s.best_ugh = NULL;
            s.tried_cnt = 0;
            s.tn = s.tried;
        
            /* try all gateway records hung off c */
            if ((c->policy & POLICY_OPPO))
            {
            struct gw_info *gw;
        
            for (gw = c->gw_info; gw != NULL; gw = gw->next)
            {
                /* only consider entries that have a key and are for our peer */
                if (gw->gw_key_present
                && same_id(&gw->gw_id, &c->spd.that.id)
                && take_a_crack(&s, gw->key, "key saved from DNS TXT"))
                return STF_OK;
            }
            }
        
            /* try all appropriate Public keys */
            {
            struct pubkey_list *p, **pp;
            int pathlen;
        
            pp = &pubkeys;
            pathlen = pathlen;      /* make sure it used even with !X509 */
        
            {
              char buf[IDTOA_BUF];
        
              DBG(DBG_CONTROL,
                  dntoa_or_null(buf, IDTOA_BUF, c->spd.that.ca, "%any");
                  DBG_log("required CA is '%s'", buf));
            }
        
            for (p = pubkeys; p != NULL; p = *pp)//获取需要使用的公钥,如何获取不再深入。
            {
                struct pubkey *key = p->key;
        
                if (key->alg == PUBKEY_ALG_RSA && same_id(&c->spd.that.id, &key->id)
                && trusted_ca(key->issuer, c->spd.that.ca, &pathlen))//最终匹配到的对方公钥由指针key指向。
                {
                time_t now;
        
                {
                  char buf[IDTOA_BUF];
        
                  DBG(DBG_CONTROL,
                      dntoa_or_null(buf, IDTOA_BUF, key->issuer, "%any");
                      DBG_log("key issuer CA is '%s'", buf));
                }
        
                /* check if found public key has expired */
                time(&now);
                if (key->until_time != UNDEFINED_TIME && key->until_time < now)
                {
                    loglog(RC_LOG_SERIOUS,
                    "cached RSA public key has expired and has been deleted");
                    *pp = free_public_keyentry(p);
                    continue; /* continue with next public key */
                }
        
                if (take_a_crack(&s, key, "preloaded key"))//传入s(包含签名值和hash3)和公钥。
                return STF_OK;
                }
                pp = &p->next;
            }
           }
        ……
        ……
        }

        take_a_crack(struct tac_state *s
        , struct pubkey *kr
        , const char *story USED_BY_DEBUG)
        {
            err_t ugh = try_RSA_signature(s->hash_val, s->hash_len, s->sig_pbs
            , kr, s->st);//s->hash_val为hash3, s->sig_pbs为签名值,kr为公钥。
            const struct RSA_public_key *k = &kr->u.rsa;
        ……
        ……
        }

        static err_t
        try_RSA_signature(const u_char hash_val[MAX_DIGEST_LEN], size_t hash_len
        , const pb_stream *sig_pbs, struct pubkey *kr
        , struct state *st)
        {
                ……
                ……
           /* We have the decoded hash: see if it matches. */
            if (memcmp(hash_val, hash_in_s, hash_len) != 0)//hash_val还是传进来的hash3,没有变化;hash_in_s是由公钥对签名值进行了解签的结果(即hash2),如中解签名不再分析。所以这里比较hash3 == hash2 ? 如果相同,那么验签通过,否则失败。
            {
            DBG_cond_dump(DBG_CRYPT, "decrypted SIG", s, sig_len);
            DBG_cond_dump(DBG_CRYPT, "computed HASH", hash_val, hash_len);
            /* XXX notification: INVALID_HASH_INFORMATION */
            return "9" "authentication failure: received SIG does not match computed HASH, but message is we
            }
        
            unreference_key(&st->st_peer_pubkey);
            st->st_peer_pubkey = reference_key(kr);
        
            return NULL;    /* happy happy */
        }


展开阅读全文

没有更多推荐了,返回首页