ASN.1编解码:asn1c的基本使用

ASN.1编解码:asn1c的基本使用


荣涛
2021年8月23日


上篇主要讲了ASN.1编码《ASN.1编解码与编程
本文主要介绍 ASN.1 编码的C语言实现,asn1c。ASN.1 在通信领域非常关键。

1. ASN.1 基础

如果你想在计算机之间传输一个标准数据格式,这个数据格式中包含字符串tag,整形,浮点型,那么这个结构就可以用 ASN.1 来描述,如下:

CertainStructure ::= SEQUENCE {
    tag     VisibleString,
    val1    INTEGER,
    val2    INTEGER   OPTIONAL,
    reals   SET OF REAL
}

用 asn1c 进行编译,即可生成 C语言代码:

typedef struct CertainStructure {
    VisibleString_t  tag;
    long             val1;
    long            *val2;        /* OPTIONAL */
    A_SET_OF(double) reals;
} CertainStructure_t;

上述变量可能被直接修改。

重要的是,这个复杂的数据格式与二进制格式之间的相互转化是比较复杂的。(快照snap)

下面展示上面的数据结构通过网络接收、编辑和转化编码的过程:

CertainStructure_t *cs = 0;
ber_decode(0, &asn_DEF_CertainStructure, &cs, buffer, buffer_length);
cs->val1 = 123;        /* Modify the contents */
der_encode(&asn_DEF_CertainStructure, cs, write_handle, 0);

write_handle的意思是asn1c编译器的使用提示。

下面就可以直接转化为 xml格式:

xer_fprint(stdout, &asn_DEF_CertainStructure, cs);

上面的代码的输出结果为:

<CertainStructure>
    <tag>This is a random tag</tag>
    <val1>123</val1>
    <reals>
        <REAL>3.14159265</REAL>
        <REAL><MINUS-INFINITY/></REAL>
        <REAL>2.7182818284</REAL>
    </reals>
</CertainStructure>

ASN.1 被用作 通信行业,如果需要使用 SSL(HTTPS)来访问银行和邮件账号,确保 ASN.1 也参与其中。

2. ASN.1 编译器

asn1c 是一个免费的将 ASN.1 结构转化为C语言格式的编译器。它支持 ASN.1 语法,包括: ISO/IEC/ITU ASN.1 1988, '94, '97, 2002 以及更新的版本。支持的编码规则为:

  • BER: ITU-T Rec. X.690 | ISO/IEC 8825-1 (2002) (BER/DER/CER)
  • PER: X.691|8825-2 (2002) (PER).
  • XER: X.693|8825-3 (2001) (BASIC-XER/CXER).

在这里插入图片描述

License:

