ASN.1语法以及在python中如何编码解码

ASN.1语法以及在python中如何编码解码,第1张

ASN.1的文章本来是免费的,但是有的人一定要搞小动作,咱惹不起就只能躲着了,做了一些修改后,改为付费文章

应用程序在网络协议的应用层对payload数据,多使用ASN.1标准进行处理

ASN.1

ASN.1

Abstract Syntax Notation One,抽象语法标记,ASN.1是描述数据格式的标准方法, 它不管语言是如何执行、这些数据具体指什么、用什么类型的编码规则 ,是一种抽象的语法

ASN.1由两部分组成:

一部分键唤描述信息内数据,数据类型及序列格式

另一部分描述如何将各部分组成消息

语法

例如

Report ::= SEQUENCE {

author OCTET STRING,

title OCTET STRING,

body OCTET STRING,

biblio Bibliography

Report是结构体名称

SEQUENCE表示消息是由许多数据单元构成的

中括号{}里面是各种类型的数据单元

前三个数据单元author/title/body的类型是OCTET STRING

最后一个数据单元biblio的类型是另一个ASN.1结构体

Bibliography ::= SEQUENCE {

author OCTET STRING

title OCTET STRING

publisher OCTET STRING

year OCTET STRING

数据类型

类型含义

NULL空

BOOLEAN布尔类型

INTEGER整型

REAL实数类型

BIT STRING比特串

OCTEC STRING字节串

OBJECT IDENTIFIER实体标识符

ENUMERATED枚举类型

SEQUENCE序列

SEQUENCE OF类型的序列返知

SET集合

SET OF类型的集合

CHOICECHOICE类型

...STRING(有很多就不一一列举)字符串类型

UTCTime时间类型

GeneralizedTime时间类型

ASN.1文件结构

例如

Foo DEFINITIONS ::= BEGIN

    Question ::= SEQUENCE {

        id        INTEGER,

        question  IA5String

    }

    Answer ::= SEQUENCE {

        id        INTEGER,

        answer    BOOLEAN

    }

END 

解析上面的ASN.1文件的结构

ASN.1支持的漏亮消编码规则

基本编码规则(BER)

规范编码规则(CER)

识别名编码规则(DER)

压缩编码规则(PER)

XML编码规则(XER)

python如何对ASN.1结构的信息编码和解码

如果我们想用python对信息进行编码,需要明确几点:

提供的ASN.1格式的数据

更多内容,请参考公-中-号, 汽车网络诊断通信

ASN.1 – Abstract Syntax Notation dot one,抽象记法1,在大部分的书写场合会简写为ASN1。它描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序。

ASN1有很多实现版本,OpenSSL主要采用DER格式。

本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。

ASN1相关的头文件为asn1.h、asn1t.h、源文件在crypto/asn1目录中。

这个结芦轮神构负责管理ANS1大多数数据类型的内存组织方式,字段含义:

length —— 管理的数据长度。

type —— 管理的数据类型。

data —— 数据指针。

flags —— 标志位,跟具体数据类型有关。

管理的数据类型:

主要数据类型的取值:

其它一些类型的定义:

在OpenSSL的实现中,每个数据类型都有一个ITEM结构,它负责定义这个数据类型的编解码规则。

相关字段含义如下:

itype —— 定义ITEM自身的类型,取值:

utype —— 定义管理的数据类型,取值前面有说明,例如V_ASN1_INTEGER。

templates —— 数据处理方法模板数组指针。

tcount —— 模板数组的个数。

funcs —— 回调方法指针。

size —— 负责数据结构的大小。

sname —— 指向数据结构的名称。

前面讲到,对每个数据类型来说,OpenSSL需要一个对应的ITEM结构,下面这几个宏为每个数据类型声明ITEM结构,同时也声明了相应的助记函数。如下:

拿 DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER) 来说,宏定义展开形式为:

还有几个宏负责为数据类型实现相应的助记函数。如下:

拿 IMPLEMENT_ASN1_ITEM(ASN1_INTEGER) 来说,其展开形式为:

可以看到,展开形式所实现的助记函数实际上是对ASN1内部几个函数的封装调用,对提供上层数据类型与内部类型的转换,其中ASN1_VALUE是类型上下转换的纽带,它是个万能指针:

typedef struct ASN1_VALUE_st ASN1_VALUE

在asn1.h中,对基本数据类型声明了助记函数:

可是我暂时还没有找到对基本数据类型助记函数的宏实现。

OpenSSL的ASN1编码处理依赖ASN1_ITEM对象,对基本类型来桐郑说,OpenSSL本身已经做了声明和初使化,所以可以直接以相关函数中使用。然而对于自定义结构体,需要开发者来定义ASN1_ITEM对陪亏象和规则,有一些宏可以提供帮助,摘录部分如下:

在ASN1_ITEM结构中,ASN1_TEMPLATE的结构定义如下:

主要字段含义:

offset —— 字段在父结构中的偏移。

field_name —— 字段名称。

item —— 指向字段对应结构类型的ASN1_ITEM对象。

举例定义如下的自定义结构:

将宏展开,为:

ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it)

void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)

