i2d_ASN1_OBJECT 问题

i2d_ASN1_OBJECT issue

我正在尝试使用 openssl 库将 ASN1_OBJECT 编码为 DER。 https://www.openssl.org/docs/man1.1.0/crypto/i2d_X509.html 中记录了一个函数 i2d_ASN1_OBJECT,还有一个示例代码确实有效(对于另一种对象类型):

unsigned char *smder = NULL;
int smderlen, r;
smderlen = i2d_X509_ALGORS(algs, &smder);
if (smderlen <= 0)
    return 0;

其中 algs 已预填充 STACK OF X509_ALGORs。我的 ASN1_OBJECT 代码:

unsigned char *ctder = NULL;
int ctderlen, r;
ASN1_OBJECT* obj = OBJ_txt2obj("1.2.3.4", 1);
ctderlen = i2d_ASN1_OBJECT(obj, &ctder);  // SEGFAULT here
if (ctderlen <= 0)
    return 0;

我遇到了段错误。

更新

这是一个错误,现在已修复(参见:https://github.com/openssl/openssl/issues/6914

您参考的手册页提到

If *ppout is NULL memory will be allocated for a buffer and the encoded data written to it.

但是,您使用的函数 i2d_ASN1_OBJECT() 并非如此。查看 the source code of that function in a_object.c, no allocation anywhere. The segfault happens here, one level deeper in ASN1_put_object.

因为它没有给你分配内存,所以你必须自己提供一个缓冲区,像这样:

unsigned char ctderarray[64];
unsigned char *ctder = ctderarray;
int ctderlen, r;
ASN1_OBJECT* obj = OBJ_txt2obj("1.2.3.4", 1);
ctderlen = i2d_ASN1_OBJECT(obj, &ctder);
if (ctderlen <= 0) {
    return 0;
} else {
    printf("ctderlen = %d\n", ctderlen);
}

将打印值 5。另一个不为您分配的函数示例是 i2d_ASN1_BOOLEAN(),如果您尝试将它与 NULL out 指针一起使用,它确实也会崩溃。

那么为什么文档与您的经验相矛盾,为什么它适用于您的其他代码片段?文档不准确,但大多数其他 i2d_XYZ() 函数都是由 the function asn1_item_flags_i2d() 通过(复杂的)模板方案以通用方式实现的。在那里,分配确实为您发生了:

/*
 * Encode an ASN1 item, this is use by the standard 'i2d' function. 'out'
 * points to a buffer to output the data to. The new i2d has one additional
 * feature. If the output buffer is NULL (i.e. *out == NULL) then a buffer is
 * allocated and populated with the encoding.
 */

static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
                               const ASN1_ITEM *it, int flags)
{
    if (out && !*out) {
        unsigned char *p, *buf;
        int len;
        len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags);
        if (len <= 0)
            return len;
        buf = OPENSSL_malloc(len);
        if (!buf)
            return -1;
        p = buf;
        ASN1_item_ex_i2d(&val, &p, it, -1, flags);
        *out = buf;
        return len;
    }

    return ASN1_item_ex_i2d(&val, out, it, -1, flags);
}

它被一些模板化函数调用(间接)here:

# define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
        stname *d2i_##fname(stname **a, const unsigned char **in, long len) \
        { \
                return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\
        } \
        int i2d_##fname(stname *a, unsigned char **out) \
        { \
                return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\
        }

只要您使用的 i2d_XYZ 函数是通过此模板机制定义的(其中大多数是),该函数就会为您进行分配。否则,你将不得不自己做。