根据ASN.1asn.1 编码规则详解解析智能变电站报文编码并对各个字段加以简要说明。

拒绝访问 | www.bzfxw.com | 百度云加速
请打开cookies.
此网站 (www.bzfxw.com) 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(43ed7-ua98).
重新安装浏览器,或使用别的浏览器ASN.1详解 - 凌峰布衣 - 博客园
随笔 - 80, 文章 - 4, 评论 - 14, 引用 - 0
Chapter 4 ASN.14.1 网络数据表示及编码4.2 ASN.1的基本概念4.3 基本编码规则4.1 网络数据表示及编码ASN.1(Abstract Syntax Notation 1)是一种形式语言。独立与表示层编码技术,提供统一的网络数据表示。管理信息PDU需要一种对数据类型和数据内容进行描述的语法。ASN.1很好地表示出数据的含义。ASN.1提供对传输语法的支持:BER(Basic EncodingRule)。4.2 ASN.1的基本概念4.2.1 文字约定ASN.1区分大小写。跳空格,与布局无关。注释以--开头,以--或行尾结束。标识符由大、小写字母,数字和横线组成,如sysName内部类型标识符全部大写,如BOOLEAN,INTEGER。用户定义的类型名和模块名以大写字母开头,School 。4.2.2 ASN.1符号说明符号 含义::= 定义为| 或-- 后面是注释(行){} 清单的开始和结束[] 标签的开始和结束() 子类型的开始和结束.. 范围4.2.3 抽象数据类型在ASN.1中,每一个数据类型都有一个标签(tag),标签的类型分4种:? 通用标签? 应用标签? 上下文专用标签? 私有标签通用数据类型有20多种,分4大类:?简单类型:由单一成分构成的原子类型。?构造类型:由两种以上成分构成的构造类型。?标签类型:由已知类型定义的新类型。?其他类型:包括CHOICE和ANY两种类型。一、简单类型1INTEGERASN.1中没有限制整型的位数,可以任意大小。e.g. PageNumber::=INTEGERColorType::=INTEGER{red (0),blue (1),green (2)}colorA ColorType::=12 BOOLEAN布尔值。取值为TRUE或FALSE。e.g. Employed::=BOOLEANMarried::=BOOLEANlincoln Married ::= TRUE ,lincoln Married ::= FALSE3 ENUMERATEDe.g. Month::= ENUMERATED {January (1),February (2),March (3),•••December (12) }hottestMonth Month::=July--or 74 REAL实数。ASN.1对实数的精度没有限制。每个实数可以用M×BE表示,即三元组。{M,B,E}e.g. AngleInRadians::=REALangle1 AngleInRadians::={,-7}5 BIT STRING位串类型,由零个或多个比特组成的有序位串。可用二进制和十六进制表示。如: He.g. Occupation::=BIT STRING{clerk (0),editor (1),artist (2),publisher (3) }peter Occupation::={editor,artist}--or "0110B"6 OCTET STRING八位位组串,由0个或多个8位位组组成的有序串。可用十进制(0-255)。二进制和十六进制表示。如:1010B或D11AHe.g. MacAddress::=OCTET STRING(SIZE(6))pc1 MacAddress::= 11A1D001E001H7 OBJECT IDENTIFIER对象标识符。从对象树派生出的一系列点分数字串的形式,用来表示对象。e.g. internet OBJECT IDENTIFIER::={iso(1) org(3) dod(6) 1}directory OBJECT IDENTIFIER::={internet 1}mgmt OBJECT IDENTIFIER::={internet 2}experimental OBJECT IDENTIFIER::={internet 3}private OBJECT IDENTIFIER::={internet 4}8 NULL空值类型,当某时刻无法知道数据的标准值,可将值为NULL。e.g. PatientIdentifier::=SEQUENCE{name Visiblestring --取自IA5的图形字符组成,不含控制字符roomNumber CHOICE{ INTEGERNULL }}patient1 PatientIdentifier::={name “peter”,roomNumber 301}patient2 PatientIdentifier::={name “john”,roomNumber NULL}CHARACTER STRING字符串类型。包括:?NumericString?PrintableString?IA5String?VisibleString?GeneralStringe.g. NumString::= NnmericStringstr1 NumString ::= &#90”Surname ::= PrintableStringperson1 Surname ::= “John”二、构造类型9 SEQUENCE序列类型。是包含零个或多个组成元素的有序列表,列表的不同元素可以分属性不同的数据类型。e.g. AirlineFlight::=SEQUENCE{airline IA5string,--取自IA5的字符组成,它与ASCII码基本相同flight Numericstring, --包含数据0到9以及空格,不包含控制字符集seats SEQUENCE{maximum INTEGER,occupied INTEGER,vacantINTEGER},airport SEQUENCE {origin IA5string,stop1 [0] IA5string OPTIONAL,stop2 [1] IA5string OPTIONAL,destination IA5string },crewsize ENUMERATED {six (6),eight (8),ten (10) },cancle BOOLEAN DEFAULT FALSE }airplane1 AirlineFlight::={airline "china",flight "1106",seats {320,280,40},airport {origin "Beijing", destination"Shanghai"},crewsize 10 }或 ::= {"china", "1106", {320,107,213}, {"Beijing", "Shanghai"},10}10 SEQUENCE OF单纯序列(数组)类型。即序列中的各项都属于是同一类型的ASN.1类型。e.g. Seats::=SEQUENCE OF INTEGER11 SET集合类型。是包含零个或多个组成元素的无序集合,元素顺序无意义,类型可以不相同。e.g. Person::=SET {name IA5string,ageINTEGER,female BOOLEAN}personA Person::={"maggie",4,ture} 、{true, "maggie",4} 、{4,true, " maggie "}12 SET OF单纯集合类型,是包含零个或多个组成元素的无序集合。e.g. Vipseats::=SET OF INTEGERvipseatset Vipseats::={340,342,345}三、标签类型Exuniv::=[UNIVERSAL 2] INTEGERvalA Exuniv ::=9Exappl::=[APPLICATION 0] INTEGERvalB Exappl ::=10Expriv::=[PRIVATE 1] INTEGERvalC Expriv::=11Excont::= SET{ type1 [0] INTEGER OPTIONAL,type2 [1] INTEGER OPTIONAL }四、其他类型13 CHOICE选择类型。包含一个可供选择的数据类型列表。对于类型能够在事先都知道,可用此类型。e.g. Prize::=CHOICE{ car IA5string,cash INTEGER,nothing BOOLEAN }peter Prize::=TRUE 或者John Prize::= “ Lincoln ” 或者Sam Prize::= 2500014 ANY如果在定义数据时还不能确定数据类型,可以使用ANY型。ANY型可以被任何ASN.1类型置换。e.g. TextBook::=SEQUENCE{ author IA5string,reference ANY}实例:book1 TextBook ::= { author "shakespeare ",reference IA5string " ISBN "}book2 TextBook ::= { author "shakespeare ",reference INTEGER 1988}4.2.4 子类型通过对某些类型加以限制,可以定义它们的子类型(subtype)。子类型的值集合是其父类型值集合的子集。(1) 单个值在定义中列举出所有可能的取值,e.g.TestResult ::=INTEGER(1|2|3|4)sp1 TestResult ::= 2(2) 大小限制通过限制父类型中元素的个数定义新类型或规模。e.g. WorkstationNumber::=OCTET STRING(SIZE(6))BitField ::= BIT STRING(SIZE(12))map1 BitField ::= �Bmap2 BitField ::= &#‟H(3) 取值范围(值区间)只适用于整数和实数类型,e.g. NoID::=INTEGER(1..100)PositiveInteger ::=INTEGER(0&..MAX)PositiveInteger ::=INTEGER(1..MAX)(4) 可用字符只用于字符串类型,限制字符集的取值范围。DigitString ::=IA5String(FROM(?0?)|(?1?)|(?2?)|(?3?)|(?4?)|(?5?)|(?6?)|(?7?)|(?8?)|(?9?))str2 DigitString ::= &#”(5) Inner Subtyping (内部类型)适用于SEQUENCE,SEQUENCE OF,SET,SET OF和CHOICE类,主要用于对这些结构类型的元素项进行限制。e.g. PDU::=set{ alpha [0] INTEGER,beta[1] IA5striong OPTIONAL,gamma [2] SEQUENCE OF parameter,delta[3] BOOLEAN }TestPDU::=PDU(WITH COMPONENTS{alpha(min..&0),...,delta(FALSE)})(6) 包含子类型要用到关键字INCLUDES,说明被定义的新子类型包含原子类型的全部可能值。e.g. Months::=ENUMERATED{jaunary(1),february(2),...,december(12)}First-quarter::=Months(january,february,march);Second-quarter::=Months(april,may,june)First-half::=Months (INCLUDES First-quarter|INCLUDE Second-quarter)jan First-quarter ::= 14.2.5 应用类型ASN.1中的应用类型与特定的应用有关,根据网络管理的实际特点,RFC1155定义了6种SNMP应用类型。(1) NetworkAddress::=CHOICE{internet IpAddress} --可从各种网络地址中选择一种,目前只有Internet地址,即IP地址。(2) IpAddress::=[APPLICATION 0] IMPLICIT OCTET STRING(SIZE(4))(3) Counter::= [APPLICATION 1] IMPLICIT INTEGER(0..)(4) Gauge::=[APPLICATION 2] IMPLICIT INTEGER(0..)(5) TimeTicks::=[APPLICATION 3] IMPLICIT INTEGER(0..)(6) Opaque::= [APPLICATION 4] OCTET STRING --arbitrary ASN.1value 不透明类型4.3 基本编码规则用ASN.1语言书写的变量必须转换为串行的字节流才能在网络中传输。为此,ASN.1又提供了基本编码规则(BER)来描述传输过程中内容的表示。约定??八位位组:八比特组成八位位组的二进制位编号从8到1,第8位为最高有效位,第1位位最低有效位编码结构标签八位位组长度八位位组? 标签(tag)字段:关于标签类别和编码格式的信息。? 长度(Length)字段:定义内容字段的长度。? 值(Value)字段:包含实际的数据 。内容八位位组1. 标签字段标签字段对标签类别、标签号和编码格式进行编码。87标签类别654P/C1 1 1 1 121标签号P/C前导八位位组类别3后继八位位组11+1+0+= 标签号e.g. UNIVERSAL 10 编码:00 0 01010APPLICATION 35 (构造类型) 编码:01 1
1000112. 长度字段用来表示值字段的八位位组数。确定格式。(编码时知道长度;长格式和短格式)长度字段分类不确定格式。规则如下:? 若编码是简单类型,则使用确定格式;?若编码是构造的,并且编码立即可用,既可以使用确定格式,也可以使用不确定格式,由发送者选择;?若编码是构造的,但编码不是立即可用,则使用不确定格式。短格式:e.g. L=38 编码:长格式:e.g. L=201 编码: 01001不确定格式:3. 值字段由零个或多个八位位组组成,并按不同类型数据值的不同规定对它们进行编码。(1) 布尔值的编码由1个字节组成。FALSE为00;TRUE为FF。e.g. TRUE的编码:01 01 FFFALSE 的编码:01 01 00(2) 整数编码值字段一个或多个八位位组组成,整数值采用二进制补码形式编码。e.g. 0 编码 02 01 00127 编码 02 01 7F256 编码 02 02 01 00-129 编码 02 02 FF 7F(3) 空值的编码空值的标签 UNIVERSAL 5, 编码 05 00(4) 位串值的编码简单类型构造类型e.g. (0A3B5F291CD)16简单类型编码:03 07 04 0A3B5F291CD0构造类型编码:将位串值拆为(0A3B)16和(5F291CD)1623 8003 03 000A3B03 05 04 5F291CD000 00(5) 对象标识符的编码对象标识符值的编码是简单类型。第一个编码子标识符的数值:(X*40)+Ye.g. 对象标识符 {joint-iso-ccitt 100 2},即{2,100,3}编码:06 03 813403 H(6) 八位位组串和字符串类型值的编码e.g. IA5String类型字符串“ACE” 编码 16 03 41 43 45 H八位位组串“ACE0” 编码 04 02 AC E0 H(7) 序列值的编码序列值的编码是构造类型。e.g. 序列类型{name IAString, ok BOOLEAN},值{name “smith”, okTRUE}编码:30 0A16 05 73 6D 69 74 6801 01 FF(8) 集合值的编码集合类型的元素是无序的,有多种编码。e.g. SET{breadth INTEGER, bent BOOLEAN}, 值{breadth 7, bentFALSE}编码:31 0602 01 0701 01 00也可以是:31 0601 01 0002 01 07Key Point:BERHomework:P89, 1,3,15English References:Mani Subramanian(苏布拉马尼亚 ) ,《Network management : principles and practice》论文发表、论文指导
周一至周五
9:00&22:00
一种基于GCM的智能变电站报文安全传输方法
  【摘 要】变电站作为我国电网中电力中转的重要节点,并担负着电能分配与电压转换的重要任务,而变电站能否准确、可靠与安全的运行,对整个电网的安全与稳定都有着重要的影响。如何在新形势下保障智能变电站的报文传输安全,也成为了电力工作者所急需解决的一项重要课题。本文结合实际工作经验,从智能变电站的报文安全机制出发,并提出和设计了一种基于GCM的智能变电站报文安全传输方法。 中国论文网 /8/view-4911385.htm  【关键词】GCM;智能变电站;报文;安全传输   MMS、SMV和GOOSE作为智能变电站常用的三种报文,在IEC61850标准中对这三种报文的长度、内容、传输时间以及安全性的复杂程度等多个方面均作出了不同的相应规定。而如果每一种报文都对于一种加密传输算法,则必然会增加智能变电站中网络设备的负担,并影响到其互操作性。基于GCM的认证加密算法具有易于实现、加密和解密速度快,以及无专利等多个优点,尤其是具有认证、认证加密和明文发送这三种不同的加密模式,能够充分满足当前智能变电站对不同报文传输的安全需要。   1 智能变电站通信安全机制及GCM算法概述   1.1 智能变电站通信安全机制   IEC61850标准将MMS、SMV和GOOSE报文分别映射在不同的通信链路中,由于这三种报文的允许传输时间,PDU大小等方面都有各自不同的特点,标准建议通过不同的安全措施来实现不同报文的处理。   对于安全性和传输实时性要求较高的GOOSE报文,标准提倡采用认证而非加密的方法以确保其完整性,当然该标准并不否定加密,只要求能够满足4ms最低要求且适宜的加密算法也是可行的;对于主要是一些测量数据的SMV报文,由于其数据量大,而重要性相对较小,因此可不进行加密措施,但要实施完整的检测;对于作为跨变电站内部与外部网络的MMS报文,由于其非常容易被入侵者利用,因此要求必须具备很高的安全性,并应通过认证与加密的双重保障。   1.2 GCM算法概述   认证加密算法是当前较为主流的一种安全技术,能够同时实现数据加密与数据认证这两种安全措施。当前GCM算法的吞吐率已然突破了100Gbps,而且最高吞吐量已达到了162.56Gbps。GCM算法因其高安全性、高速率、无专利以及能适用于数据链路层加密等多个方面的优点,使其在安全技术方面得到了广泛的关注与应用。GCM算法的定义如下:   GCM加密函数:   GCM解密函数:   2 基于GCM算法的安全报文模型设计   本文在保留了IEC62351扩展报文格式优点的基础上,对适合GCM算法处理的报文模型进行了设计,如下图1所示。通过重新定义而保留了字段1,并将所保留的字段分为了A和B两个部分,分别表示扩展字段的长度以及加密模式,其中每一个部分都各自占有一个字节。从而通过对A、B两个不同部分的组合,来确定报文是否需要加密以及确定采用加密的模式。   图1 基于GCM算法的安全报文结构   在设计中,若当A部分为0而B部分取任意值时,该报文即为原始报文,可不采取任何的认证与加密措施,而进行明文发送,其中GCM算法选择NULL模式;若A部分为20,则表示该报文为扩展报文,此时B部分定义为0或者255,则分别表示扩展报文选择GCM认证模式或者认证加密模式,如B部分为两者之间则为预留字段。通过这样的定义设计,也方便了对智能变电站中不同报文的分类处理,提高了传输的灵活性与安全性。   3 基于GCM的智能变电站报文安全传输的算法设计   3.1 加密算法设计   步骤一:在发送端组织好报文,并将其组装为APDU,经过变电站表示层和应用层的ASN.1编码处理后传递到数据链路层。   步骤二:通过Set_ReservedFied(A,B)分别将A部分和B部分设置为20和255,并通过对Get_SeqNum函数将APDU中的SeqNum的属性值赋值给SN。   步骤三:对报文的安全类型和模式的选择,以此判断该报文为扩展报文,且需要经过认证和加密处理。在加密处理之前应首先对该报文进行扩展,并添加MAC头部字段、扩展字段以及优先权字段,并计算赋值给A部分。   步骤四:调用GCM加密函数对该报文进行加密。加密过程中首先通过Step1和Step2对向量参数H和P进行初始化;Step3进行报文的预处理;Step4、Step5和Step6对每段明文进行加密后再输出密文段;Step7调用GHASH函数的计算验证码,并选择最高位中的16位码作为认证码T;Step8返回认证码T和密文段,并将其添加进相应的字段中。   步骤五:计算CRC值并将其填充入保留字段2中,再发送报文,加密算法结束。   3.2 解密算法设计   步骤一:首先进行CRC值的检查,如果没有错误再进行其余变量的初始化工作。   步骤二:通过Get_InitialVal中所获得的报文各字段的值,将优先权字段和头部字段的值赋值给A部分,保留字段的值分别赋值给A和B部分,MAC字段赋值给T,SN字段赋值给P,密文数据赋值给M。   步骤三:对报文的安全类型和模式进行选择,判断该报文为扩展报文,需选择认证和加密模式。   步骤四:调用GCM解密函数进行解密运算,在解密之前应先进行MAC判断,若相等则证明报文未被篡改,具有完整性,然后再进行相应的解密操作。   步骤五:若解密成果,则将明文提交到相应的接受设备中进行内部安全处理。若解密失败或MAC不相等,则表明该报文已经被篡改,应当丢弃,则解密算法结束。   4 基于GCM的智能变电站报文安全传输方法的优点分析   (1)对于GOOSE类报文,可利用GCM算法中的GMAC模式,将该类报文分为需要安全保护的报文和不需要安全保护的报文,只系统的检查消息内的完整性与来源的正确性。这样不仅提高了数据传输的安全性,有节省了时间,减少了网络流量。   (2)对于电能质量和计量类的SMV报文,由于其数据流量大,也可采取与GOOSE类相同的模式,以减轻网络负担,减少网络延时。   (3)对于变电站内外网络交互的MMS报文,由于其安全性要求很高,为加强报文的完整性与机密性,可选择加密模式中的GCM模式,同时实现报文的完整性和机密性检测。   5 总结   本文针对智能变电站的特殊需求环境,设计了一种基于GCM的智能变电站报文安全传输算法,并就该方法的优点进行了分析与介绍,证明了其能够充分满足智能变电站报文传输在安全性和实时性上的要求。   参考文献:   [1]宋磊,罗其亮,等.电力系统实施数据通信加密方案[J].电力系统自动化,2004(14).   [2]吴文玲,冯登国,等.分组密码的设计与分析[M].北京:清华大学出版社,2009.
