国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

ASN1編解碼實現(xiàn)方法

2019-11-09 19:27:49
字體:
來源:轉載
供稿:網(wǎng)友

目 錄版本記錄 1目 錄 1第1章 概述 31.1 背景 31.2 ASN.1概念 31.3 TAG 4第2章 開發(fā)工具 42.1 開發(fā)庫 42.2 輔助工具 5第3章 javaAsn1Compiler 63.1 定義ASN.1描述文件 63.2 生成java代碼 63.2.1 代碼生成 63.2.2 代碼案例 63.2.3 編解碼 73.3 總結 8第4章 bouncycastle 84.1 編碼 84.1.1 確定編碼的文件格式 84.1.2 構造ASN1映射類 94.2 解碼 114.2.1 定義實體結構 114.2.2 定義實體解析類 114.2.3 外部調(diào)用接口 12第1章 概述1.1 背景系統(tǒng)與充值平臺的接口是文件的方式,充值平臺將文件內(nèi)容以ASN.1方式進行編碼,系統(tǒng)需要根據(jù)ASN.1協(xié)議進行解碼。關于ASN.1開發(fā)的資料,網(wǎng)上資料非常少,特別是涉及到具體的語言,如java,資料、案例及第三方庫更是少之又少。從無到有是很困難的,為了防止后期其他系統(tǒng)還需要做類似接口,將其記錄為文章以便后查,文章會以充值接口作為案例進行介紹。1.2 ASN.1概念在電信和計算機網(wǎng)絡領域,ASN.1(Abstract Syntax Notation one) 是一套標準,是描述數(shù)據(jù)的表示、編碼、傳輸、解碼的靈活的記法。它提供了一套正式、無歧義和精確的規(guī)則以描述獨立于特定計算機硬件的對象結構。ASN.1 包括幾個標準化編碼規(guī)則,如基本編碼規(guī)則(BER) -X.209 、規(guī)范編碼規(guī)則(CER)、識別名編碼規(guī)則(DER)、壓縮編碼規(guī)則(PER)和 xml編碼規(guī)則(XER)。這些編碼規(guī)則描述了如何對 ASN.1 中定義的數(shù)值進行編碼,以便用于傳輸,而不管計算機、編程語言或它在應用程序中如何表示等因素。ASN.1 的編碼方法比許多與之相競爭的標記系統(tǒng)更先進,它支持可擴展信息快速可靠的傳輸 — 在無線寬帶中,這是一種優(yōu)勢。1984年,ASN.1 就已經(jīng)成為了一種國際標準,它的編碼規(guī)則已經(jīng)成熟并在可靠性和兼容性方面擁有更豐富的歷程。  簡潔的二進制編碼規(guī)則(BER、CER、DER、PER,但不包括 XER)可當作更現(xiàn)代 XML 的替代。然而,ASN.1 支持對數(shù)據(jù)的語義進行描述,所以它是比 XML 更為高級的語言。  ASN.1 的描述可以容易地被映射成 C 或 C++ 或 Java 的數(shù)據(jù)結構,并可以被應用程序代碼使用,并得到運行時程序庫的支持,進而能夠對編碼和解碼 XML 或 TLV 格式的,或一種非常緊湊的壓縮編碼格式的描述。同時,ASN.1也是一種用于描述結構化實體的結構和內(nèi)容的語言。如:使用ASN.1語法可以這樣定義一個類:Report ::= SEQUENCE {author OCTET STRING,  title OCTET STRING,   body OCTET STRING,  biblio INTEGER  } 詳見《ASN.1編碼規(guī)則詳解》:http://wenku.baidu.com/view/33ba22d276eeaeaad1f3304c.html 注:在進行ASN1開發(fā)前,需要先閱讀上述文章,了解其中的一些基本概念。1.3 TAG由于TAG在ASN1中非常之重要,而且在對文件進行解析時就是因為TAG的問題導致浪費了很多時間,因此這里對其單獨介紹,不過只是提出概念,詳細描述還需參見相關規(guī)范。TAG是對ASN1協(xié)議中每個數(shù)據(jù)域的標識,通過2.2的截圖可以看到,每個結點名稱后面都有一個數(shù)字,這個就TAG值。TAG有可分為四大類:UNIVERSAL、Context、PRivate、application。詳見:http://wenku.baidu.com/view/33ba22d276eeaeaad1f3304c.html第2章 開發(fā)工具通常情況下,如果使用該協(xié)議進行交互,雙方應該規(guī)定出一個以ASN.1語法描述的協(xié)議文件,類似webservice開發(fā)中的wsdl,然后各自系統(tǒng)使用相關工具進行編解碼。2.1 開發(fā)庫目前網(wǎng)上能查到的第三方免費工具,主要有JavaAsn1Comiler和bouncycastle子庫:l JavaAsn1Comiler(JAC.jar):n 該工具可根據(jù)ASN.1協(xié)議描述文件,生成對應的java類,同時提供的API接口非常友好,命名概念同理論基本一致,使用非常方便,但是前提是必須要有完整的ASN.1描述文件,而且非常重要的一點使用限制是,該庫目前支持TAG值在0-127之間,即:如果協(xié)議中的數(shù)據(jù)使用了超過127的TAG值,則該庫無法支持,不可使用(否則會出現(xiàn)編碼錯誤,無法解析)。n 該庫提供了相當豐富的使用案例,可參考其工程下的test目錄。l bouncycastle(bcprov-jdk16-1.46.jar)n 該工具沒有提供自動生成java代碼的能力,如果要進行編解碼,則需要手動對協(xié)議中的類進行定義,并且自己調(diào)用相應的API實現(xiàn)編解碼。使用起來較JAC復雜,但是該庫對TAG值沒有限制,適合用在TAG值大于127的場景。n 在使用該類進行解析時,由于沒有提供方便的API進行自動解析,因此需要手工編寫解析代碼,比較郁悶的是其幫助文檔也沒有比較詳細的案例,最后的解碼操作還是通過閱讀其ASN1Dump類的實現(xiàn)方才完成。2.2 輔助工具由于經(jīng)ASN.1編碼后的文件是二進制格式,無法直接閱讀,因此在開發(fā)過程中,為了能夠比較直觀的閱讀到其編碼后的記錄,需要借助第三方工具來查看編碼后的文件內(nèi)容。網(wǎng)上有幾個查看工具,但是最方便、最直觀的工具則是ASN1VE 2.1(未注冊版有功能限制,只能查看編碼后的文件),通過該軟件可以很輕松的查看到文件內(nèi)容,截圖如下:l 二進制視圖:l XML視圖第3章 JavaAsn1Compiler3.1 定義ASN.1描述文件通過通信雙方約定的數(shù)據(jù)格式,使用ASN.1語法對其進行定義(參見1.2百度文庫),形成.asn文件,如vc.asn。3.2 生成java代碼3.2.1 代碼生成將編寫好的.asn文件放到JAC.jar目錄,執(zhí)行:java -jar JAC.jar -d c:/jac_test -p vc vc.asn參數(shù)描述:-d:生成java代碼文件的保存目錄;-p:生成java代碼的package;3.2.2 代碼案例以下代碼可從JavaAsn1Compiler工程的test目錄下獲取,ASN1文件內(nèi)容:MiddleSeq ::= SEQUENCE{status [22] INTEGER,location [APPLICATION 11] INTEGER}生成的java代碼如下:import com.turkcelltech.jac.*;import com.chaosinmotion.asn1.Tag;public class MiddleSeq extends Sequence{public ASN1Integer status = new ASN1Integer("status");public ASN1Integer location = new ASN1Integer("location");publicMiddleSeq(){super();setUpElements();}publicMiddleSeq(String name){super(name);setUpElements();}protected voidsetUpElements(){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 = new ByteArrayOutputStream();BerOutputStream out = new BerOutputStream(outStream);MiddleSeq ms = new MiddleSeq();ms.status.setValue(2);ms.location.setValue(314);ms.encode(out);// 解碼ByteArrayInputStream inputStream;BerInputStream in;inputStream = new ByteArrayInputStream(outStream.toByteArray());in = new BerInputStream(inputStream);MiddleSeq decode_ms = new MiddleSeq("decode_ms");decode_ms.decode(in);System.out.println("ms.status=" + decode_ms.status.getValue());可見編解碼非常簡單,如果是嵌套結構,只要對最外層對象執(zhí)行encode/decode操作即可。3.3 總結使用該庫在有ASN1協(xié)議描述文件時,開發(fā)ASN1編解碼非常容易,缺點就是不支持超過127的TAG值。第4章 bouncycastlebouncycastle(簡稱bc)包含了一系列的java編解碼工具,ASN1只是其中的一類。在沒有ASN1協(xié)議描述文件的情況下,結合ASN1VE工具,可以進行相關的編解碼開發(fā),云南服務質(zhì)量管理系統(tǒng)與VC充值平臺正是使用這種方式開發(fā)的。4.1 編碼在云南服務質(zhì)量管理系統(tǒng)中,實際上并沒用用到ASN1編碼的知識,但是在從零開始的背景下,為了更好的學習和理解ASN1的編碼格式,這里便開發(fā)了一個編碼模型。4.1.1 確定編碼的文件格式由于沒有ASN1文件,只有編碼后的文件,因此需要通過ASN1VE來查看編碼后是什么格式,如圖所示:通過上圖可以看出整個文件的組織結構、每個數(shù)據(jù)域對應的TAG值以及TAG的類型(Application)。但是ASN1的編碼有多種方式,如:BER/DER/PER等,bc庫提供的API就包含了BER和DER兩種類型,為了確定具體的編碼格式,利用bc庫自帶的ASN1Dump工具,可以將該文件通過文本的方式輸出出來(略),最后獲取的方式為DER,下面就利用bc庫提供的API來構造上述的結構。4.1.2 構造ASN1映射類有了上面分析出的結構再結合對方提供的Word文檔,即可以定義出大概結構,這里只給出其中Header的定義,其他可參考具體代碼:import java.io.IOException;import org.bouncycastle.asn1.ASN1EncodableVector;import org.bouncycastle.asn1.DERApplicationSpecific;import org.bouncycastle.asn1.DEREncodable;import org.bouncycastle.asn1.DERIA5String;import org.bouncycastle.asn1.DERInteger;public class RecordHeader extends DERApplicationSpecific{public RecordHeader(boolean explicit, int tag, DEREncodable object) throws IOException {super(explicit, tag, object);// TODO Auto-generated constructor stub}public RecordHeader(int tagNo, ASN1EncodableVector vec) {super(tagNo, vec);}public RecordHeader(int tag, byte[] octets) {super(tag, octets);// TODO Auto-generated constructor stub}public RecordHeader(int tag, DEREncodable object) throws IOException {super(tag, object);// TODO Auto-generated constructor stub}public static RecordHeader createHeader(int recodeType, String senderCode, String accepterCode,String fileSerialNo, String fileCreateTime, int fileVersionNo){DERInteger d_recodeType = new DERInteger(recodeType);DERIA5String d_senderCode = new DERIA5String(senderCode);DERIA5String d_accepterCode = new DERIA5String(accepterCode);DERIA5String d_fileSerialNo = new DERIA5String(fileSerialNo);DERIA5String d_fileCreateTime = new DERIA5String(fileCreateTime);DERInteger d_fileVersionNo = new DERInteger(fileVersionNo);ASN1EncodableVector vec = new ASN1EncodableVector();try{vec.add(new DERApplicationSpecific(false, 50, d_recodeType));vec.add(new DERApplicationSpecific(false, 51, d_senderCode));vec.add(new DERApplicationSpecific(false, 52, d_accepterCode));vec.add(new DERApplicationSpecific(false, 53, d_fileSerialNo));vec.add(new DERApplicationSpecific(false, 54, d_fileCreateTime));vec.add(new DERApplicationSpecific(false, 55, d_fileVersionNo));}catch(IOException e){e.printStackTrace();}RecordHeader header = new RecordHeader(33, vec);return header;}}代碼中幾個重要概念l 類定義:理論上一個類的定義應該是一個SEQUENCE,但是通過ASN1Dump出來的數(shù)據(jù)顯示其并不是一個Sequence,而只是一個普通的結點,因此這里不能繼承DERSequence,否則編碼出的文件將會在Application的上或者下多出一個Sequence結點。l DERApplicationSpecific:這個是表示構造Application類型Tag的類,如果協(xié)議中沒有規(guī)定Tag類型,默認的可以使用DERTaggedObject來定義節(jié)點。(說明:而在JAC庫中,如果要定義TAG的類型,實際上只要調(diào)用一個set方法即可,這里定義了一個單獨的類,在沒有說明的情況下是很難找到的)l 結構的模擬:為了實現(xiàn)ASN1VE中查看出的結構(一個結點下面的多個App子結點),可以通過bc庫提供的ASN1EncodableVector來進行模擬(見代碼)。4.2 解碼由于bc庫沒有提供直接解析成對象的API(也可能是我沒找到),因此需要自行定義業(yè)務實體以及解碼代碼。4.2.1 定義實體結構根據(jù)文檔(word)提供的協(xié)議的業(yè)務描述結合ASN1VE查看出的結構,定義出業(yè)務對象,其實就是普通的javabean,如:Root、Header、Tail和Body等,Root結構如下:public class Root {private Header header;private List<Body> bodys;private Tail tail;}4.2.2 定義實體解析類為了將數(shù)據(jù)解析出一個復合對象,需要針對每個實體定義一個解析類,將二進制數(shù)據(jù)解析成類對象,如:RootDecoder、HeaderDecoder、BodyDecoder和TailDecoder等,其中HeaderDecoder代碼如下:public class HeaderDecoder {private Header header = new Header();/*** 按Sequence順序解析包頭* @param nodeHeader* @throws IOException*/public HeaderDecoder(DERApplicationSpecific derHeader) throws IOException{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 = new DERInteger(derObj.getContents());header.setRecodeType(recodeType.getValue().intValue());}else if (derObj.getApplicationTag() == 51){DERIA5String senderCode = new DERIA5String(derObj.getContents()); header.setSenderCode(senderCode.getString());}else if (derObj.getApplicationTag() == 52){DERIA5String accepterCode = new DERIA5String(derObj.getContents());header.setAccepterCode(accepterCode.getString());}else if (derObj.getApplicationTag() == 53){DERIA5String fileSerialNo = new DERIA5String(derObj.getContents());header.setFileSerialNo(fileSerialNo.getString());}else if (derObj.getApplicationTag() == 54){DERIA5String fileCreateTime = new DERIA5String(derObj.getContents());header.setFileCreateTime(fileCreateTime.getString());}else if (derObj.getApplicationTag() == 55){DERInteger fileVersionNo = new DERInteger(derObj.getContents());header.setFileVersionNo(fileVersionNo.getValue().intValue());}}}/*** @return the header*/public Header getHeader() {return header;}}代碼簡介:該Decoder在構造函數(shù)中讀取外部傳來的DERApplicationSpecific 對象(該對象使其上層root解析子節(jié)點獲取得到的),在利用bc庫的API根據(jù)TAG值將其所有的子節(jié)點的二進制數(shù)據(jù)讀出來,最后通過bc庫內(nèi)置的基本類型將二進制數(shù)據(jù)再解碼成原始數(shù)據(jù)。4.2.3 外部調(diào)用接口為了方便的實現(xiàn)文件解析,單獨提供一個類(DecodeMan):輸入為需要解碼的ASN1二進制文件,輸出為定義的復合類型Root,最后相關人員再通過對Root的數(shù)據(jù)進行格式化轉換成項目需要用的數(shù)據(jù)格式。public class DecodeMan {public static void main(String[] args) throws IOException {Root root = decode("c:/IVCRECORD_20110324871.0058");System.out.println(root.toString());}/*** 文件解碼* @param encodeFile 文件絕對路徑* @return 填充數(shù)據(jù)的Root實例* @throws IOException*/public static Root decode(String encodeFile) throws IOException{System.out.println("Starting decode file: [" + encodeFile + "]");File file = new File(encodeFile);byte[] byteContents = FileUtils.readFileToByteArray(file);ByteArrayInputStream inputStream = null;try{inputStream = new ByteArrayInputStream(byteContents);ASN1StreamParser asn1Parser = new ASN1StreamParser(inputStream);DERApplicationSpecific derRoot = (DERApplicationSpecific)asn1Parser.readObject();RootDecoder rootDecoder = new RootDecoder(derRoot);System.out.println("Decode file succ: [" + encodeFile + "]");return rootDecoder.getRoot();}finally{IOUtils.closeQuietly(inputStream);}}}


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿拉善左旗| 县级市| 克山县| 石屏县| 田林县| 韶山市| 崇明县| 庄河市| 长丰县| 岚皋县| 五峰| 东至县| 肥东县| 白山市| 广东省| 保康县| 巴青县| 吴江市| 会宁县| 西青区| 山阳县| 惠水县| 江津市| 赞皇县| 全椒县| 河池市| 衡阳县| 怀化市| 康定县| 永胜县| 高要市| 叙永县| 紫云| 巨野县| 镇赉县| 卓尼县| 通化市| 柳江县| 景东| 藁城市| 南平市|