ASN1标准对OID的编码

asn1对OID的编码有一些规定,形如a.b.c.d.e的OID被编码的时候,完全可以按照der的编码规则将整个oid的类型设定为object,然后将每一个点分数字的类型设定为integer,最终编码为[obj|length[[int|lena[a]]][int|lenb[b]][int|lenc[c]]...],可是asn1标准并没有如此编码,而是使用了"more bit"这种方式,这样就少了很多的层次,不必为每一个点分数字进行asn1编码,具体说来就是将一个点分数字拆分为7bit一组的序列,然后除了最后一个的最高位填入0之外其余的最高位都填1,然后将它们合并在一起,最终将每一个经过这样处理的点分数字连接在一起,可以减少编码的长度,起码每个点分数字的type和length都省略了,唯一的不足是8bit的点分数字要编码为16bits。
以上仅仅是对oid编码的规定之一,还有一个很有意思的就是由于OID的前两极的标识数字都不是很大,因此更好的办法就是将OID的前两级编码到一个7bit的数中(由于标识more bit需要最高位,所以只剩下7位可以存数据),第一级OID只有3个,分别是itu-t(0),iso(1),joint-iso-itu-t(2),并且第二级OID标识最多也就23,挂于joint-iso-itu-t之下,现在需要一个算法,将第一级和第二级的OID标识编码到一个7bit的数里面,这就需要用一种平坦的方式来索引前两级的数据,构造一组虚拟的标识,为了对待三个一级OID标识更公平,最好是将127个“位置”平均分到三个一级标识,于是就将127除以3,结果是40,于是头40个虚拟标识分给itu-t,中间40个分给iso,后面的47个分给joint-iso-itu-t,这样,前两级是a.b的OID的a和b就被编码成了40*a+b,如此也就节省了一个编码byte。本质上这种编码的思想是在分级的标识上构造一组平坦的虚拟标识。
实际上为何不采用x86的页表的形式(或者说是trie树的形式)来进行编码呢,将127,也就是7个bit分成3个段,0-x为第一级索引,x+1-6位第二级索引...事实上这个想法是很不错的,但是看一下真实的情况,由于第二级最大的OID标识数是23,二进制为10111,又因为只能使用7个bit,则唯一的方案就是0-1bit为第一级索引,2-6bit为第二级索引,5个bit最大只能表示32,因此joint-iso-itu-t下还剩下9个可用的位置,虽然说root下面还有一个位置,而该位置下能挂32个二级位置,但是OID是国际机构的人定义的,这就存在很大的不稳定性,虽说7bit数能包容的总的虚拟位置是一定的,但是怎样对应这些平坦虚拟位置的真实分级位置却存在很大的人为因素,在root下增加一个节点的可能性不大,那意味的一个新的顶级机构的产生,但是在已经存在的机构下产生一个分支却是正常的,故joint-iso-itu-t下增加新节点的可能性要比root下增加新节点的可能性大得多,asn1的编码方案为joint-iso-itu-t留下了11个空闲位置,而类似trie树的编码方案只留下了确定的9个位置,孰优孰劣?看看同样都是二级标识,joint-iso-itu-t下面有23个节点,而itu-t下面却只有3个,然后就知道这样人为因素很大的系统是多么不适合在计算机中普遍使用的trie树了,trie树在不平衡的时候可以平衡化调整,可是OID树能调整吗?...
下面的perl代码展示了OID的编码过程:
sub der_it
{
local($v)=@_;
local(@a,$i,$ret,@r);
@a=split(//s+/,$v);
$ret.=pack("C*",$a[0]*40+$a[1]);
shift @a;
shift @a;
foreach (@a)
{
@r=();
$t=0;
while ($_ >= 128)
{
$x=$_%128;
$_/=128;
push(@r,((($t++)?0x80:0)|$x));
}
push(@r,((($t++)?0x80:0)|$_));
$ret.=pack("C*",reverse(@r));
}
return($ret);
}

你可能感兴趣的:(编码)