/*-
 * Copyright (c) 2003-2013 Lev Walkin <vlm@lionet.info>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

3. 文档

http://lionet.info/asn1c/documentation.html

文档请按需下载:

在这里插入图片描述

4. F&Q

http://lionet.info/asn1c/faq.html

在这里插入图片描述

5. 示例

这一章节,给出一个简单的编码示例,从 ASN.1 格式到 C语言代码,然后,再生成xml格式。

5.1. 编写 ASN.1 文件

假定我们已经有了MyModule.asn1文件:

MyModule DEFINITIONS ::=
BEGIN

MyTypes ::= SEQUENCE {
    myObjectId   OBJECT IDENTIFIER,
    mySeqOf      SEQUENCE OF MyInt,
    myBitString  BIT STRING {
            muxToken(0), 
            modemToken(1)
     }
}

MyInt ::= INTEGER (0..65535)

END

5.2. 用asn1c指令进行编译

asn1c MyModule.asn1

期间会生成几个文件,同时,会拷贝一些文件

[rongtao@localhost demo]$ asn1c MyModule.asn1 
Compiled MyTypes.c
Compiled MyTypes.h
Compiled MyInt.c
Compiled MyInt.h
Copied /usr/local/share/asn1c/INTEGER.h	-> INTEGER.h
Copied /usr/local/share/asn1c/NativeEnumerated.h	-> NativeEnumerated.h
Copied /usr/local/share/asn1c/INTEGER.c	-> INTEGER.c
Copied /usr/local/share/asn1c/NativeEnumerated.c	-> NativeEnumerated.c
Copied /usr/local/share/asn1c/NativeInteger.h	-> NativeInteger.h
Copied /usr/local/share/asn1c/NativeInteger.c	-> NativeInteger.c
Copied /usr/local/share/asn1c/OBJECT_IDENTIFIER.h	-> OBJECT_IDENTIFIER.h
Copied /usr/local/share/asn1c/OBJECT_IDENTIFIER.c	-> OBJECT_IDENTIFIER.c
Copied /usr/local/share/asn1c/asn_SEQUENCE_OF.h	-> asn_SEQUENCE_OF.h
Copied /usr/local/share/asn1c/asn_SEQUENCE_OF.c	-> asn_SEQUENCE_OF.c
Copied /usr/local/share/asn1c/asn_SET_OF.h	-> asn_SET_OF.h
Copied /usr/local/share/asn1c/asn_SET_OF.c	-> asn_SET_OF.c
Copied /usr/local/share/asn1c/constr_SEQUENCE.h	-> constr_SEQUENCE.h
Copied /usr/local/share/asn1c/constr_SEQUENCE.c	-> constr_SEQUENCE.c
Copied /usr/local/share/asn1c/constr_SEQUENCE_OF.h	-> constr_SEQUENCE_OF.h
Copied /usr/local/share/asn1c/constr_SEQUENCE_OF.c	-> constr_SEQUENCE_OF.c
Copied /usr/local/share/asn1c/constr_SET_OF.h	-> constr_SET_OF.h
Copied /usr/local/share/asn1c/constr_SET_OF.c	-> constr_SET_OF.c
Copied /usr/local/share/asn1c/asn_application.h	-> asn_application.h
Copied /usr/local/share/asn1c/asn_system.h	-> asn_system.h
Copied /usr/local/share/asn1c/asn_codecs.h	-> asn_codecs.h
Copied /usr/local/share/asn1c/asn_internal.h	-> asn_internal.h
Copied /usr/local/share/asn1c/OCTET_STRING.h	-> OCTET_STRING.h
Copied /usr/local/share/asn1c/OCTET_STRING.c	-> OCTET_STRING.c
Copied /usr/local/share/asn1c/BIT_STRING.h	-> BIT_STRING.h
Copied /usr/local/share/asn1c/BIT_STRING.c	-> BIT_STRING.c
Copied /usr/local/share/asn1c/asn_codecs_prim.c	-> asn_codecs_prim.c
Copied /usr/local/share/asn1c/asn_codecs_prim.h	-> asn_codecs_prim.h
Copied /usr/local/share/asn1c/ber_tlv_length.h	-> ber_tlv_length.h
Copied /usr/local/share/asn1c/ber_tlv_length.c	-> ber_tlv_length.c
Copied /usr/local/share/asn1c/ber_tlv_tag.h	-> ber_tlv_tag.h
Copied /usr/local/share/asn1c/ber_tlv_tag.c	-> ber_tlv_tag.c
Copied /usr/local/share/asn1c/ber_decoder.h	-> ber_decoder.h
Copied /usr/local/share/asn1c/ber_decoder.c	-> ber_decoder.c
Copied /usr/local/share/asn1c/der_encoder.h	-> der_encoder.h
Copied /usr/local/share/asn1c/der_encoder.c	-> der_encoder.c
Copied /usr/local/share/asn1c/constr_TYPE.h	-> constr_TYPE.h
Copied /usr/local/share/asn1c/constr_TYPE.c	-> constr_TYPE.c
Copied /usr/local/share/asn1c/constraints.h	-> constraints.h
Copied /usr/local/share/asn1c/constraints.c	-> constraints.c
Copied /usr/local/share/asn1c/xer_support.h	-> xer_support.h
Copied /usr/local/share/asn1c/xer_support.c	-> xer_support.c
Copied /usr/local/share/asn1c/xer_decoder.h	-> xer_decoder.h
Copied /usr/local/share/asn1c/xer_decoder.c	-> xer_decoder.c
Copied /usr/local/share/asn1c/xer_encoder.h	-> xer_encoder.h
Copied /usr/local/share/asn1c/xer_encoder.c	-> xer_encoder.c
Copied /usr/local/share/asn1c/per_support.h	-> per_support.h
Copied /usr/local/share/asn1c/per_support.c	-> per_support.c
Copied /usr/local/share/asn1c/per_decoder.h	-> per_decoder.h
Copied /usr/local/share/asn1c/per_decoder.c	-> per_decoder.c
Copied /usr/local/share/asn1c/per_encoder.h	-> per_encoder.h
Copied /usr/local/share/asn1c/per_encoder.c	-> per_encoder.c
Copied /usr/local/share/asn1c/per_opentype.h	-> per_opentype.h
Copied /usr/local/share/asn1c/per_opentype.c	-> per_opentype.c
Copied /usr/local/share/asn1c/converter-sample.c	-> converter-sample.c
Generated Makefile.am.sample

从上面的输出结果可以看出,生成了一些文件:

Compiled MyTypes.c
Compiled MyTypes.h
Compiled MyInt.c
Compiled MyInt.h
Generated Makefile.am.sample

同时,也会拷贝一些文件:

Copied /usr/local/share/asn1c/per_encoder.c	-> per_encoder.c

asn1c 为啥做成这种拷贝文件的格式,我不知道,我感觉可以做成 asn1c-devel 形式,编译成动态库来做,可能相对来说对项目代码量没有那么大。

5.3. 分析数据结构

5.3.1. MyInt

首先从简单的开始看:

MyInt ::= INTEGER (0..65535)

然我们查看 MyInt.h 头文件,看下数据类型:

/* MyInt */
typedef long	 MyInt_t;

