目 錄版本記錄 1目 錄 1第1章 概述 31.1 背景 31.2 ASN.1概念 31.3 TAG 4第2章 開發(fā)工具 42.1 開發(fā)庫(kù) 42.2 輔助工具 5第3章 javaAsn1Compiler 63.1 定義ASN.1描述文件 63.2 生成java代碼 63.2.1 代碼生成 63.2.2 代碼案例 63.2.3 編解碼 73.3 總結(jié) 8第4章 bouncycastle 84.1 編碼 84.1.1 確定編碼的文件格式 84.1.2 構(gòu)造ASN1映射類 94.2 解碼 114.2.1 定義實(shí)體結(jié)構(gòu) 114.2.2 定義實(shí)體解析類 114.2.3 外部調(diào)用接口 12第1章 概述1.1 背景系統(tǒng)與充值平臺(tái)的接口是文件的方式,充值平臺(tái)將文件內(nèi)容以ASN.1方式進(jìn)行編碼,系統(tǒng)需要根據(jù)ASN.1協(xié)議進(jìn)行解碼。關(guān)于ASN.1開發(fā)的資料,網(wǎng)上資料非常少,特別是涉及到具體的語(yǔ)言,如java,資料、案例及第三方庫(kù)更是少之又少。從無(wú)到有是很困難的,為了防止后期其他系統(tǒng)還需要做類似接口,將其記錄為文章以便后查,文章會(huì)以充值接口作為案例進(jìn)行介紹。1.2 ASN.1概念在電信和計(jì)算機(jī)網(wǎng)絡(luò)領(lǐng)域,ASN.1(Abstract Syntax Notation one) 是一套標(biāo)準(zhǔn),是描述數(shù)據(jù)的表示、編碼、傳輸、解碼的靈活的記法。它提供了一套正式、無(wú)歧義和精確的規(guī)則以描述獨(dú)立于特定計(jì)算機(jī)硬件的對(duì)象結(jié)構(gòu)。ASN.1 包括幾個(gè)標(biāo)準(zhǔn)化編碼規(guī)則,如基本編碼規(guī)則(BER) -X.209 、規(guī)范編碼規(guī)則(CER)、識(shí)別名編碼規(guī)則(DER)、壓縮編碼規(guī)則(PER)和 xml編碼規(guī)則(XER)。這些編碼規(guī)則描述了如何對(duì) ASN.1 中定義的數(shù)值進(jìn)行編碼,以便用于傳輸,而不管計(jì)算機(jī)、編程語(yǔ)言或它在應(yīng)用程序中如何表示等因素。ASN.1 的編碼方法比許多與之相競(jìng)爭(zhēng)的標(biāo)記系統(tǒng)更先進(jìn),它支持可擴(kuò)展信息快速可靠的傳輸 — 在無(wú)線寬帶中,這是一種優(yōu)勢(shì)。1984年,ASN.1 就已經(jīng)成為了一種國(guó)際標(biāo)準(zhǔn),它的編碼規(guī)則已經(jīng)成熟并在可靠性和兼容性方面擁有更豐富的歷程。 簡(jiǎn)潔的二進(jìn)制編碼規(guī)則(BER、CER、DER、PER,但不包括 XER)可當(dāng)作更現(xiàn)代 XML 的替代。然而,ASN.1 支持對(duì)數(shù)據(jù)的語(yǔ)義進(jìn)行描述,所以它是比 XML 更為高級(jí)的語(yǔ)言。 ASN.1 的描述可以容易地被映射成 C 或 C++ 或 Java 的數(shù)據(jù)結(jié)構(gòu),并可以被應(yīng)用程序代碼使用,并得到運(yùn)行時(shí)程序庫(kù)的支持,進(jìn)而能夠?qū)幋a和解碼 XML 或 TLV 格式的,或一種非常緊湊的壓縮編碼格式的描述。同時(shí),ASN.1也是一種用于描述結(jié)構(gòu)化實(shí)體的結(jié)構(gòu)和內(nèi)容的語(yǔ)言。如:使用ASN.1語(yǔ)法可以這樣定義一個(gè)類:Report ::= SEQUENCE {author OCTET STRING, title OCTET STRING, body OCTET STRING, biblio INTEGER } 詳見《ASN.1編碼規(guī)則詳解》:http://wenku.baidu.com/view/33ba22d276eeaeaad1f3304c.html 注:在進(jìn)行ASN1開發(fā)前,需要先閱讀上述文章,了解其中的一些基本概念。1.3 TAG由于TAG在ASN1中非常之重要,而且在對(duì)文件進(jìn)行解析時(shí)就是因?yàn)門AG的問(wèn)題導(dǎo)致浪費(fèi)了很多時(shí)間,因此這里對(duì)其單獨(dú)介紹,不過(guò)只是提出概念,詳細(xì)描述還需參見相關(guān)規(guī)范。TAG是對(duì)ASN1協(xié)議中每個(gè)數(shù)據(jù)域的標(biāo)識(shí),通過(guò)2.2的截圖可以看到,每個(gè)結(jié)點(diǎn)名稱后面都有一個(gè)數(shù)字,這個(gè)就TAG值。TAG有可分為四大類:UNIVERSAL、Context、PRivate、application。詳見:http://wenku.baidu.com/view/33ba22d276eeaeaad1f3304c.html第2章 開發(fā)工具通常情況下,如果使用該協(xié)議進(jìn)行交互,雙方應(yīng)該規(guī)定出一個(gè)以ASN.1語(yǔ)法描述的協(xié)議文件,類似webservice開發(fā)中的wsdl,然后各自系統(tǒng)使用相關(guān)工具進(jìn)行編解碼。2.1 開發(fā)庫(kù)目前網(wǎng)上能查到的第三方免費(fèi)工具,主要有JavaAsn1Comiler和bouncycastle子庫(kù):l JavaAsn1Comiler(JAC.jar):n 該工具可根據(jù)ASN.1協(xié)議描述文件,生成對(duì)應(yīng)的java類,同時(shí)提供的API接口非常友好,命名概念同理論基本一致,使用非常方便,但是前提是必須要有完整的ASN.1描述文件,而且非常重要的一點(diǎn)使用限制是,該庫(kù)目前支持TAG值在0-127之間,即:如果協(xié)議中的數(shù)據(jù)使用了超過(guò)127的TAG值,則該庫(kù)無(wú)法支持,不可使用(否則會(huì)出現(xiàn)編碼錯(cuò)誤,無(wú)法解析)。n 該庫(kù)提供了相當(dāng)豐富的使用案例,可參考其工程下的test目錄。l bouncycastle(bcprov-jdk16-1.46.jar)n 該工具沒(méi)有提供自動(dòng)生成java代碼的能力,如果要進(jìn)行編解碼,則需要手動(dòng)對(duì)協(xié)議中的類進(jìn)行定義,并且自己調(diào)用相應(yīng)的API實(shí)現(xiàn)編解碼。使用起來(lái)較JAC復(fù)雜,但是該庫(kù)對(duì)TAG值沒(méi)有限制,適合用在TAG值大于127的場(chǎng)景。n 在使用該類進(jìn)行解析時(shí),由于沒(méi)有提供方便的API進(jìn)行自動(dòng)解析,因此需要手工編寫解析代碼,比較郁悶的是其幫助文檔也沒(méi)有比較詳細(xì)的案例,最后的解碼操作還是通過(guò)閱讀其ASN1Dump類的實(shí)現(xiàn)方才完成。2.2 輔助工具由于經(jīng)ASN.1編碼后的文件是二進(jìn)制格式,無(wú)法直接閱讀,因此在開發(fā)過(guò)程中,為了能夠比較直觀的閱讀到其編碼后的記錄,需要借助第三方工具來(lái)查看編碼后的文件內(nèi)容。網(wǎng)上有幾個(gè)查看工具,但是最方便、最直觀的工具則是ASN1VE 2.1(未注冊(cè)版有功能限制,只能查看編碼后的文件),通過(guò)該軟件可以很輕松的查看到文件內(nèi)容,截圖如下:l 二進(jìn)制視圖:l XML視圖第3章 JavaAsn1Compiler3.1 定義ASN.1描述文件通過(guò)通信雙方約定的數(shù)據(jù)格式,使用ASN.1語(yǔ)法對(duì)其進(jìn)行定義(參見1.2百度文庫(kù)),形成.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());可見編解碼非常簡(jiǎn)單,如果是嵌套結(jié)構(gòu),只要對(duì)最外層對(duì)象執(zhí)行encode/decode操作即可。3.3 總結(jié)使用該庫(kù)在有ASN1協(xié)議描述文件時(shí),開發(fā)ASN1編解碼非常容易,缺點(diǎn)就是不支持超過(guò)127的TAG值。第4章 bouncycastlebouncycastle(簡(jiǎn)稱bc)包含了一系列的java編解碼工具,ASN1只是其中的一類。在沒(méi)有ASN1協(xié)議描述文件的情況下,結(jié)合ASN1VE工具,可以進(jìn)行相關(guān)的編解碼開發(fā),云南服務(wù)質(zhì)量管理系統(tǒng)與VC充值平臺(tái)正是使用這種方式開發(fā)的。4.1 編碼在云南服務(wù)質(zhì)量管理系統(tǒng)中,實(shí)際上并沒(méi)用用到ASN1編碼的知識(shí),但是在從零開始的背景下,為了更好的學(xué)習(xí)和理解ASN1的編碼格式,這里便開發(fā)了一個(gè)編碼模型。4.1.1 確定編碼的文件格式由于沒(méi)有ASN1文件,只有編碼后的文件,因此需要通過(guò)ASN1VE來(lái)查看編碼后是什么格式,如圖所示:通過(guò)上圖可以看出整個(gè)文件的組織結(jié)構(gòu)、每個(gè)數(shù)據(jù)域?qū)?yīng)的TAG值以及TAG的類型(Application)。但是ASN1的編碼有多種方式,如:BER/DER/PER等,bc庫(kù)提供的API就包含了BER和DER兩種類型,為了確定具體的編碼格式,利用bc庫(kù)自帶的ASN1Dump工具,可以將該文件通過(guò)文本的方式輸出出來(lái)(略),最后獲取的方式為DER,下面就利用bc庫(kù)提供的API來(lái)構(gòu)造上述的結(jié)構(gòu)。4.1.2 構(gòu)造ASN1映射類有了上面分析出的結(jié)構(gòu)再結(jié)合對(duì)方提供的Word文檔,即可以定義出大概結(jié)構(gòu),這里只給出其中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;}}代碼中幾個(gè)重要概念l 類定義:理論上一個(gè)類的定義應(yīng)該是一個(gè)SEQUENCE,但是通過(guò)ASN1Dump出來(lái)的數(shù)據(jù)顯示其并不是一個(gè)Sequence,而只是一個(gè)普通的結(jié)點(diǎn),因此這里不能繼承DERSequence,否則編碼出的文件將會(huì)在Application的上或者下多出一個(gè)Sequence結(jié)點(diǎn)。l DERApplicationSpecific:這個(gè)是表示構(gòu)造Application類型Tag的類,如果協(xié)議中沒(méi)有規(guī)定Tag類型,默認(rèn)的可以使用DERTaggedObject來(lái)定義節(jié)點(diǎn)。(說(shuō)明:而在JAC庫(kù)中,如果要定義TAG的類型,實(shí)際上只要調(diào)用一個(gè)set方法即可,這里定義了一個(gè)單獨(dú)的類,在沒(méi)有說(shuō)明的情況下是很難找到的)l 結(jié)構(gòu)的模擬:為了實(shí)現(xiàn)ASN1VE中查看出的結(jié)構(gòu)(一個(gè)結(jié)點(diǎn)下面的多個(gè)App子結(jié)點(diǎn)),可以通過(guò)bc庫(kù)提供的ASN1EncodableVector來(lái)進(jìn)行模擬(見代碼)。4.2 解碼由于bc庫(kù)沒(méi)有提供直接解析成對(duì)象的API(也可能是我沒(méi)找到),因此需要自行定義業(yè)務(wù)實(shí)體以及解碼代碼。4.2.1 定義實(shí)體結(jié)構(gòu)根據(jù)文檔(word)提供的協(xié)議的業(yè)務(wù)描述結(jié)合ASN1VE查看出的結(jié)構(gòu),定義出業(yè)務(wù)對(duì)象,其實(shí)就是普通的javabean,如:Root、Header、Tail和Body等,Root結(jié)構(gòu)如下:public class Root {private Header header;private List<Body> bodys;private Tail tail;}4.2.2 定義實(shí)體解析類為了將數(shù)據(jù)解析出一個(gè)復(fù)合對(duì)象,需要針對(duì)每個(gè)實(shí)體定義一個(gè)解析類,將二進(jìn)制數(shù)據(jù)解析成類對(duì)象,如: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;}}代碼簡(jiǎn)介:該Decoder在構(gòu)造函數(shù)中讀取外部傳來(lái)的DERApplicationSpecific 對(duì)象(該對(duì)象使其上層root解析子節(jié)點(diǎn)獲取得到的),在利用bc庫(kù)的API根據(jù)TAG值將其所有的子節(jié)點(diǎn)的二進(jìn)制數(shù)據(jù)讀出來(lái),最后通過(guò)bc庫(kù)內(nèi)置的基本類型將二進(jìn)制數(shù)據(jù)再解碼成原始數(shù)據(jù)。4.2.3 外部調(diào)用接口為了方便的實(shí)現(xiàn)文件解析,單獨(dú)提供一個(gè)類(DecodeMan):輸入為需要解碼的ASN1二進(jìn)制文件,輸出為定義的復(fù)合類型Root,最后相關(guān)人員再通過(guò)對(duì)Root的數(shù)據(jù)進(jìn)行格式化轉(zhuǎn)換成項(xiàng)目需要用的數(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 文件絕對(duì)路徑* @return 填充數(shù)據(jù)的Root實(shí)例* @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);}}}
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注