本文介绍BC中的ASN1组装和解析,ASN1是什么就不解释了。其实直接使用BC解析ASN1的场景是比较少见的, 因为大多数操作已经对应封装好了。比如说X509解析本身就已经封装好,不需要一步一步的用这些函数解析。但有些场景需要用到自定义的ASN1结构时,自己组装和解析就不可避免了。下面的例子中使用的bcprov-ext-jdk15on-1.51.jar包请自行下载,我所使用的是1.51,其他旧版本基本上一致,应该可以使用。
1.1首先是简单组装
创建一个简单的ASN1格式数据,里面包含Integer和Boolean值。try{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//创建ByteArrayOutputStream,用于放置输出的byte流
DEROutputStream derOutputStream = new DEROutputStream(outputStream);
//创建DEROutputStream
derOutputStream.writeObject(new DERInteger(10));
//写入DERInteger数据,10对应的hex为0a。
derOutputStream.writeObject(new DERBoolean(false));
//写入DERBoolean数据,false对应asn1中的hex为00
derOutputStream.flush();
System.out.println(StringUtils.Hex(outputStream.toByteArray()));
//将输出的bytearray转换为直观显示的Hex格式输出
} catch (Exception e) {
e.printStackTrace();
}
输出:02 01 0a 01 01 00
对应解析:
02 : Interger
<span style="white-space:pre"> </span>01 : Len
<span style="white-space:pre"> </span>0a : value(10)
01 : Boolean
<span style="white-space:pre"> </span>01 : len
<span style="white-space:pre"> </span>00 : value(false)
说明:首先是需要创建ASN1OutputStream,然后就能往里面填充输出的数据了。这个例子非常简单,输出中的02 01 0a,02是TAG = Integer,01是后续长度,0a就是Integer的值了。另一个也同理。
1.2序列的组装
在1.1的基础上,加入一个Sequence。try{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//创建ByteArrayOutputStream,用于放置输出的byte流
DEROutputStream derOutputStream = new DEROutputStream(outputStream);
//创建DEROutputStream
derOutputStream.writeObject(new DERInteger(10));
//写入DERInteger数据,10对应的hex为0a。
derOutputStream.writeObject(new DERBoolean(false));
//写入DERBoolean数据,false对应asn1中的hex为00
ASN1EncodableVector encodableVector = new ASN1EncodableVector();
//创建ASN1EncodableVector,用于放Sequence的数据
encodableVector.add(new DERPrintableString("PP"));
//encodableVector中写入各种对象
encodableVector.add(new DERUTCTime(new Date()));
encodableVector.add(new DERNull());
DERSequence derSequence = new DERSequence(encodableVector);
//ASN1EncodableVector封装为DERSequence
derOutputStream.writeObject(derSequence);
//写入DERSequence数据。
derOutputStream.flush();
System.out.println(StringUtils.Hex(outputStream.toByteArray()));
//将输出的bytearray转换为直观显示的Hex格式输出
} catch (Exception e) {
e.printStackTrace();
}
输出:02 01 0a 01 01 00 30 15 13 02 50 50 17 0d 31 35 30 35 31 36 30 32 33 39 31 32 5a 05 00
对应解析
02 : Interger
<span style="white-space:pre"> </span>01 : Len
<span style="white-space:pre"> </span>0a : value(10)
01 : Boolean
<span style="white-space:pre"> </span>01 : len
<span style="white-space:pre"> </span>00 : value(false)
30 : Sequence
<span style="white-space:pre"> </span>15 : len
<span style="white-space:pre"> </span>13 : PrintableString
<span style="white-space:pre"> </span>02 : len
<span style="white-space:pre"> </span>50 50 : value(PP)
<span style="white-space:pre"> </span>17 : UTCTime
<span style="white-space:pre"> </span>0d : len
<span style="white-space:pre"> </span>31 35 30 35 31 36 30 32 33 39 31 32 5a : value(150516023912GMT+00:00)
<span style="white-space:pre"> </span>05 : NULL
<span style="white-space:pre"> </span>00 : len
说明:需要创建序列的时候,先要创建ASN1EncodableVector,填充后序列内容后,再添加到DEROutputStream中即可。输出中30 15即为DERSequence的TAG和长度。
2.解析
解析1.2中生成的DERtry{
ASN1InputStream asn1InputStream = new ASN1InputStream(StringUtils.Hex("02010a010100301513025050170d3135303531363032333931325a0500"));
//将hex转换为byte输出
ASN1Primitive asn1Primitive = null;
while ((asn1Primitive = asn1InputStream.readObject()) != null) {
//循环读取,分类解析。这样的解析方式可能不适合有两个同类的ASN1对象解析,如果遇到同类,那就需要按照顺序来调用readObject,就可以实现解析了。
if ( asn1Primitive instanceof ASN1Integer) {
ASN1Integer asn1Integer = (ASN1Integer) asn1Primitive;
System.out.println("Integer:" + asn1Integer.getValue());
}else if (asn1Primitive instanceof ASN1Boolean) {
ASN1Boolean asn1Boolean = (ASN1Boolean) asn1Primitive;
System.out.println("Boolean:"+asn1Boolean.isTrue());
}else if (asn1Primitive instanceof ASN1Sequence) {
ASN1Sequence asn1Sequence = (ASN1Sequence) asn1Primitive;
ASN1SequenceParser asn1SequenceParser = asn1Sequence.parser();
ASN1Encodable asn1Encodable = null;
while ((asn1Encodable = asn1SequenceParser.readObject()) != null) {
asn1Primitive = asn1Encodable.toASN1Primitive();
if (asn1Primitive instanceof ASN1String) {
ASN1String string = (ASN1String) asn1Primitive;
System.out.println("PrintableString:"+string.getString());
}else if (asn1Primitive instanceof ASN1UTCTime) {
ASN1UTCTime asn1utcTime = (ASN1UTCTime) asn1Primitive;
System.out.println("UTCTime:"+asn1utcTime.getTime());
}else if (asn1Primitive instanceof ASN1Null) {
System.out.println("NULL");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
输出:
Integer:10
Boolean:false
PrintableString:PP
UTCTime:150516023912GMT+00:00
NULL
说明:无