工作中用到了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上,有需要的可以查看。