c++如何解析ASN.1编码格式的数据包_DER/BER解码【深度】

浅浩君_7960

浅浩君_7960

2026-04-13

492人浏览

原创

直接解析ASN.1 DER/BER易崩溃,因TLV结构复杂:Tag可多字节(如0x1F开头),Length分短/长格式(如0x81 0xFF表示255),新手硬memcpy必越界;须用OpenSSL d2i_*或asn1c生成代码。

c++如何解析asn.1编码格式的数据包_der/ber解码【深度】

为什么直接用 libasn1 或手写解析器会崩溃?

因为 ASN.1 的 DER/BER 编码不是“扁平字节流”,而是嵌套的 TLV(Tag-Length-Value)结构,且长度字段可能为不定长(BER 允许)或定长(DER 强制),更麻烦的是:Tag 可能是多字节(如 0x1F 开头的扩展 tag),Length 可能是短格式(单字节,低 7 位表示长度)或长格式(首字节高 7 位非零,后 N 字节表示长度)。很多 C++ 新手一上来就 memcpy 固定偏移,结果在遇到 0x81 0xFF(表示长度 255)或 0x1F 0x80 0x01(扩展 tag + 长度)时直接越界读取。

实操建议:

  • 别自己解析 TLV 头——用成熟库的底层解码器,比如 OpenSSL 的 d2i_*() 系列函数,或 asn1c 生成的 C 代码(它内部已处理所有 TLV 变体)
  • 若必须手动解析,先检查第一个字节:tag = buf[0];若 (tag & 0x1F) == 0x1F,则 tag 至少两字节,需循环读直到某字节 & 0x80 == 0
  • 长度解析必须分两步:读 len_byte = buf[1],若 len_byte & 0x80 == 0,长度就是 len_byte;否则,取低 7 位 n = len_byte & 0x7F,再读后续 n 字节拼成大端整数

asn1c 生成的 C++ 代码为什么编译不过?

asn1c 默认生成纯 C 代码(.c/.h),没有 C++ namespace、RAII 或异常支持。直接 #include 到 C++ 工程里,常见报错包括:error: 'struct A_SEQUENCE_OF' has no member named 'list'(因 C++ 模板名冲突)、undefined reference to `asn_DEF_MyStruct'(链接时未加 -lasn1 或未定义 ASN_DISABLE_OER 宏)。

实操建议:

  • 生成时加参数:asn1c -fcompound-names -gen-PER -no-gen-OER -D ./out my.asn1,其中 -fcompound-names 避免结构体名冲突,-no-gen-OER 减少依赖
  • C++ 中包含前加 extern "C":
    extern "C" {
    #include "MyStruct.h"
    #include "MyStruct.c"
    }
  • 链接时确保 -I./out -L./out -lasn1,且 MyStruct.c 必须参与编译(不能只靠头文件)

OpenSSL 的 d2i_X509() 能不能用来解任意 ASN.1 结构?

不能。d2i_X509()、d2i_RSAPrivateKey() 这类函数是 OpenSSL 为**特定 RFC 定义结构**硬编码的解码器,它们内部调用 ASN1_item_d2i() 并传入对应 ASN1_ITEM,但该 ASN1_ITEM 是 OpenSSL 内部静态定义的,不对外开放自定义。你拿一个自定义 ASN.1 模块(比如 MyProtocol DEFINITIONS ::= BEGIN ... END)的 DER 数据喂给 d2i_X509(),大概率触发 ASN1_R_HEADER_TOO_LONG 或直接返回 NULL。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

实操建议:

  • 如果只是解析 X.509、PKCS#8、CMS 等标准结构,用 OpenSSL 是最稳的——d2i_X509_bio() 支持 BIO 流式读取,比内存全载更安全
  • 如果要解析私有 ASN.1 协议,必须走 asn1c 或 libtasn1;libtasn1 提供 asn1_der_decoding() + asn1_parser2tree(),但需先用 asn1Parser 工具把 ASN.1 文件转成 .asn 树描述文件
  • 别试图用 OpenSSL 的 ASN1_item_unpack() 解非标准结构——它要求输入已是 ASN1_STRING* 且类型已知,而你连类型都还没从 DER 里抽出来

BER 和 DER 解码结果不一致?

BER 允许更多编码自由度:长度可用不定长形式(0x80 开始,以 0x00 0x00 结束),字符串可分段(UTF8String 分片),甚至允许无用的填充字节;DER 则强制唯一编码(canonical form):长度必须最短表示、禁止不定长、禁止分片、布尔值必须为 0xFF 或 0x00。所以同一个逻辑结构,BER 编码可能有多种字节表现,而 DER 只有一种。如果你用 BER 解码器去读 DER 数据,通常没问题;但反过来——用 DER 解码器(如某些硬件模块)读 BER 数据,会在遇到 0x80 长度标记时直接报错 ASN1_R_UNKNOWN_LENGTH。

实操建议:

  • 通信协议明确写 “BER encoded” 就别用 openssl asn1parse -der 去调试——改用 -strparse 手动剥层,或换 libtasn1 的 asn1Coding 工具
  • 服务端若要求 DER,客户端发 BER 会被拒;反之,服务端接受 BER 时,需确保解码器开启 ASN1_TFLG_BER_UNIVERSAL 标志(libtasn1 默认开,OpenSSL 的 ASN1_item_d2i() 不支持动态切 BER/DER 模式)
  • 调试时用 xxd -p data.der | fold -w2 | paste -sd' ' 看十六进制流,重点盯 Tag 字节是否以 1f 开头、Length 字节是否以 8 开头——这是 BER 特征

实际项目里最耗时间的往往不是解码本身,而是 ASN.1 模块定义和生成代码之间的映射断层:比如 .asn1 文件里写了 MySeq SEQUENCE { a INTEGER, b OCTET STRING },生成的 C 结构体里 b 是 struct asn_TYPE_descriptor_s * 类型,而不是 uint8_t*,得靠 asn_encode_to_buffer() 反向验证才能确认字段位置对不对。这种隐式契约,文档极少提,只能靠反复跑 asn1c -S 查生成规则。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

6799

3

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

2024.03.01

1698

6

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

1009

5

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.25

3140

5

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.03

1818

5

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

2884

5

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6204

49

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

2023.11.24

6009

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
LLVM Programmer’s Manual
LLVM Programmer’s Manual

共0课时 | 0人学习

Fitten Code官方手册
Fitten Code官方手册

共0课时 | 0人学习

CodeGeex使用手册
CodeGeex使用手册

共0课时 | 0人学习