转载请注明来源。原文地址:
【xzbu】郑重声明:本网站资源、信息来源于网络,完全免费共享,仅供学习和研究使用,版权和著作权归原作者所有,如有不愿意被转载的情况,请通知我们删除已转载的信息。
xzbu发布此信息目的在于传播更多信息,与本网站立场无关。xzbu不保证该信息(包括但不限于文字、数据及图表)准确性、真实性、完整性等。我不会编程,但也不是一点都不会,我稍微会一些 :-)
asn1编码格式的解析过程
本文以x509的解析为例说明asn1的编码格式的解析逻辑。x509证书的解析实际上是asn1格式的解析,这里着重说的是asn1的ber编码的解析,总的来讲,asn1格式的解析过程有三个重要的元素,一个是asn1数据本身,一个是openssl的内部数据结构,比如X509_st,还有一个指导asn1数据往内部数据结构填充的结构体,这个过程实际上就是d2i,而反向的过程就是i2d,asn1作为抽象语法标记语言,ber其实只是其一种编码实现,不管什么实现都要体现“抽象数据结构”本身,这种数据结构其实很显然,标记了类型,标记了数据的长度以及数据本身等,每一种标记都是自解释的,因此最完美的解决方案就是将openssl的内部数据结构也规整为前面所说的这种“抽象数据结构”,也即是说,将内部数据结构作为asn1的另一种编码格式,和ber并列的编码格式,这样就很好理解d2i的过程了,同样i2d是一种和d2i并列的转换,这么理解的话,ber编码和内部编码实质上是同一种意思的两种不同表述,以x509为例,如果现在有一个x509的der格式的数字证书,有一个openssl的x509数据结构,以下的两个数据结构就是这个d2i过程的指导结构:
ASN1_ITEM_st表述一个“项”,这个“项”是一种复合结构,templates是一个ASN1_ITEM_st容器,该容器可以容纳一个ASN1_ITEM_st也可以容纳多个ASN1_ITEM_st,多个ASN1_ITEM_st同样以templates为更低一级的容器,实质上在我们的例子中,x509_cinf结构体就是一个ASN1_ITEM_st,其中包含一系列的ASN1_TEMPLATE,这个一会会谈到:
struct ASN1_ITEM_st {
/* The item type, primitive, SEQUENCE, CHOICE or extern */
/* underlying type */
const ASN1_TEMPLATE *
//用于itype是SEQUENCE的情况,而一个templates又要包含一个ASN1_ITEM,注释0
/* Number of templates if SEQUENCE or CHOICE */
const void *
/* functions that handle this type */
//被描述的内部结构体的大小
ASN1_TEMPLATE是ASN1_ITEM的内容容器,真正的内容还是ASN1_ITEM,由于ASN1是一个大的嵌套体,所以每个ASN1_ITEM还可以包含别的ASN1_ITEM,这些ITEM通过TEMPLATE进行汇总,也就是说一个TEMPLATE可以容纳很多存在于TEMPLATE中的ITEM:
struct ASN1_TEMPLATE_st {
//指示一些特殊用途,比如变长结构或者是可选信息等
//asn.1的tag
//该TEMPLATE在被描述结构体的偏移
ASN1_ITEM_EXP *
//每个TEMPLATE由一个ASN1_ITEM描述,此字段指向该ITEM
以下几个#define是几个帮助宏,学过c语言的人都能看懂:
#define ASN1_ITEM_start(itname) /
const ASN1_ITEM * itname##_it(void) /
static const ASN1_ITEM local_it = { /
#define ASN1_ITEM_end(itname) /
return &local_ /
#define ASN1_SEQUENCE_ref(tname, cb, lck) /
static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_REFCOUNT, offsetof(tname, references), lck, cb, 0}; /
ASN1_SEQUENCE(tname)
#define ASN1_SEQUENCE(tname) /
static const ASN1_TEMPLATE tname##_seq_tt[]
ASN1_SEQUENCE_ref其实就是表示一系列的ASN1_TEMPLATE,也就是一个ASN1_TEMPLAT类型的数组,这些数组就是上面的注释0;
ASN1_SEQUENCE_ref(X509, x509_cb, CRYPTO_LOCK_X509) = { //这里仅仅讲x509,而没有说x509_cinf,但是原理一样。注释1
ASN1_SIMPLE(X509, cert_info, X509_CINF),
ASN1_SIMPLE(X509, sig_alg, X509_ALGOR),
ASN1_SIMPLE(X509, signature, ASN1_BIT_STRING)
} ASN1_SEQUENCE_END_ref(X509, X509)
#define ASN1_SEQUENCE_END_ref(stname, tname) /
//上面ASN1_TEMPLATE数组定义的结束,这种用法“真的很奇妙”
ASN1_ITEM_start(tname) /
ASN1_ITYPE_SEQUENCE,/
V_ASN1_SEQUENCE,/
tname##_seq_tt,/
//这就是那个ASN1_TEMPLATE数组,到此为止,这个数组是已经定义过的,就在注释1处被定义
sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),/
&tname##_aux,/
sizeof(stname),/
ASN1_ITEM_end(tname)
下面的几个宏更能体现出来item和template的意义,openssl中最最难理解的恐怕就是这些宏了,比windows api的宏还可怕,不同的是,windows的api宏主要是为了兼顾兼容性,而openssl的宏主要是为了迎合asn1的简洁结构和实现上的高效,如此复杂的d2i过程在openssl中调用过程竟然没有太深的调用层次,可谓精妙!
#define ASN1_EXP_EX(stname, field, type, tag, ex) /
ASN1_EX_TYPE(ASN1_TFLG_EXPLICIT | ex, tag, stname, field, type)
#define ASN1_SIMPLE(stname, field, type)
ASN1_EX_TYPE(0,0, stname, field, type)
#define ASN1_EXP_SEQUENCE_OF_OPT(stname, field, type, tag) /
ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL)
#define ASN1_EXP_EX(stname, field, type, tag, ex) /
ASN1_EX_TYPE(ASN1_TFLG_EXPLICIT | ex, tag, stname, field, type)
下面的这个宏最终定义了一个ASN1_TEMPLATE,可以看到最后一个字段是ASN1_ITEM_ref(type),实际上ASN1_ITEM_ref也是一个宏,它得到了一个ASN1_ITEM,也就是这个ASN1_TEMPLATE容纳的一个ASN1_ITEM:
#define ASN1_EX_TYPE(flags, tag, stname, field, type) { /
(flags), (tag), offsetof(stname, field),/
#field, ASN1_ITEM_ref(type) }
#define ASN1_ITEM_ref(iptr) (&(iptr##_it))
理解了上面的宏和过程之后,接下来看看具体的d2i过程的函数调用过程:
ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it)
//初始化一个context,这个context可以保存中间的状态
ASN1_VALUE *ptmpval = NULL;
if (!pval)
c.valid = 0;
if (ASN1_item_ex_d2i(pval, in, len, it, -1, 0, 0, &c) & 0)
return NULL;
所以说,openssl中的d2i的过程实际上是很简单的,无非就是将具体数据的传输语法在抽象语法的指导下转换为实际语法,上面的例子中只是说明了如何转换为openssl的c语言的内部结构体的过程,但是并不仅仅有这么一种方式,如果你使用的是java,那么应该还有另一套类似的d2i/i2d的代码,转换过程中,i可能有很多,比如c语言的结构体,java语言的类等等,d也可能有很多种(注意仅仅是广义的说,侠义的说,d仅仅指der),不变的2(to)本身,它就是一套指导方案,其实就是asn规范本身,指的就是抽象语法本身,在转换过程中,传输语法和实际语法之间保持高度的一致性,因此出现那么多的递归也就不足为奇了。
最后看一些openssl中具体代码,这些代码我认为是很值得体会的:
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
ASN1_TLC *ctx)
int flags,
const unsigned char *p, *q;
flags = tt-&
aclass = flags & ASN1_TFLG_TAG_CLASS;
if (flags & ASN1_TFLG_SK_MASK) {
//如果该项是一个可变大小的数组,比如x509的扩展项
int sktag,
ret = asn1_check_tlen(&len, NULL, NULL, &sk_eoc, NULL,
&p, len, sktag, skaclass, opt, ctx);
else if (ret == -1)
return -1;
if (!*val)
*val = (ASN1_VALUE *)sk_new_null();
while(len & 0) {
ASN1_VALUE *
skfield = NULL;
if (!ASN1_item_ex_d2i(&skfield, &p, len,
ASN1_ITEM_ptr(tt-&item),
-1, 0, 0, ctx))
len -= p -
if (!sk_push((STACK *)*val, (char *)skfield)) //val本身就是一个STACK_OF类型的数据结构,是一个变长的内存区域,容纳很多可选项
ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt-&item),
-1, 0, opt, ctx);
else if (ret == -1)
return -1;
int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
const ASN1_ITEM *it,
int tag, int aclass, char opt, ASN1_TLC *ctx)
switch(it-&itype) {
case ASN1_ITYPE_PRIMITIVE:
... //下面的这个asn1_d2i_ex_primitive真正实现了数据的分配和指派
return asn1_d2i_ex_primitive(pval, in, len, it,....);
在asn1_check_tlen的调用路径中存在下列代码:
for (i = 0, tt = it-& i & it-& i++, tt++)
const ASN1_TEMPLATE *
ASN1_VALUE **
seqtt = asn1_do_adb(pval, tt, 1);
pseqval = asn1_get_field_ptr(pval, seqtt); //由tt中的offset计算要读取数据在接收结构体里面的偏移
else isopt = (char)(seqtt-&flags & ASN1_TFLG_OPTIONAL);
ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx);
else if (ret == -1) {
ASN1_template_free(pseqval, seqtt);
len -= p -
asn1_template_ex_d2i中调用的asn1_check_tlen中可以确定可选项是否存在,形参中ASN1_TLC *ctx是个很重要的参数,它在整个解析过程中起作用,在解析的开始,也就是ASN1_item_d2i中作为局部变量初始化,然后一直到该函数返回后销毁,这个参数在解析过程中存储一些中间状态和临时变量,但凡一些繁琐的冗长的复杂的操作都要有类似的结构体,在linux内核中释放内存时用到的struct scan_control也是类似的结构。在ssl握手的过程中,为了重用消息,或者说为了处理可选消息,用到了struct ssl3_state_st中的tmp字段:
if (s-&s3-&tmp.message_type == SSL3_MT_SERVER_DONE) { //本次读取的消息和需要读取的不对应,那么暂存起来,下次就不再读取了,而是用这次的。
s-&s3-&tmp.reuse_message=1;
return(1);
然后在具体的读取消息函数的最开始:
if (s-&s3-&tmp.reuse_message) {
s-&s3-&tmp.reuse_message=0;
s-&init_msg = s-&init_buf-&data + 4; //重用了上次读取但是没有使用的消息
s-&init_num = (int)s-&s3-&tmp.message_
return s-&init_
ASN1_TLC *ctx的用途之一也是这样子的,在asn1_check_tlen中:
if (ctx && ctx-&valid) {
//上次读到了,但是没有使用,比如上次是在解析可选项,然而该可选项不存在,于是返回了-1,把读取的结果暂存起来供以后使用(*)
plen = ctx-&
pclass = ctx-&
ptag = ctx-&
p += ctx-&
i = ASN1_get_object(&p, &plen, &ptag, &pclass, len);
if (ctx) { //保存信息,以便供上面的(*)处使用
ctx-&valid = 1;
if (exptag &= 0) {
if ((exptag != ptag) || (expclass != pclass)) {
if (opt) return -1;
asn1_tlc_clear(ctx);
ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_WRONG_TAG);
asn1_tlc_clear(ctx); //清除ctx-&valid,代表此次读取的信息被使用了
Asn.1格式文件流方式解析
asn1 学习笔记
openssl 代码分析(4) ASN1_item_ex_d2i
asn1编码的理解
13.openssl编程——ASN1库
没有更多推荐了,}

我要回帖

更多关于 固定资产编码规则 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信