第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);

ms.encode(out);

// 解码

ByteArrayInputStreaminputStream;

BerInputStream in;

inputStream =newByteArrayInputStream(outStream.toByteArray());

in =newBerInputStream(inputStream);

MiddleSeqdecode_ms = newMiddleSeq("decode_ms");

decode_ms.decode(in);

System.out.println("ms.status=" + decode_ms.status.getValue());

可见编解码非常简单,如果是嵌套结构,只要对最外层对象执行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

{

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;

}

}

代码中几个重要概念

l类定义:理论上一个类的定义应该是一个SEQUENCE,但是通过ASN1Dump出来的数据显示其并不是一个Sequence,而只是一个普通的结点,因此这里不能继承DERSequence,否则编码出的文件将会在Application的上或者下多出一个Sequence结点。

lDERApplicationSpecific:这个是表示构造Application类型Tag的类,如果协议中没有规定Tag类型,默认的可以使用DERTaggedObject来定义节点。(说明:而在JAC库中,如果要定义TAG的类型,实际上只要调用一个set方法即可,这里定义了一个单独的类,在没有说明的情况下是很难找到的)

l结构的模拟:为了实现ASN1VE中查看出的结构(一个结点下面的多个App子结点),可以通过bc库提供的ASN1EncodableVector来进行模拟(见代码)。

4.2解码

由于bc库没有提供直接解析成对象的API(也可能是我没找到),因此需要自行定义业务实体以及解码代码。

4.2.1定义实体结构

根据文档(word)提供的协议的业务描述结合ASN1VE查看出的结构,定义出业务对象,其实就是普通的javabean,如:Root、Header、Tail和Body等,Root结构如下:publicclassRoot {

privateHeaderheader;

privateList

bodys;

privateTailtail;

}

4.2.2定义实体解析类

为了将数据解析出一个复合对象,需要针对每个实体定义一个解析类,将二进制数据解析成类对象,如:RootDecoder、HeaderDecoder、BodyDecoder和TailDecoder等,其中HeaderDecoder代码如下:publicclassHeaderDecoder {

privateHeaderheader=newHeader();

/**

* 按Sequence顺序解析包头

*@paramnodeHeader

*@throwsIOException

*/

publicHeaderDecoder(DERApplicationSpecific derHeader)throwsIOException

{

ASN1Sequence s = ASN1Sequence.getInstance(derHeader.getObject(DERTags.SEQUENCE));

for(Enumeration e = s.getObjects(); e.hasMoreElements();)

{

DERApplicationSpecific derObj = (DERApplicationSpecific)e.nextElement();

System.out.println(derObj.getApplicationTag());

if(derObj.getApplicationTag() == 50){

DERInteger recodeType =newDERInteger(derObj.getContents());

header.setRecodeType(recodeType.getValue().intValue());

}

elseif(derObj.getApplicationTag() == 51){

DERIA5String senderCode =newDERIA5String(derObj.getContents());

header.setSenderCode(senderCode.getString());

}

elseif(derObj.getApplicationTag() == 52){

DERIA5String accepterCode =newDERIA5String(derObj.getContents());

header.setAccepterCode(accepterCode.getString());

}

elseif(derObj.getApplicationTag() == 53){

DERIA5String fileSerialNo =newDERIA5String(derObj.getContents());

header.setFileSerialNo(fileSerialNo.getString());

}

elseif(derObj.getApplicationTag() == 54){

DERIA5String fileCreateTime =newDERIA5String(derObj.getContents());

header.setFileCreateTime(fileCreateTime.getString());

}

elseif(derObj.getApplicationTag() == 55){

DERInteger fileVersionNo =newDERInteger(derObj.getContents());

header.setFileVersionNo(fileVersionNo.getValue().intValue());

}

}

}

/**

*@returnthe header

*/

publicHeader getHeader() {

returnheader;

}

}

代码简介:

该Decoder在构造函数中读取外部传来的DERApplicationSpecific对象(该对象使其上层root解析子节点获取得到的),在利用bc库的API根据TAG值将其所有的子节点的二进制数据读出来,最后通过bc库内置的基本类型将二进制数据再解码成原始数据。

4.2.3外部调用接口

为了方便的实现文件解析,单独提供一个类(DecodeMan):输入为需要解码的ASN1二进制文件,输出为定义的复合类型Root,最后相关人员再通过对Root的数据进行格式化转换成项目需要用的数据格式。publicclassDecodeMan {

publicstaticvoidmain(String[] args)throwsIOException {

Root root =decode("c:/IVCRECORD_20110324871.0058");

System.out.println(root.toString());

}

/**

* 文件解码

*@paramencodeFile 文件绝对路径

*@return填充数据的Root实例

*@throwsIOException

*/

publicstaticRoot decode(String encodeFile)throwsIOException

{

System.out.println("Starting decode file: ["+ encodeFile +"]");

File file =newFile(encodeFile);

byte[] byteContents = FileUtils.readFileToByteArray(file);

ByteArrayInputStream inputStream =null;

try{

inputStream =newByteArrayInputStream(byteContents);

ASN1StreamParser asn1Parser =newASN1StreamParser(inputStream);

DERApplicationSpecific derRoot = (DERApplicationSpecific)asn1Parser.readObject();

RootDecoder rootDecoder =newRootDecoder(derRoot);

System.out.println("Decode file succ: ["+ encodeFile +"]");

returnrootDecoder.getRoot();

}

finally{

IOUtils.closeQuietly(inputStream);

}

}

}

