java实现asn.1解码_ASN1编解码实现⽅法
第1章概述
1.1背景
系统与充值平台的接⼝是⽂件的⽅式,充值平台将⽂件内容以ASN.1⽅式进⾏编码,系统需要根据ASN.1协议进⾏解码。
关于ASN.1开发的资料,⽹上资料⾮常少,特别是涉及到具体的语⾔,如java,资料、案例及第三⽅库更是少之⼜少。从⽆到有是很困难的,为了防⽌后期其他系统还需要做类似接⼝,将其记录为⽂章以便后查,⽂章会以充值接⼝作为案例进⾏介绍。
1.2ASN.1概念
在和领域,ASN.1(Abstract Syntax Notation one) 是⼀套,是描述的表⽰、编码、传输、解码的灵活的记法。它提供了⼀套正式、⽆歧义和精确的规则以描述独⽴于特定计算机硬件的对象结构。
ASN.1包括⼏个标准化编码规则,如基本编码规则(BER)-X.209、规范编码规则(CER)、识别名编码规则(DER)、压缩编码规则(PER)和XML编码规则(XER)。这些编码规则描述了如何对ASN.1中定义的数值进
⾏编码,以便⽤于传输,⽽不管计算机、编程语⾔或它在应⽤程序中如何表⽰等因素。ASN.1的编码⽅法⽐许多与之相竞争的标记系统更先进,它⽀持可扩展信息快速可靠的传输—在⽆线宽带中,这是⼀种优势。1984年,ASN.1就已经成为了⼀种国际标准,它的编码规则已经成熟并在可靠性和兼容性⽅⾯拥有更丰富的历程。
简洁的⼆进制编码规则(BER、CER、DER、PER,但不包括XER)可当作更现代XML的替代。然⽽,ASN.1⽀持对数据的语义进⾏描述,所以它是⽐XML更为⾼级的语⾔。
ASN.1 的描述可以容易地被映射成C或C++或Java的数据结构,并可以被应⽤程序代码使⽤,并得到运⾏时程序库的⽀持,进⽽能够对编码和解码XML或TLV格式的,或⼀种⾮常紧凑的压缩编码格式的描述。同时,ASN.1也是⼀种⽤于描述结构化实体的结构和内容的语⾔。
如:使⽤ASN.1语法可以这样定义⼀个类:Report ::= SEQUENCE {
author OCTET STRING,
title OCTET STRING,
body OCTET STRING,
biblioINTEGER
}
注:在进⾏ASN1开发前,需要先阅读上述⽂章,了解其中的⼀些基本概念。
1.3TAG
由于TAG在ASN1中⾮常之重要,⽽且在对⽂件进⾏解析时就是因为TAG的问题导致浪费了很多时间,因此这⾥对其单独介绍,不过只是提出概念,详细描述还需参见相关规范。
TAG是对ASN1协议中每个数据域的标识,通过2.2的截图可以看到,每个结点名称后⾯都有⼀个数字,这个就TAG值。TAG有可分为四⼤类:UNIVERSAL、Context、Private、Application。详见:
第2章开发⼯具
通常情况下,如果使⽤该协议进⾏交互,双⽅应该规定出⼀个以ASN.1语法描述的协议⽂件,类似webservice开发中的wsdl,然后各⾃系统使⽤相关⼯具进⾏编解码。
2.1开发库
⽬前⽹上能查到的第三⽅免费⼯具,主要有JavaAsn1Comiler和bouncycastle⼦库:
lJavaAsn1Comiler(JAC.jar):
n该⼯具可根据ASN.1协议描述⽂件,⽣成对应的java类,同时提供的API接⼝⾮常友好,命名概念同理论基本⼀致,使⽤⾮常⽅便,但是前提是必须要有完整的ASN.1描述⽂件,⽽且⾮常重要的⼀点使⽤限制是,该库⽬前⽀持TAG值在0-127之间,即:如果协议中的数据使⽤了超过127的TAG值,则该库⽆法⽀持,不可使⽤(否则会出现编码错误,⽆法解析)。
n该库提供了相当丰富的使⽤案例,可参考其⼯程下的test⽬录。
lbouncycastle(bcprov-jdk16-1.46.jar)
n该⼯具没有提供⾃动⽣成java代码的能⼒,如果要进⾏编解码,则需要⼿动对协议中的类进⾏定义,并且⾃⼰调⽤相应的API实现编解码。使⽤起来较JAC复杂,但是该库对TAG值没有限制,适合⽤在TAG值⼤于127的场景。
n在使⽤该类进⾏解析时,由于没有提供⽅便的API进⾏⾃动解析,因此需要⼿⼯编写解析代码,⽐较郁闷的是其帮助⽂档也没有⽐较详细的案例,最后的解码操作还是通过阅读其ASN1Dump类的实现⽅才完成。
2.2辅助⼯具
由于经ASN.1编码后的⽂件是⼆进制格式,⽆法直接阅读,因此在开发过程中,为了能够⽐较直观的阅读到其编码后的记录,需要借助第三⽅⼯具来查看编码后的⽂件内容。
⽹上有⼏个查看⼯具,但是最⽅便、最直观的⼯具则是ASN1VE 2.1(未注册版有功能限制,只能查看编码后的⽂件),通过该软件可以很轻松的查看到⽂件内容,截图如下:
l⼆进制视图:
lXML视图
第3章JavaAsn1Compiler
3.1定义ASN.1描述⽂件
通过通信双⽅约定的数据格式,使⽤ASN.1语法对其进⾏定义(参见1.2百度⽂库),形成.asn⽂件,如vc.asn。
3.2⽣成java代码
3.2.1代码⽣成
将编写好的.asn⽂件放到JAC.jar⽬录,执⾏:java -jar JAC.jar -d c:/jac_test -pvcvc.asn
参数描述:
-d:⽣成java代码⽂件的保存⽬录;
-p:⽣成java代码的package;
3.2.2代码案例
以下代码可从JavaAsn1Compiler⼯程的test⽬录下获取,ASN1⽂件内容:MiddleSeq ::= SEQUENCE
{
status[22]INTEGER,
location[APPLICATION 11]INTEGER
}
⽣成的java代码如下:importcom.turkcelltech.jac.*;
importcom.chaosinmotion.asn1.Tag;
publicclassMiddleSeqextendsSequence
{
publicASN1Integerstatus=newASN1Integer("status");
publicASN1Integerlocation=newASN1Integer("location");
public
MiddleSeq()
{
super();
setUpElements();
}
public
MiddleSeq(String name)
{
super(name);
setUpElements();
}
protectedvoid
setUpElements()
{
super.addElement(status);
status.setTagClass(Tag.CONTEXT);
status.setTagNumber(22);
super.addElement(location);
location.setTagClass(Tag.APPLICATION);
location.setTagNumber(11);
/* end of element setup */
}
}
3.2.3编解码// 编码
ByteArrayOutputStream outStream =newByteArrayOutputStream(); BerOutputStream out =newBerOutputStream(outStream); MiddleSeqms = newMiddleSeq();
ms.status.setValue(2);
ms.location.setValue(314);
// 解码
ByteArrayInputStreaminputStream;
BerInputStream in;
inputStream =ByteArray());
in =newBerInputStream(inputStream);
MiddleSeqdecode_ms = newMiddleSeq("decode_ms");
decode_ms.decode(in);
System.out.println("ms.status=" + decode_Value());
可见编解码⾮常简单,如果是嵌套结构,只要对最外层对象执⾏encode/decode操作即可。
3.3总结
使⽤该库在有ASN1协议描述⽂件时,开发ASN1编解码⾮常容易,缺点就是不⽀持超过127的TAG值。
第4章bouncycastle
bouncycastle(简称bc)包含了⼀系列的java编解码⼯具,ASN1只是其中的⼀类。在没有ASN1协议描述⽂件的情况下,结合ASN1VE⼯具,可以进⾏相关的编解码开发,云南服务质量管理系统与VC充值平台正是使⽤这种⽅式开发的。
4.1编码
在云南服务质量管理系统中,实际上并没⽤⽤到ASN1编码的知识,但是在从零开始的背景下,为了更好的学习和理解ASN1的编码格式,这⾥便开发了⼀个编码模型。
4.1.1确定编码的⽂件格式
由于没有ASN1⽂件,只有编码后的⽂件,因此需要通过ASN1VE来查看编码后是什么格式,如图所⽰:
通过上图可以看出整个⽂件的组织结构、每个数据域对应的TAG值以及TAG的类型(Application)。但是
ASN1的编码有多种⽅式,如:BER/DER/PER等,bc库提供的API就包含了BER和DER两种类型,为了确定具体的编码格式,利⽤bc库⾃带的ASN1Dump⼯具,可以将该⽂件通过⽂本的⽅式输出出来(略),最后获取的⽅式为DER,下⾯就利⽤bc库提供的API来构造上述的结构。
4.1.2构造ASN1映射类
有了上⾯分析出的结构再结合对⽅提供的word⽂档,即可以定义出⼤概结构,这⾥只给出其中Header的定义,其他可参考具体代码:importjava.io.IOException;
importorg.bouncycastle.asn1.ASN1EncodableVector;
importorg.bouncycastle.asn1.DERApplicationSpecific;
importorg.bouncycastle.asn1.DEREncodable;
importorg.bouncycastle.asn1.DERIA5String;
importorg.bouncycastle.asn1.DERInteger;
publicclassRecordHeaderextendsDERApplicationSpecific
java类的概念
{
publicRecordHeader(booleanexplicit,inttag, DEREncodable object)throwsIOException {
super(explicit, tag, object);
//TODOAuto-generated constructor stub
}
publicRecordHeader(inttagNo, ASN1EncodableVector vec) {
super(tagNo, vec);
}
publicRecordHeader(inttag,byte[] octets) {
super(tag, octets);
//TODOAuto-generated constructor stub
}
publicRecordHeader(inttag, DEREncodable object)throwsIOException {
super(tag, object);
//TODOAuto-generated constructor stub
}
publicstaticRecordHeader createHeader(intrecodeType, String senderCode, String accepterCode, String fileSerialNo, String fileCreateTime,intfileVersionNo)
{
DERInteger d_recodeType =newDERInteger(recodeType);
DERIA5String d_senderCode =newDERIA5String(senderCode);
DERIA5String d_accepterCode =newDERIA5String(accepterCode);
DERIA5String d_fileSerialNo =newDERIA5String(fileSerialNo);
DERIA5String d_fileCreateTime =newDERIA5String(fileCreateTime);
DERInteger d_fileVersionNo =newDERInteger(fileVersionNo);
ASN1EncodableVector vec =newASN1EncodableVector();
try{
vec.add(newDERApplicationSpecific(false, 50, d_recodeType));
vec.add(newDERApplicationSpecific(false, 51, d_senderCode));
vec.add(newDERApplicationSpecific(false, 52, d_accepterCode));
vec.add(newDERApplicationSpecific(false, 53, d_fileSerialNo));
vec.add(newDERApplicationSpecific(false, 54, d_fileCreateTime));
vec.add(newDERApplicationSpecific(false, 55, d_fileVersionNo));
}
catch(IOException e){
e.printStackTrace();
}
RecordHeader header =newRecordHeader(33, vec);
returnheader;
}
}
代码中⼏个重要概念
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论