工作中用到了asn.1格式數據,所以這裏對asn.1格式的編解碼做一個簡單的介紹,主要通過程序來構建與解析asn.1格式數據。asn.1格式一般分爲三個部分,分別是類型、長度、值,也就是Tag,Length,Value,簡稱TLV格式。
類型一般分爲以下幾種:
sequence,也叫集合和結構
integer,整數
utf8string,字符串
boolean,布爾類型
time,時間類型
下面構建一個maven項目,引入項目依賴:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.65</version>
</dependency>
定義一個student的實體類,繼承ASN1Object:
package com.xxx.asn1;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x509.Time;
public class Student extends ASN1Object {
private ASN1Integer id;
private DERUTF8String name;
private ASN1Integer age;
private Time createDate;
public void setId(ASN1Integer id) {
this.id = id;
}
public void setName(DERUTF8String name) {
this.name = name;
}
public void setAge(ASN1Integer age) {
this.age = age;
}
public void setCreateDate(Time createDate) {
this.createDate = createDate;
}
public ASN1Integer getId() {
return id;
}
public DERUTF8String getName() {
return name;
}
public ASN1Integer getAge() {
return age;
}
public Time getCreateDate() {
return createDate;
}
public Student() {
// TODO Auto-generated constructor stub
}
public Student(ASN1Integer id, DERUTF8String name, ASN1Integer age,
Time createDate) {
super();
this.id = id;
this.name = name;
this.age = age;
this.createDate = createDate;
}
@Override
public ASN1Primitive toASN1Primitive() {
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(id);
vector.add(name);
vector.add(age);
vector.add(createDate);
return new DERSequence(vector);
}
}
定義一個主類App,來分別構建asn.1和解析asn.1格式數據:
package com.xxx.asn1;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1SequenceParser;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.util.encoders.Base64;
public class App {
/**
* 構建asn.1
*/
public static void buildStudent(){
Integer id = 1;
String name = "buejee";
Integer age = 18;
Time createDate = new Time(new Date());
try {
Student student = new Student(new ASN1Integer(id),
new DERUTF8String(name),
new ASN1Integer(age),
createDate);
String data = Base64.toBase64String(student.getEncoded());
System.out.println(data);
OutputStream out = new FileOutputStream(new File("student.cer"));
out.write(data.getBytes());
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 解析asn.1
*/
public static void resolveStudent(){
byte[] data = Base64.decode("MB0CAQEMBmJ1ZWplZQIBEhcNMjAwNTE1MTUxNDAzWg==");
ASN1InputStream ais = new ASN1InputStream(data);
ASN1Primitive primitive = null;
try {
while((primitive=ais.readObject())!=null){
System.out.println("sequence->"+primitive);
if(primitive instanceof ASN1Sequence){
ASN1Sequence sequence = (ASN1Sequence)primitive;
ASN1SequenceParser parser = sequence.parser();
ASN1Encodable encodable = null;
while((encodable=parser.readObject())!=null){
primitive = encodable.toASN1Primitive();
System.out.println("prop->"+primitive);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
ais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main( String[] args ){
//buildStudent();
resolveStudent();
}
}
分別運行兩個方法,第一個是buildStudent()是構建一個asn.1格式數據,我們最後將他轉爲base64字符串,打印並存儲在student.cer文件中。
打印的結果和保存在student.cer中的結果是一樣的。我們可以通過asn1view工具查看這個內容:
sequence部分:
第一個integer:
string:
第二個integer:
time :
這所有的內容都符合我們在代碼中定義的數據:
id=1,name=buejee,age=18,createDate=(20)20/05/15 15:36:43,時間是世界時間,所以東八區的話應該是(20)20/05/15 23:36:43,都是正確的。
我們再來看看解析,調用resolveStudent()方法,控制檯打印結果如下:
數據讀出來了。跟我們構建時傳入的參數一樣。
本文的代碼和工具asn1view都會放到github上,有需要的可以查看。