java实现asn.1解码_ASN1编解码实现方法相关推荐

  1. Jva编解码,加密工具类大全(Base64编解码,URL 编解码,sha56_Hmac加密,MD5对字符串进行加密,java自带类实现SHA-256方式加密)

    Base64编解码 /*** Base64编码.*/public static String encodeBase64(byte[] input) {return new String(Base64. ...

  2. silk 编解码_Silk编解码在android实现

    Silk编解码是Skype向第三方开发人员和硬件制造商提供免版税认证(RF)的Silk宽带音频编码器.Skype已将其开源,可以访问http://developer.skype.com/silk获取最 ...

  3. 音视频编解码--Opus编解码系列1

    1 简介 Opus 是一个完全开源,免费的,通用性高的音频解码器.Opus 在网络上有着无与伦比的交互式语音和音乐传播功能,但也可以用来存储,在流媒体上使用.Opus 遵从 Internet Engi ...

  4. js编码java解码_JS编解码与Java编解码的对应关系

    最近前段在导出数据时会遇到"illegal character"的异常错误,结果发现是在请求地址中请求参数包含了空白字符(其编码为%C2%A0)或者是空格字符(其编码为%20),之 ...

  5. java解析asn.1编码_asn1编码格式的解析过程

    asn1编码格式的解析过程 本文以x509的解析为例说明asn1的编码格式的解析逻辑.x509证书的解析实际上是asn1格式的解析,这里着重说的是asn1的ber编码的解析,总的来讲,asn1格式的解 ...

  6. 硬件编解码软件编解码

    http://www.cnblogs.com/littlehb/p/3914645.html http://www.cnblogs.com/weinyzhou/archive/2012/12/08/2 ...

  7. java web编码详解_Java Web 之编解码分析

    Java Web 之编解码分析 所谓编码,就是将字符转换成字节,所谓解码,就是将字节转换为字符.而编解码中存在的问题主要是由编码和解码所用字符集不匹配导致的.本文主要从以下三个方面分析 Java We ...

  8. H264视频传输、编解码----FFmpeg软解码

    记录一下之前项目的实际使用过程. 将按照Java层------>JNI接口------>JNI代码中使用FFmpeg解码. 首先Java层: public class CodecWrapp ...

  9. 图片解码 java_Java 8实现图片BASE64编解码

    前言 Basic编码是标准的BASE64编码,用于处理常规的需求:输出的内容不添加换行符,而且输出的内容由字母加数字组成. 最近做了个Web模版,其中想用Base64背景图.虽然网络上有现成的编码器, ...

最新文章

  1. 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  2. python 装饰器 参数-python函数装饰器之带参数的函数和带参数的装饰器用法示例...
  3. Vue.js 学习笔记 第5章 内置指令
  4. Windows7右键菜单栏添加打开cmd项
  5. Android 系统(49)---Android获取窗口可视区域大小: getWindowVisibleDisplayFrame()
  6. 数据库写入性能测试小工具
  7. selenium 示例_Selenium findElement和findElements示例
  8. 小程序开发之单页面生命周期
  9. 华为设备ERPS配置命令
  10. IAR8.3 STM8安装过程
  11. matlab newton method,matlab Newton method
  12. 华为防火墙基于IP地址的带宽管理
  13. 大学四年一路自学走来,我把这些私藏的实用工具/学习网站我贡献出来了
  14. 【Plant Biotech. J.】DcMYB113可调节胡萝卜中花青素的生物合成和修饰
  15. CSDN复制文章到Word保留样式方法
  16. 【Python代码实践】使用Python批量获取雨课堂图片
  17. matlab——计算VPD(vapor pressure defict)
  18. 开关量、数字量、模拟量、脉冲量的区别
  19. AE中文破解版软件屏蔽_破解教程
  20. 如何关闭Internet Explorer 增强的安全配置

热门文章

  1. android 手柄sdk,android studio 手柄接入
  2. Flex 布局教程:语法篇(阮一峰)
  3. 时延,时延带宽积,RTT和利用率
  4. React学习笔记汇总
  5. SMBJ5.0CA双向TVS管,在电路保护中的应用
  6. 凌华服务器维护手册,凌华科技
  7. 自适应分段常数近似( Adaptive Piecewise Constant Approximation,APCA)
  8. 计算机cpu制冷,成熟的解决方案:制冷技术:用冷却芯片冷却CPU
  9. SecureCRT超级终端使用说明
  10. FusionCompute8.0.0 实验(3)安装windows虚拟机