此外,还有一些函数指针:

/* Implementation */
extern asn_TYPE_descriptor_t asn_DEF_MyInt;
asn_struct_free_f MyInt_free;
asn_struct_print_f MyInt_print;
asn_constr_check_f MyInt_constraint;
ber_type_decoder_f MyInt_decode_ber;
der_type_encoder_f MyInt_encode_der;
xer_type_decoder_f MyInt_decode_xer;
xer_type_encoder_f MyInt_encode_xer;

到这里,是不是和net-snmp 的使用很像,我觉得有异曲同工之妙。

然后,我们看c文件,该文件中实现了上述中头文件的 方法,截取一部分:

asn_enc_rval_t
MyInt_encode_der(asn_TYPE_descriptor_t *td,
		void *structure, int tag_mode, ber_tlv_tag_t tag,
		asn_app_consume_bytes_f *cb, void *app_key) {
	MyInt_1_inherit_TYPE_descriptor(td);
	return td->der_encoder(td, structure, tag_mode, tag, cb, app_key);
}

asn_dec_rval_t
MyInt_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
		void **structure, const char *opt_mname, const void *bufptr, size_t size) {
	MyInt_1_inherit_TYPE_descriptor(td);
	return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size);
}

asn_enc_rval_t
MyInt_encode_xer(asn_TYPE_descriptor_t *td, void *structure,
		int ilevel, enum xer_encoder_flags_e flags,
		asn_app_consume_bytes_f *cb, void *app_key) {
	MyInt_1_inherit_TYPE_descriptor(td);
	return td->xer_encoder(td, structure, ilevel, flags, cb, app_key);
}

一个正向还是比较简单的,下面看比较复杂的是数据结构。

5.3.2. MyTypes

MyType 的 ASN.1 定义为:

MyTypes ::= SEQUENCE {
    myObjectId   OBJECT IDENTIFIER,
    mySeqOf      SEQUENCE OF MyInt,
    myBitString  BIT STRING {
                        muxToken(0), 
                        modemToken(1)
                 }
}

首先看一下C语言结构定义:

/* Dependencies */
typedef enum myBitString {
	myBitString_muxToken	= 0,
	myBitString_modemToken	= 1
} e_myBitString;

/* MyTypes */
typedef struct MyTypes {
	OBJECT_IDENTIFIER_t	 myObjectId;
	struct mySeqOf {
		A_SEQUENCE_OF(MyInt_t) list;
		
		/* Context for parsing across buffer boundaries */
		asn_struct_ctx_t _asn_ctx;
	} mySeqOf;
	BIT_STRING_t	 myBitString;
	
	/* Context for parsing across buffer boundaries */
	asn_struct_ctx_t _asn_ctx;
} MyTypes_t;

上述数据结构中,_asn_ctx用来管理,我们在编写过程中不能显式对其进行修改。其他标签,均和ASN.1 格式匹配。

5.4. 编写asn程序

#include <stdio.h>	/* for stdout */
#include <stdlib.h>	/* for malloc() */
#include <assert.h>	/* for run-time control */
#include "MyTypes.h"	/* Include MyTypes definition */

