OpenSSL 1.1.1g C API解析ASN.1 CHOICE时d2i返回空问题
问题根因
- 核心问题违反ASN.1基本规则:CHOICE类型要求所有备选分支必须具备全局唯一的标签(Tag),d2i解码器完全依赖二进制数据中的标签值判断匹配分支,不会做失败回溯尝试。
- 你定义的两个CHOICE分支都是
SEQUENCE OF类型,默认外层标签都是通用构造类SEQUENCE(标签值0x30),标签完全重复。OpenSSL处理CHOICE时会按模板定义顺序从上到下匹配标签,只要标签命中就直接用对应分支模板解码,根本不会走到后续分支:- 把string分支放在模板第一位时,解码obj分支编码的数据,会尝试按
SEQUENCE OF OCTET STRING规则解析,发现内部元素是Obj_t对应的SEQUENCE类型,和OCTET STRING类型不匹配,直接返回空指针。 - 你调换分支顺序测试时,没有同步修改编码阶段给
choice->type赋的索引值:OpenSSL默认用分支在模板里的顺序索引(从0开始计数)作为type字段的判定依据,调换顺序后obj分支的索引从1变成0,你还是给type赋值1,会导致i2d编码阶段就选错模板,生成的二进制数据本身格式错误,解码自然失败。
- 把string分支放在模板第一位时,解码obj分支编码的数据,会尝试按
- 测试代码存在独立逻辑bug:两次往栈中push同一个对象指针,会导致后续释放内存时触发double free,该问题不影响本次解码结果,但也需要修复。
修复方法
- 给CHOICE的两个分支加上唯一的上下文类显式标签,从根源解决标签冲突问题,这也是符合ASN.1标准的写法。修改CHOICE模板定义如下:
这里用ASN1_CHOICE(Choice_t) = { ASN1_EXP_SEQUENCE_OF(Choice_t, d.string, ASN1_OCTET_STRING, 0), ASN1_EXP_SEQUENCE_OF(Choice_t, d.obj, Obj_t, 1) }ASN1_CHOICE_END(Choice_t)ASN1_EXP_SEQUENCE_OF替代原来的ASN1_SEQUENCE_OF,给两个分支分别加上值为0、1的显式上下文标签,编码后两个分支的外层标签会变成全局唯一的0xa0、0xa1,解码器可以直接靠标签区分分支,解码后会自动给Choice_t的type字段赋值对应标签值0或1,不需要手动调整索引匹配模板顺序。 - 修复测试代码的重复指针问题,每次往栈中压入元素时都生成新的独立对象:case 0分支要压入两个字符串时,分别调用
ASN1_STRING_new()生成两个独立的ASN1_STRING对象再push;case 1分支压入两个Obj_t时同理,分别调用Obj_t_new()生成两个独立对象,避免重复释放内存。 - 校验方法:编码后打印二进制缓冲区首字节,string分支首字节为0xa0、obj分支首字节为0xa1即表示标签添加正确,此时d2i解码即可正常返回正确结构体,type字段和联合体指针都会被正确填充。
内容的提问来源于stack exchange,提问作者Yinxf
相关产品推荐
相关产品推荐