根据ITEM对象创建和释放对应的数据类型。

ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_ITEM *it)

根据ITEM对象将DER编码转换为二进制结构。

成功返回有效指针,成功返回NULL。

如果val为NULL,将内部分配内存,但必须由外部使用者释放。

其内部实现为:

可以看到内部做了为val传入NULL的兼容性处理。

int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)

根据ITEM对象将二进制结构转换为DER编码。

成功返回DER编码长度,失败返回负数。

如果*out为NULL,将内部分配内存,但内存必须由外部使用者释放。若**out为NULL,将仅返回长度,这用于试探长度的场景。

其内部实现为:

可以看到内部做了为out和*out传入NULL的兼容性处理。在out传入NULL的情况下,无法接收缓冲区,只能返回长度。

void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)

根据ITEM对象将DER编码转换为二进制结构,输入的DER编码来源于BIO对象。

成功返回有效指针,成功返回NULL。

事实上,本函数是对ASN1_item_d2i()的封装调用。

其内部实现为:

void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)

ASN1_item_d2i_bio()的FILE版本。

ASN1_item_d2i_bio()和ASN1_item_d2i_fp() 这两个函数关于输出参数x的类型定义是不恰当的,后面讲的 ASN1_d2i_bio()和ASN1_d2i_bio() 有修复这个问题。

int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)

根据ITEM对象将二进制结构转换为DER编码,输出到BIO对象中。

成功返回DER编码长度,失败返回负数。

如果*out为NULL,将内部分配内存,但内存必须由外部使用者释放。事实上,本函数是对ASN1_item_i2d()的封装调用。

其内部实现为:

int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)

ASN1_item_i2d_bio()的FILE版本。

其实上,以上函数由于直接需要ASN1_ITEM做为参数,很少在程序中直接使用,而是由DECLARE_ASN1_FUNCTIONS宏声明的变体,或者下面介绍的函数代替。

void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)

从BIO对象读取DER编码数据,转换为对应的二进制结构,具体的转换依赖d2i函数指针。

成功返回有效指针,失改返回NULL。

其内部实现为:

void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)

ASN1_d2i_bio()的FILE版本。

int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, unsigned char *x)

将二进制结构转换为DER编码,输出到BIO中,具体的转换依赖i2d函数指针。

成功返回1,失败返回0。

其内部实现为:

int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)

ASN1_i2d_bio() 的FILE版本。

上面d2i_of_void 和 i2d_of_void 函数的签名为:

通常为数据类型的转换助记函数。

int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)

将v值设置到a中。成功返回1,失败返回0。

long ASN1_INTEGER_get(const ASN1_INTEGER *a)

获取a中存储的整数值。失败返回-1。

int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)

将v值设置到a中。成功返回1,失败返回0。

long ASN1_ENUMERATED_get(const ASN1_ENUMERATED *a)

获取a中存储的枚举值。失败返回-1。

int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len)

将data和len指向的串值设置到str中。成功返回1,失败返回0。

unsigned char *ASN1_STRING_data(ASN1_STRING *x)

获取x的字符串首指针。成功返回有效指针,失败返回NULL。

int ASN1_STRING_length(const ASN1_STRING *x)

获取x的字符串长度。

int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a)

将ASN1_INTEGER转换为ASC码,输出到bp中。成功返回1,失败返回0。

int a2i_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_INTEGER,buf存放BIO的ASC码。成功返回1,失败返回0。

int i2a_ASN1_ENUMERATED(BIO *bp, const ASN1_ENUMERATED *a)

将ASN1_ENUMERATED转换为ASC码,输出到bp中。成功返回1,失败返回0。

int a2i_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_ENUMERATED,buf存放BIO的ASC码。成功返回1,失败返回0。

int i2a_ASN1_STRING(BIO *bp, const ASN1_STRING *a, int type)

将a中的字符串转换为ASC码输出到bp中,type不起作用。返回转换的ASC字符串长度。

int a2i_ASN1_STRING(BIO *bp, ASN1_STRING *bs, char *buf, int size)

将bp中的ASC码转换为ASN1_STRING,buf存放BIO的ASC码。成功返回1,失败返回0。

ASN1_OBJECT *ASN1_OBJECT_new(void)

分配OID对象。

void ASN1_OBJECT_free(ASN1_OBJECT *a)

释放OID对象。

OID的编码规为:第一个八位组采用公式:first_arc* 40+second_arc。如果一个数大于127,就采用多个8位表示,最高位用1表示后续还有octet,用0表示后续没有。成功返回OID编码的字节数。

int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)

计算由ASC字符串指定的OID的DER编码。返回编码的字节数。所果事先需要知道编码的长度来分配内存,可以设置out为NULL,olen为0,获取编码字节长度,根据该长度再去分配内存。

ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp, long length)

将DER编码转换为OID对象。成功返回有效指针。

int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)

将OID对象换转为DER编码。返回DER编码的长度。

int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)

将DER编码转换为ASC字符串,结果输出到bp中。返回ASC字符串长度。

下面这个例子测试了很多函数的用法,请自行放开注释并测试。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12464855.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-25
下一篇 2023-05-25

发表评论

登录后才能评论

评论列表(0条)

保存