int main() {
	/* Define an OBJECT IDENTIFIER value */
	int oid[] = { 1, 3, 6, 1, 4, 1, 9363, 1, 5, 0 }; /* or whatever */

	/* Declare a pointer to a new instance of MyTypes type */
	MyTypes_t *myType;
	/* Declare a pointer to a MyInt type */
	MyInt_t *myInt;

	/* Temporary return value */
	int ret;

	/* Allocate an instance of MyTypes */
	myType = calloc(1, sizeof *myType);
	assert(myType);	/* Assume infinite memory */

	/*
	 * Fill in myObjectId
	 */
	ret = OBJECT_IDENTIFIER_set_arcs(&myType->myObjectId,
			oid, sizeof(oid[0]), sizeof(oid) / sizeof(oid[0]));
	assert(ret == 0);

	/*
	 * Fill in mySeqOf with a couple of integers.
	 */

	/* Prepare a certain INTEGER */
	myInt = calloc(1, sizeof *myInt);
	assert(myInt);
	*myInt = 123;	/* Set integer value */

	/* Fill in mySeqOf with the prepared INTEGER */
	ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
	assert(ret == 0);

	/* Prepare another integer */
	myInt = calloc(1, sizeof *myInt);
	assert(myInt);
	*myInt = 111222333;	/* Set integer value */

	/* Append another INTEGER into mySeqOf */
	ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
	assert(ret == 0);

	/*
	 * Fill in myBitString
	 */

	/* Allocate some space for bitmask */
	myType->myBitString.buf = calloc(1, 1);
	assert(myType->myBitString.buf);
	myType->myBitString.size = 1;	/* 1 byte */

	/* Set the value of muxToken */
	myType->myBitString.buf[0] |= 1 << (7 - myBitString_muxToken);

	/* Also set the value of modemToken */
	myType->myBitString.buf[0] |= 1 << (7 - myBitString_modemToken);

	/* Trim unused bits (optional) */
	myType->myBitString.bits_unused = 6;

	/*
	 * Print the resulting structure as XER (XML)
	 */
	xer_fprint(stdout, &asn_DEF_MyTypes, myType);

	return 0;
}

因为上面的编译 ASN.1 语法的时候,生成了一个 Makefile.am.sample 文件,实际上,main主程序在拷贝的文件 converter-sample.c中 ,为了编译方便,我把上面的文件内容放入了文件converter-sample.c中,并进行编译执行:

[rongtao@localhost demo]$ make -f Makefile.am.sample
[rongtao@localhost demo]$ ./progname
<MyTypes>
    <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId>
    <mySeqOf>
        <MyInt>123</MyInt>
        <MyInt>111222333</MyInt>
    </mySeqOf>
    <myBitString>
        11
    </myBitString>
</MyTypes>

5.5. 从xer文件中取数据

我将上面的文本保存到文件MyType.xer中:

[rongtao@localhost demo]$ cat MyType.xer 
<MyTypes>
    <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId>
    <mySeqOf>
        <MyInt>123</MyInt>
        <MyInt>111222333</MyInt>
    </mySeqOf>
    <myBitString>
        11
    </myBitString>
</MyTypes>

采用上面的编译步骤,运行下面的程序,源代码如下:

#include <stdio.h>	/* for stdout */
#include <stdlib.h>	/* for malloc() */
#include <assert.h>	/* for run-time control */
#include "MyTypes.h"	/* Include MyTypes definition */

int main(int argc, char *argv[]) {
	char buf[1024];	/* Hope, sufficiently large buffer */
	MyTypes_t *myType = 0;
	asn_dec_rval_t rval;
	char *filename;
	size_t size;
	FILE *f;

	/*
	 * Target variables.
	 */
	int *oid_array;	/* holds myObjectId */
	int oid_size;
	int *int_array;	/* holds mySeqOf */
	int int_size;
	int muxToken_set;	/* holds single bit */
	int modemToken_set;	/* holds single bit */

	/*
	 * Read in the input file.
	 */
	assert(argc == 2);
	filename = argv[1];
	f = fopen(filename, "r");
	assert(f);
	size = fread(buf, 1, sizeof buf, f);
	if(size == 0 || size == sizeof buf) {
		fprintf(stderr, "%s: Too large input\n", filename);
		exit(1);
	}

	/*
	 * Decode the XER buffer.
	 */
	rval = xer_decode(0, &asn_DEF_MyTypes, &myType, buf, size);
	assert(rval.code == RC_OK);

	/*
	 * Convert the OBJECT IDENTIFIER into oid_array/oid_size pair.
	 */
	/* Figure out the number of arcs inside OBJECT IDENTIFIER */
	oid_size = OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
			0, sizeof(oid_array[0]), 0);
	assert(oid_size >= 0);
	/* Create the array of arcs and fill it in */
	oid_array = malloc(oid_size * sizeof(oid_array[0]));
	assert(oid_array);
	(void)OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
			oid_array, sizeof(oid_array[0]), oid_size);

	/*
	 * Convert the sequence of integers into array of integers.
	 */
	int_size = myType->mySeqOf.list.count;
	int_array = malloc(int_size * sizeof(int_array[0]));
	assert(int_array);
	for(int_size = 0; int_size < myType->mySeqOf.list.count; int_size++)
		int_array[int_size] = *myType->mySeqOf.list.array[int_size];

	if(myType->myBitString.buf) {
		muxToken_set   = myType->myBitString.buf[0]
					& (1 << (7 - myBitString_muxToken));
		modemToken_set = myType->myBitString.buf[0]
					& (1 << (7 - myBitString_modemToken));
	} else {
		muxToken_set = modemToken_set = 0;	/* Nothing is set */
	}

    
	/*
	 * Print the resulting structure as XER (XML)
	 */
	xer_fprint(stdout, &asn_DEF_MyTypes, myType);

	return 0;
}

