走读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部分

原文地址:https://www.cnblogs.com/efzju/p/2443345.html