zoukankan      html  css  js  c++  java
  • 走读OpenSSL代码从一张奇怪的证书说起(五)

    上次我们停在
    inl = ASN1_item_i2d(asn, &buf_in, it);
    调用处(注意是第2次中断),此时的调用栈为
    > openssl.exe!ASN1_item_verify
      openssl.exe!X509_verify
      openssl.exe!internal_verify
      openssl.exe!X509_verify_cert
      openssl.exe!check
      openssl.exe!verify_main
      openssl.exe!do_cmd
      openssl.exe!main

    我们目标锁定在函数唯一的入参asn上,只有它才可能引起返回参数错误
    查看上一层函数栈,代码上下文如下

    1 int X509_verify(X509 *a, EVP_PKEY *r)
    2  {
    3  return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
    4   a->signature,a->cert_info,r));
    5  }

    原来asn是参数a->cert_info,其类型为指向X509_CINF *的指针
    在VC自动变量查看窗口,查看a->cert_info的当前值,如下

    这里简单介绍下,cert_info 是指向结构X509_CINF(x509_cinf_st)的指针,而 X509_CINF 是 OpenSSL 内部表示证书信息的数据结构
    上图中cert_info展开的成员表示证书的某个属性,比如subject表示当前证书持有者的身份信息,等等

    再看serialNumber这个成员,它是asn1_string_st*类型,表示证书的序列号,见下面数据结构定义

    typedef struct x509_cinf_st
        {
        ASN1_INTEGER *version;        /* [ 0 ] default of v1 */
        ASN1_INTEGER *serialNumber;
        X509_ALGOR *signature;
        X509_NAME *issuer;
        X509_VAL *validity;
        X509_NAME *subject;
        X509_PUBKEY *key;
        ASN1_BIT_STRING *issuerUID;        /* [ 1 ] optional in v2 */
        ASN1_BIT_STRING *subjectUID;        /* [ 2 ] optional in v2 */
        STACK_OF(X509_EXTENSION) *extensions;    /* [ 3 ] optional in v3 */
        } X509_CINF;
    
    typedef struct asn1_string_st ASN1_INTEGER;
    
    typedef struct asn1_string_st
        {
        int length;
        int type;
        unsigned char *data;
        /* The value of the following field depends on the type being
         * held.  It is mostly being used for BIT_STRING so if the
         * input data has a non-zero 'unused bits' value, it will be
         * handled correctly */
        long flags;
        } ASN1_STRING;

    当前我们看到证书序列号长度为8(见length成员),内容(由data指出)如下
    0x006053c0  00 a2 42 4a a2 6a 51 df [cd cd fd fd fd fd ab ab] -- []中的内容不是序列号部分
    刚好比证书中的序列号 00 00 a2 42 4a a2 6a 51 df 少一个0x00字节

    现在基本可以肯定,正是这里被截断了一个字节,造成后面的一系列错误并最终导致证书验证不过
    我们可以做个实验,来验证 serialNumber 的 data 内容不对是造成后面错误的原因

    当程序第2次中断在 inl = ASN1_item_i2d(asn, &buf_in, it); 调用处时,我们将内存中序列号的内容临时修改为正确的值
       00 a2 42 4a a2 6a 51 df
    改为
    00 00 a2 42 4a a2 6a 51 df

    F10继续执行,再在 int main(int Argc, char *Argv[]) 中的第8行(注意是显示行)设断点

     1     /* ok, now check that there are not arguments, if there are,
     2      * run with them, shifting the ssleay off the front */
     3     if (Argc != 1)
     4         {
     5         Argc--;
     6         Argv++;
     7         ret=do_cmd(prog,Argc,Argv);
     8         if (ret < 0) ret=0;
     9         goto end;
    10         }

    按F5全速前进,屏幕上终于打出久违的
    openssl.cert.verify.error.pem: OK

    这说明,到此为止我们的猜测都是正确

    剩下的任务很简单,就是追踪为什么serialNumber记录的序列号出错
    而正是从这里开始,我们将进入证书解析Asn1parse命令的Kernel部分

  • 相关阅读:
    watir-webdriver使用过程中异常
    分治算法
    回溯算法
    线性时间求取第 K 大数
    深度和广度优先搜索算法
    排序优化——模拟栈及三路快排
    排序算法之——归并排序和快速排序
    数据结构和算法之——算法复杂度分析
    LeetCode 10——正则表达式匹配
    线性代数之——复数矩阵
  • 原文地址:https://www.cnblogs.com/efzju/p/2443345.html
Copyright © 2011-2022 走看看