和原网址的唯一区别在于,我在 main 程序的末尾添加了 打印的代码:

/*
 * Print the resulting structure as XER (XML)
 */
xer_fprint(stdout, &asn_DEF_MyTypes, myType);

我们运行上述程序,得出:

[rongtao@localhost demo]$ ./progname MyType.xer 
<MyTypes>
    <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId>
    <mySeqOf>
        <MyInt>123</MyInt>
        <MyInt>111222333</MyInt>
    </mySeqOf>
    <myBitString>
        11
    </myBitString>
</MyTypes>

6. 参考链接

  1. http://lionet.info/asn1c/basics.html
  2. http://lionet.info/asn1c/examples.html
  3. http://lionet.info/asn1c/download.html
摘 要 I ABSTRACT II 第1章 绪论 1 1.1 ASN.1概述 1 1.2 ASN.1系列标准 1 1.3 ASN.1编解码的应用与发展前景 2 1.4论文章节结构组织 2 第2章 ASN.1词汇及词法约定 3 2.1 字符集 3 2.2 词项 3 2.3 本章小结 4 第3章 ASN.1数据类型定义 5 3.1 简单数据类型 5 3.1.1 布尔类型 5 3.1.2 空类型 5 3.1.3 整数类型 6 3.1.4 枚举类型 6 3.1.5 实数类型 7 3.1.6 位串类型 7 3.1.7 八位位串类型 8 3.2构造类型 8 3.2.1 序列类型 8 3.2.2 单一序列类型 9 3.2.3 集合类型 10 3.2.4 单一集合类型 10 3.3 本章小结 11 第4章 ASN.1BER编码规则 12 4.1 ASN.1BER数据值的编码结构 12 4.1.1标识符八位位组 12 4.1.2长度八位位组 13 4.1.3内容八位位组 14 4.1.4内容结束八位位组 14 4.2简单数据类型的编码 14 4.2.1 布尔值的编码 14 4.2.2 空值的编码 14 4.2.3 整数值的编码 15 4.2.4 枚举值的编码 16 4.2.5 实数值的编码 16 4.2.6 位串值的编码 18 4.2.7 八位位串值的编码 18 4.3 构造类型的编码 18 4.3.1 序列值的编码 18 4.3.2 集合值的编码 19 4.3.3 单一序列值的编码 19 4.3.4 单一集合值的编码 20 4.4本章小结 20 第5章 基于ASN.1BER规则的解码实现 21 5.1 布尔值的解码 21 5.2 空值的解码 23 5.3 整数值的解码 24 5.4 枚举值的解码 27 5.5 位串值的解码 27 5.6 八位位串值的解码 29 5.7 实数值的解码 30 5.8 构造类型解码 32 5.8.1 序列值的解码 32 5.8.2 单一序列值的解码 35 5.8.3 集合值的解码 35 5.8.4 单一集合的解码 35 5.9 解码系统设计流程图 37 5.10本章小结 38 第6章 基于ASN.1 BER规约的可视化编解码系统及通信 39 6.1 ASN.1BER可视化编解码系统 39 6.2编解码系统通信的实现 40 6.3编解码系统的测试 41 6.4本章小结 43 第7章 总结 44 7.1主要内容回顾 44 7.2本次设计的不足和进一步完善 44 致谢 46 参考文献 47 附录A 外文翻译-原文部分 48 附录B 外文翻译-中文译文 56
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值